Skip to main content

miniextendr_macros/
factor_derive.rs

1//! # `#[derive(RFactor)]` - Enum ↔ R Factor Support
2//!
3//! This module implements the `#[derive(RFactor)]` macro which generates
4//! the `RFactor` trait implementation for C-style enums, enabling automatic
5//! conversion between Rust enums and R factors.
6//!
7//! ## Usage
8//!
9//! ```ignore
10//! #[derive(Copy, Clone, RFactor)]
11//! enum Color {
12//!     Red,
13//!     Green,
14//!     Blue,
15//! }
16//!
17//! // Generates impl RFactor for Color, IntoR for Color, TryFromSexp for Color,
18//! // and Vec/Option variants.
19//! ```
20//!
21//! ## Attributes
22//!
23//! - `#[r_factor(rename = "name")]` - Rename a variant's level string
24//! - `#[r_factor(rename_all = "snake_case")]` - Rename all variants (snake_case, kebab-case, lower, upper)
25//!
26//! ```ignore
27//! #[derive(Copy, Clone, RFactor)]
28//! #[r_factor(rename_all = "snake_case")]
29//! enum Status {
30//!     InProgress,  // level: "in_progress"
31//!     #[r_factor(rename = "done")]
32//!     Completed,   // level: "done"
33//! }
34//! ```
35//!
36//! ## Interaction Factors
37//!
38//! For enums wrapping another RFactor type (like R's `interaction()`):
39//!
40//! ```ignore
41//! #[derive(Copy, Clone, RFactor)]
42//! enum Supplement { OJ, VC }
43//!
44//! #[derive(Copy, Clone, RFactor)]
45//! #[r_factor(interaction = ["OJ", "VC"])]  // inner type's levels
46//! enum SpeciesSupplement {
47//!     Setosa(Supplement),
48//!     Versicolor(Supplement),
49//!     Virginica(Supplement),
50//! }
51//! // Levels: ["Setosa.OJ", "Setosa.VC", "Versicolor.OJ", ...]
52//! ```
53
54use proc_macro2::TokenStream;
55use quote::quote;
56use syn::{Data, DeriveInput, Fields, Type};
57
58use crate::naming::apply_rename_all;
59
60/// Parsed `#[r_factor(...)]` attributes from an enum or variant.
61#[derive(#[automatically_derived]
impl ::core::default::Default for RFactorAttrs {
    #[inline]
    fn default() -> Self {
        RFactorAttrs {
            rename: ::core::default::Default::default(),
            rename_all: ::core::default::Default::default(),
            interaction: ::core::default::Default::default(),
            sep: ::core::default::Default::default(),
        }
    }
}Default)]
62struct RFactorAttrs {
63    /// Per-variant rename: `#[r_factor(rename = "custom_name")]`.
64    rename: Option<String>,
65    /// Enum-level rename-all: `#[r_factor(rename_all = "snake_case")]`.
66    /// Applied to all variants that don't have an explicit `rename`.
67    rename_all: Option<String>,
68    /// Inner type's level names for interaction factors:
69    /// `#[r_factor(interaction = ["A", "B"])]`.
70    /// When present, triggers interaction factor codegen instead of simple factor.
71    interaction: Option<Vec<String>>,
72    /// Separator between outer and inner level names in interaction factors.
73    /// Defaults to `"."`. Specified via `#[r_factor(sep = "_")]`.
74    sep: Option<String>,
75}
76
77/// Parse `#[r_factor(...)]` attributes from a list of `syn::Attribute`.
78///
79/// Extracts `rename`, `rename_all`, `interaction`, and `sep` keys.
80/// Returns `Err` for unknown attribute keys.
81fn parse_r_factor_attrs(attrs: &[syn::Attribute]) -> syn::Result<RFactorAttrs> {
82    let mut result = RFactorAttrs::default();
83
84    for attr in attrs {
85        if attr.path().is_ident("r_factor") {
86            attr.parse_nested_meta(|meta| {
87                if meta.path.is_ident("rename") {
88                    let value: syn::LitStr = meta.value()?.parse()?;
89                    result.rename = Some(value.value());
90                } else if meta.path.is_ident("rename_all") {
91                    let value: syn::LitStr = meta.value()?.parse()?;
92                    result.rename_all = Some(value.value());
93                } else if meta.path.is_ident("interaction") {
94                    // Parse as array: interaction = ["A", "B", "C"]
95                    let _eq: ::syn::token::Eqsyn::Token![=] = meta.input.parse()?;
96                    let content;
97                    match ::syn::__private::parse_brackets(&meta.input) {
    ::syn::__private::Ok(brackets) => {
        content = brackets.content;
        _ = content;
        brackets.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};syn::bracketed!(content in meta.input);
98                    let levels: syn::punctuated::Punctuated<syn::LitStr, ::syn::token::Commasyn::Token![,]> =
99                        content.parse_terminated(|input| input.parse(), ::syn::token::Commasyn::Token![,])?;
100                    result.interaction = Some(levels.iter().map(|s| s.value()).collect());
101                } else if meta.path.is_ident("sep") {
102                    let value: syn::LitStr = meta.value()?.parse()?;
103                    result.sep = Some(value.value());
104                } else {
105                    return Err(meta.error("unknown r_factor attribute"));
106                }
107                Ok(())
108            })?;
109        }
110    }
111
112    Ok(result)
113}
114
115/// Main entry point for `#[derive(RFactor)]`.
116///
117/// Dispatches to either [`derive_simple_factor`] (C-style unit variants) or
118/// [`derive_interaction_factor`] (tuple variants wrapping an inner RFactor type),
119/// based on whether `#[r_factor(interaction = [...])]` is present.
120///
121/// Generates:
122/// - `impl MatchArg` (string choices for `match.arg`)
123/// - `impl RFactor` (1-based level index conversion)
124/// - `impl IntoR` (Rust enum -> R factor SEXP)
125/// - `impl TryFromSexp` (R factor SEXP -> Rust enum)
126///
127/// Returns `Err` for structs, unions, or invalid attribute combinations.
128pub fn derive_r_factor(input: DeriveInput) -> syn::Result<TokenStream> {
129    let name = &input.ident;
130    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
131
132    // Parse enum-level attributes
133    let attrs = parse_r_factor_attrs(&input.attrs)?;
134
135    // Get enum variants
136    let variants = match &input.data {
137        Data::Enum(data) => &data.variants,
138        Data::Struct(_) => {
139            return Err(syn::Error::new_spanned(
140                &input,
141                "#[derive(RFactor)] can only be applied to enums",
142            ));
143        }
144        Data::Union(_) => {
145            return Err(syn::Error::new_spanned(
146                &input,
147                "#[derive(RFactor)] can only be applied to enums",
148            ));
149        }
150    };
151
152    // Branch based on whether this is an interaction factor
153    if let Some(inner_levels) = &attrs.interaction {
154        derive_interaction_factor(
155            name,
156            &impl_generics,
157            &ty_generics,
158            where_clause,
159            variants,
160            inner_levels,
161            attrs.sep.as_deref().unwrap_or("."),
162            attrs.rename_all.as_deref(),
163        )
164    } else {
165        derive_simple_factor(
166            name,
167            &impl_generics,
168            &ty_generics,
169            where_clause,
170            variants,
171            attrs.rename_all.as_deref(),
172        )
173    }
174}
175
176/// Generate `RFactor`, `MatchArg`, `IntoR`, and `TryFromSexp` impls for simple
177/// (unit variant) enums.
178///
179/// Each variant maps to a 1-based level index and a level name string.
180/// Level names are determined by variant ident, optionally transformed by
181/// `rename_all` or overridden by per-variant `#[r_factor(rename = "...")]`.
182///
183/// Uses a `OnceLock`-cached levels SEXP for efficient repeated conversion.
184///
185/// Returns `Err` if any variant has fields (only C-style enums are supported
186/// in the simple path).
187fn derive_simple_factor(
188    name: &syn::Ident,
189    impl_generics: &syn::ImplGenerics,
190    ty_generics: &syn::TypeGenerics,
191    where_clause: Option<&syn::WhereClause>,
192    variants: &syn::punctuated::Punctuated<syn::Variant, ::syn::token::Commasyn::Token![,]>,
193    rename_all: Option<&str>,
194) -> syn::Result<TokenStream> {
195    let mut level_names = Vec::new();
196    let mut variant_idents = Vec::new();
197
198    for variant in variants {
199        // Check for fields (only allow unit variants)
200        if !#[allow(non_exhaustive_omitted_patterns)] match variant.fields {
    Fields::Unit => true,
    _ => false,
}matches!(variant.fields, Fields::Unit) {
201            return Err(syn::Error::new_spanned(
202                variant,
203                "#[derive(RFactor)] only supports fieldless (C-style) enum variants \
204                 (use #[r_factor(interaction = [...])] for tuple variants)",
205            ));
206        }
207
208        // Parse variant-level attributes
209        let var_attrs = parse_r_factor_attrs(&variant.attrs)?;
210
211        // Determine level name
212        let level_name = if let Some(r) = var_attrs.rename {
213            r
214        } else {
215            apply_rename_all(&crate::naming::ident_name(&variant.ident), rename_all)
216        };
217
218        level_names.push(level_name);
219        variant_idents.push(&variant.ident);
220    }
221
222    // Generate indices (1-based for R)
223    let indices: Vec<i32> = (1..=variant_idents.len() as i32).collect();
224    let level_name_strs: Vec<&str> = level_names.iter().map(|s| s.as_str()).collect();
225
226    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "match_arg");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "MatchArg");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "CHOICES");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_lifetime(&mut _s, "\'static");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_lifetime(&mut _s, "\'static");
                    ::quote::__private::push_ident(&mut _s, "str");
                    _s
                });
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let mut _first = true;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut level_name_strs, i) =
                            level_name_strs.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let level_name_strs =
                                match level_name_strs.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::ToTokens::to_tokens(&level_name_strs, &mut _s);
                        }
                    }
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "from_choice");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "choice");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "str");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Option");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_ident(&mut _s, "choice");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut level_name_strs, i) =
                                    level_name_strs.quote_into_iter();
                                let has_iter = has_iter | i;
                                #[allow(unused_mut)]
                                let (mut variant_idents, i) =
                                    variant_idents.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let level_name_strs =
                                        match level_name_strs.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    let variant_idents =
                                        match variant_idents.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&level_name_strs, &mut _s);
                                    ::quote::__private::push_fat_arrow(&mut _s);
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "core");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "option");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Option");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Some");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "Self");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::ToTokens::to_tokens(&variant_idents, &mut _s);
                                            _s
                                        });
                                    ::quote::__private::push_comma(&mut _s);
                                }
                            }
                            ::quote::__private::push_underscore(&mut _s);
                            ::quote::__private::push_fat_arrow(&mut _s);
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "core");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "option");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "Option");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "None");
                            ::quote::__private::push_comma(&mut _s);
                            _s
                        });
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "to_choice");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_lifetime(&mut _s, "\'static");
            ::quote::__private::push_ident(&mut _s, "str");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut variant_idents, i) =
                                    variant_idents.quote_into_iter();
                                let has_iter = has_iter | i;
                                #[allow(unused_mut)]
                                let (mut level_name_strs, i) =
                                    level_name_strs.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let variant_idents =
                                        match variant_idents.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    let level_name_strs =
                                        match level_name_strs.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::__private::push_ident(&mut _s, "Self");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::ToTokens::to_tokens(&variant_idents, &mut _s);
                                    ::quote::__private::push_fat_arrow(&mut _s);
                                    ::quote::ToTokens::to_tokens(&level_name_strs, &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                }
                            }
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "RFactor");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "to_level_index");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "i32");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut variant_idents, i) =
                                    variant_idents.quote_into_iter();
                                let has_iter = has_iter | i;
                                #[allow(unused_mut)]
                                let (mut indices, i) = indices.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let variant_idents =
                                        match variant_idents.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    let indices =
                                        match indices.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::__private::push_ident(&mut _s, "Self");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::ToTokens::to_tokens(&variant_idents, &mut _s);
                                    ::quote::__private::push_fat_arrow(&mut _s);
                                    ::quote::ToTokens::to_tokens(&indices, &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                }
                            }
                            _s
                        });
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "from_level_index");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "idx");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "i32");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Option");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_ident(&mut _s, "idx");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut indices, i) = indices.quote_into_iter();
                                let has_iter = has_iter | i;
                                #[allow(unused_mut)]
                                let (mut variant_idents, i) =
                                    variant_idents.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let indices =
                                        match indices.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    let variant_idents =
                                        match variant_idents.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&indices, &mut _s);
                                    ::quote::__private::push_fat_arrow(&mut _s);
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "core");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "option");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Option");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Some");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "Self");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::ToTokens::to_tokens(&variant_idents, &mut _s);
                                            _s
                                        });
                                    ::quote::__private::push_comma(&mut _s);
                                }
                            }
                            ::quote::__private::push_underscore(&mut _s);
                            ::quote::__private::push_fat_arrow(&mut _s);
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "core");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "option");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "Option");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "None");
                            ::quote::__private::push_comma(&mut _s);
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "IntoR");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "type");
            ::quote::__private::push_ident(&mut _s, "Error");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "convert");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Infallible");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "try_into_sexp");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXP");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "convert");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Infallible");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "result");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Result");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Ok");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "self");
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "into_sexp");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            _s
                        });
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s,
                "try_into_sexp_unchecked");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXP");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "convert");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Infallible");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "try_into_sexp");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "into_sexp");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXP");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "static");
                    ::quote::__private::push_ident(&mut _s, "LEVELS_CACHE");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "std");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "sync");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "OnceLock");
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "SEXP");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "std");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "sync");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "OnceLock");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "new");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "levels");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_star(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "LEVELS_CACHE");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "get_or_init");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_or_or(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Brace,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s,
                                        "build_levels_sexp_cached");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_lt(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "Self");
                                            ::quote::__private::push_ident(&mut _s, "as");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "match_arg");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "MatchArg");
                                            ::quote::__private::push_gt(&mut _s);
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "CHOICES");
                                            _s
                                        });
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "build_factor");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_and(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Bracket,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_lt(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Self");
                                    ::quote::__private::push_ident(&mut _s, "as");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "RFactor");
                                    ::quote::__private::push_gt(&mut _s);
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "to_level_index");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "self");
                                            _s
                                        });
                                    _s
                                });
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "levels");
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "TryFromSexp");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "type");
            ::quote::__private::push_ident(&mut _s, "Error");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SexpError");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "CHARACTER_ONLY");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_ident(&mut _s, "bool");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_ident(&mut _s, "true");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "try_from_sexp");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "sexp");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "SEXP");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SexpError");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "factor_from_sexp");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "sexp");
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
227        impl #impl_generics ::miniextendr_api::match_arg::MatchArg for #name #ty_generics #where_clause {
228            const CHOICES: &'static [&'static str] = &[#(#level_name_strs),*];
229
230            fn from_choice(choice: &str) -> ::core::option::Option<Self> {
231                match choice {
232                    #(#level_name_strs => ::core::option::Option::Some(Self::#variant_idents),)*
233                    _ => ::core::option::Option::None,
234                }
235            }
236
237            fn to_choice(self) -> &'static str {
238                match self {
239                    #(Self::#variant_idents => #level_name_strs,)*
240                }
241            }
242        }
243
244        impl #impl_generics ::miniextendr_api::RFactor for #name #ty_generics #where_clause {
245            fn to_level_index(self) -> i32 {
246                match self {
247                    #(Self::#variant_idents => #indices,)*
248                }
249            }
250
251            fn from_level_index(idx: i32) -> ::core::option::Option<Self> {
252                match idx {
253                    #(#indices => ::core::option::Option::Some(Self::#variant_idents),)*
254                    _ => ::core::option::Option::None,
255                }
256            }
257        }
258
259        impl #impl_generics ::miniextendr_api::IntoR for #name #ty_generics #where_clause {
260            type Error = ::core::convert::Infallible;
261
262            fn try_into_sexp(self) -> ::core::result::Result<::miniextendr_api::SEXP, ::core::convert::Infallible> {
263                ::core::result::Result::Ok(self.into_sexp())
264            }
265
266            unsafe fn try_into_sexp_unchecked(self) -> ::core::result::Result<::miniextendr_api::SEXP, ::core::convert::Infallible> {
267                self.try_into_sexp()
268            }
269
270            fn into_sexp(self) -> ::miniextendr_api::SEXP {
271                static LEVELS_CACHE: ::std::sync::OnceLock<::miniextendr_api::SEXP> =
272                    ::std::sync::OnceLock::new();
273                let levels = *LEVELS_CACHE.get_or_init(|| {
274                    ::miniextendr_api::build_levels_sexp_cached(
275                        <Self as ::miniextendr_api::match_arg::MatchArg>::CHOICES
276                    )
277                });
278                ::miniextendr_api::build_factor(&[<Self as ::miniextendr_api::RFactor>::to_level_index(self)], levels)
279            }
280        }
281
282        impl #impl_generics ::miniextendr_api::TryFromSexp for #name #ty_generics #where_clause {
283            type Error = ::miniextendr_api::SexpError;
284            const CHARACTER_ONLY: bool = true;
285
286            fn try_from_sexp(sexp: ::miniextendr_api::SEXP) -> ::core::result::Result<Self, ::miniextendr_api::SexpError> {
287                ::miniextendr_api::factor_from_sexp(sexp)
288            }
289        }
290    })
291}
292
293/// Generate `RFactor`, `MatchArg`, `IntoR`, and `TryFromSexp` impls for interaction
294/// (tuple variant) enums.
295///
296/// Interaction factors combine an outer enum (the variant) with an inner `RFactor`
297/// type, producing combined level names like `"Outer.Inner"`. The level order is
298/// outer-varies-slowest (matches R's `interaction(..., lex.order = TRUE)`).
299///
300/// Generates a compile-time assertion that the specified `inner_levels` match the
301/// inner type's `MatchArg::CHOICES`, catching mismatches early.
302///
303/// All variants must be single-field tuples wrapping the same inner type.
304///
305/// # Arguments
306///
307/// * `inner_levels` - The expected level strings of the inner type
308///   (from `#[r_factor(interaction = [...])]`)
309/// * `sep` - Separator between outer and inner level names (default `"."`)
310/// * `rename_all` - Optional rename transformation for outer variant names
311#[allow(clippy::too_many_arguments)] // generics plumbing, single call site
312fn derive_interaction_factor(
313    name: &syn::Ident,
314    impl_generics: &syn::ImplGenerics,
315    ty_generics: &syn::TypeGenerics,
316    where_clause: Option<&syn::WhereClause>,
317    variants: &syn::punctuated::Punctuated<syn::Variant, ::syn::token::Commasyn::Token![,]>,
318    inner_levels: &[String],
319    sep: &str,
320    rename_all: Option<&str>,
321) -> syn::Result<TokenStream> {
322    let mut outer_names = Vec::new();
323    let mut variant_idents = Vec::new();
324    let mut inner_type: Option<Type> = None;
325
326    for variant in variants {
327        // Require single-field tuple variants
328        let field_ty = match &variant.fields {
329            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
330                fields.unnamed.first().unwrap().ty.clone()
331            }
332            _ => {
333                return Err(syn::Error::new_spanned(
334                    variant,
335                    "interaction factors require single-field tuple variants: Variant(InnerType)",
336                ));
337            }
338        };
339
340        // All variants must have the same inner type
341        if let Some(ref existing) = inner_type {
342            if field_ty != *existing {
343                return Err(syn::Error::new_spanned(
344                    &variant.fields,
345                    "all variants must have the same inner type",
346                ));
347            }
348        } else {
349            inner_type = Some(field_ty);
350        }
351
352        // Parse variant-level attributes for rename
353        let var_attrs = parse_r_factor_attrs(&variant.attrs)?;
354        let outer_name = if let Some(r) = var_attrs.rename {
355            r
356        } else {
357            apply_rename_all(&crate::naming::ident_name(&variant.ident), rename_all)
358        };
359
360        outer_names.push(outer_name);
361        variant_idents.push(&variant.ident);
362    }
363
364    let inner_type = inner_type.ok_or_else(|| {
365        syn::Error::new_spanned(name, "interaction factor must have at least one variant")
366    })?;
367
368    let n_outer = outer_names.len();
369    let n_inner = inner_levels.len();
370
371    // Generate combined levels at compile time using concat!
372    // Order: outer varies slowest (lex.order style)
373    // [Outer1.Inner1, Outer1.Inner2, ..., Outer2.Inner1, ...]
374    let mut combined_levels = Vec::new();
375    for outer_name in &outer_names {
376        for inner_name in inner_levels {
377            // Use concat! for compile-time string concatenation
378            let combined = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}", outer_name, sep,
                inner_name))
    })format!("{}{}{}", outer_name, sep, inner_name);
379            combined_levels.push(combined);
380        }
381    }
382    let combined_level_strs: Vec<&str> = combined_levels.iter().map(|s| s.as_str()).collect();
383
384    // Generate to_level_index match arms
385    // Index = outer_idx_0 * n_inner + inner_idx_0 + 1
386    let n_inner_lit = n_inner as i32;
387    let to_index_arms: Vec<_> = variant_idents
388        .iter()
389        .enumerate()
390        .map(|(outer_idx, var_ident)| {
391            let outer_idx_lit = outer_idx as i32;
392            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&var_ident, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "inner");
            _s
        });
    ::quote::__private::push_fat_arrow(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::__private::push_ident(&mut _s, "inner_idx_0");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&inner_type, &mut _s);
            ::quote::__private::push_ident(&mut _s, "as");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "RFactor");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "to_level_index");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "inner");
                    _s
                });
            ::quote::__private::push_sub(&mut _s);
            ::quote::__private::parse(&mut _s, "1");
            ::quote::__private::push_semi(&mut _s);
            ::quote::ToTokens::to_tokens(&outer_idx_lit, &mut _s);
            ::quote::__private::push_star(&mut _s);
            ::quote::ToTokens::to_tokens(&n_inner_lit, &mut _s);
            ::quote::__private::push_add(&mut _s);
            ::quote::__private::push_ident(&mut _s, "inner_idx_0");
            ::quote::__private::push_add(&mut _s);
            ::quote::__private::parse(&mut _s, "1");
            _s
        });
    _s
}quote! {
393                Self::#var_ident(inner) => {
394                    let inner_idx_0 = <#inner_type as ::miniextendr_api::RFactor>::to_level_index(inner) - 1;
395                    #outer_idx_lit * #n_inner_lit + inner_idx_0 + 1
396                }
397            }
398        })
399        .collect();
400
401    // Generate from_level_index match arms
402    // outer_idx_0 = (idx - 1) / n_inner
403    // inner_idx_1 = (idx - 1) % n_inner + 1
404    let from_index_arms: Vec<_> = (0..n_outer)
405        .map(|outer_idx| {
406            let var_ident = &variant_idents[outer_idx];
407            let start_idx = (outer_idx * n_inner + 1) as i32;
408            let end_idx = ((outer_idx + 1) * n_inner) as i32;
409            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&start_idx, &mut _s);
    ::quote::__private::push_dot_dot_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&end_idx, &mut _s);
    ::quote::__private::push_fat_arrow(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::__private::push_ident(&mut _s, "inner_idx_1");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "idx");
                    ::quote::__private::push_sub(&mut _s);
                    ::quote::__private::parse(&mut _s, "1");
                    _s
                });
            ::quote::__private::push_rem(&mut _s);
            ::quote::ToTokens::to_tokens(&n_inner_lit, &mut _s);
            ::quote::__private::push_add(&mut _s);
            ::quote::__private::parse(&mut _s, "1");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&inner_type, &mut _s);
            ::quote::__private::push_ident(&mut _s, "as");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "RFactor");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "from_level_index");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "inner_idx_1");
                    _s
                });
            ::quote::__private::push_dot(&mut _s);
            ::quote::__private::push_ident(&mut _s, "map");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "Self");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::ToTokens::to_tokens(&var_ident, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote! {
410                #start_idx..=#end_idx => {
411                    let inner_idx_1 = (idx - 1) % #n_inner_lit + 1;
412                    <#inner_type as ::miniextendr_api::RFactor>::from_level_index(inner_idx_1)
413                        .map(Self::#var_ident)
414                }
415            }
416        })
417        .collect();
418
419    // Generate inner level strings for the const assertion
420    let inner_level_strs: Vec<&str> = inner_levels.iter().map(|s| s.as_str()).collect();
421
422    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "const");
    ::quote::__private::push_underscore(&mut _s);
    ::quote::__private::push_colon(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "ACTUAL");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "str");
                    _s
                });
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&inner_type, &mut _s);
            ::quote::__private::push_ident(&mut _s, "as");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "match_arg");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "MatchArg");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "CHOICES");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "EXPECTED");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "str");
                    _s
                });
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let mut _first = true;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut inner_level_strs, i) =
                            inner_level_strs.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let inner_level_strs =
                                match inner_level_strs.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::ToTokens::to_tokens(&inner_level_strs, &mut _s);
                        }
                    }
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "assert");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "ACTUAL");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "len");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_eq_eq(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "EXPECTED");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "len");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s,
                        "\"interaction factor: inner type level count mismatch\"");
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::__private::push_ident(&mut _s, "mut");
            ::quote::__private::push_ident(&mut _s, "i");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s, "0");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "while");
            ::quote::__private::push_ident(&mut _s, "i");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ACTUAL");
            ::quote::__private::push_dot(&mut _s);
            ::quote::__private::push_ident(&mut _s, "len");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "actual_bytes");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "ACTUAL");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "i");
                            _s
                        });
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "as_bytes");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "expected_bytes");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "EXPECTED");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "i");
                            _s
                        });
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "as_bytes");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "assert");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "actual_bytes");
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "len");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            ::quote::__private::push_eq_eq(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "expected_bytes");
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "len");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::__private::parse(&mut _s,
                                "\"interaction factor: inner type level string length mismatch\"");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "mut");
                    ::quote::__private::push_ident(&mut _s, "j");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::parse(&mut _s, "0");
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "while");
                    ::quote::__private::push_ident(&mut _s, "j");
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "actual_bytes");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "len");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "core");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "assert");
                            ::quote::__private::push_bang(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "actual_bytes");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Bracket,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "j");
                                            _s
                                        });
                                    ::quote::__private::push_eq_eq(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "expected_bytes");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Bracket,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "j");
                                            _s
                                        });
                                    ::quote::__private::push_comma(&mut _s);
                                    ::quote::__private::parse(&mut _s,
                                        "\"interaction factor: inner type level string content mismatch\"");
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "j");
                            ::quote::__private::push_add_eq(&mut _s);
                            ::quote::__private::parse(&mut _s, "1");
                            ::quote::__private::push_semi(&mut _s);
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "i");
                    ::quote::__private::push_add_eq(&mut _s);
                    ::quote::__private::parse(&mut _s, "1");
                    ::quote::__private::push_semi(&mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "match_arg");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "MatchArg");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "CHOICES");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_lifetime(&mut _s, "\'static");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_lifetime(&mut _s, "\'static");
                    ::quote::__private::push_ident(&mut _s, "str");
                    _s
                });
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let mut _first = true;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut combined_level_strs, i) =
                            combined_level_strs.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let combined_level_strs =
                                match combined_level_strs.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::ToTokens::to_tokens(&combined_level_strs, &mut _s);
                        }
                    }
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "from_choice");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "choice");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "str");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Option");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "idx_1");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Self");
                    ::quote::__private::push_ident(&mut _s, "as");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "match_arg");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "MatchArg");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "CHOICES");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "iter");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "position");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_or(&mut _s);
                            ::quote::__private::push_and(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "l");
                            ::quote::__private::push_or(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "l");
                            ::quote::__private::push_eq_eq(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "choice");
                            _s
                        });
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "map");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_or(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "i");
                            ::quote::__private::push_or(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "i");
                            ::quote::__private::push_ident(&mut _s, "as");
                            ::quote::__private::push_ident(&mut _s, "i32");
                            ::quote::__private::push_add(&mut _s);
                            ::quote::__private::parse(&mut _s, "1");
                            _s
                        });
                    ::quote::__private::push_question(&mut _s);
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Self");
                    ::quote::__private::push_ident(&mut _s, "as");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "RFactor");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "from_level_index");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "idx_1");
                            _s
                        });
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "to_choice");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_lifetime(&mut _s, "\'static");
            ::quote::__private::push_ident(&mut _s, "str");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Self");
                    ::quote::__private::push_ident(&mut _s, "as");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "match_arg");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "MatchArg");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "CHOICES");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_lt(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Self");
                                    ::quote::__private::push_ident(&mut _s, "as");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "RFactor");
                                    ::quote::__private::push_gt(&mut _s);
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "to_level_index");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "self");
                                            _s
                                        });
                                    ::quote::__private::push_sub(&mut _s);
                                    ::quote::__private::parse(&mut _s, "1");
                                    _s
                                });
                            ::quote::__private::push_ident(&mut _s, "as");
                            ::quote::__private::push_ident(&mut _s, "usize");
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "RFactor");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "to_level_index");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "i32");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut to_index_arms, i) =
                                    to_index_arms.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let to_index_arms =
                                        match to_index_arms.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&to_index_arms, &mut _s);
                                }
                            }
                            _s
                        });
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "from_level_index");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "idx");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "i32");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Option");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_ident(&mut _s, "idx");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut from_index_arms, i) =
                                    from_index_arms.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let from_index_arms =
                                        match from_index_arms.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&from_index_arms, &mut _s);
                                }
                            }
                            ::quote::__private::push_underscore(&mut _s);
                            ::quote::__private::push_fat_arrow(&mut _s);
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "core");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "option");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "Option");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "None");
                            ::quote::__private::push_comma(&mut _s);
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "IntoR");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "type");
            ::quote::__private::push_ident(&mut _s, "Error");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "convert");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Infallible");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "try_into_sexp");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXP");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "convert");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Infallible");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "result");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Result");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Ok");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "self");
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "into_sexp");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            _s
                        });
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s,
                "try_into_sexp_unchecked");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXP");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "convert");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Infallible");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "try_into_sexp");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "into_sexp");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXP");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "static");
                    ::quote::__private::push_ident(&mut _s, "LEVELS_CACHE");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "std");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "sync");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "OnceLock");
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "SEXP");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "std");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "sync");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "OnceLock");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "new");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "levels");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_star(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "LEVELS_CACHE");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "get_or_init");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_or_or(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Brace,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s,
                                        "build_levels_sexp_cached");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_lt(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "Self");
                                            ::quote::__private::push_ident(&mut _s, "as");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "match_arg");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "MatchArg");
                                            ::quote::__private::push_gt(&mut _s);
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "CHOICES");
                                            _s
                                        });
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "build_factor");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_and(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Bracket,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_lt(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Self");
                                    ::quote::__private::push_ident(&mut _s, "as");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "RFactor");
                                    ::quote::__private::push_gt(&mut _s);
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "to_level_index");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "self");
                                            _s
                                        });
                                    _s
                                });
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "levels");
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "TryFromSexp");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "type");
            ::quote::__private::push_ident(&mut _s, "Error");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SexpError");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "CHARACTER_ONLY");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_ident(&mut _s, "bool");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_ident(&mut _s, "true");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "try_from_sexp");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "sexp");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "SEXP");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SexpError");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "factor_from_sexp");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "sexp");
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
423        // Compile-time assertion: verify specified inner levels match the actual inner type's CHOICES.
424        // This catches mismatches between the `interaction = [...]` attribute and the inner type.
425        const _: () = {
426            const ACTUAL: &[&str] = <#inner_type as ::miniextendr_api::match_arg::MatchArg>::CHOICES;
427            const EXPECTED: &[&str] = &[#(#inner_level_strs),*];
428
429            // Check level count
430            ::core::assert!(
431                ACTUAL.len() == EXPECTED.len(),
432                "interaction factor: inner type level count mismatch"
433            );
434
435            // Check each level matches (const string comparison)
436            let mut i = 0;
437            while i < ACTUAL.len() {
438                let actual_bytes = ACTUAL[i].as_bytes();
439                let expected_bytes = EXPECTED[i].as_bytes();
440                ::core::assert!(
441                    actual_bytes.len() == expected_bytes.len(),
442                    "interaction factor: inner type level string length mismatch"
443                );
444                let mut j = 0;
445                while j < actual_bytes.len() {
446                    ::core::assert!(
447                        actual_bytes[j] == expected_bytes[j],
448                        "interaction factor: inner type level string content mismatch"
449                    );
450                    j += 1;
451                }
452                i += 1;
453            }
454        };
455
456        impl #impl_generics ::miniextendr_api::match_arg::MatchArg for #name #ty_generics #where_clause {
457            const CHOICES: &'static [&'static str] = &[#(#combined_level_strs),*];
458
459            fn from_choice(choice: &str) -> ::core::option::Option<Self> {
460                let idx_1 = <Self as ::miniextendr_api::match_arg::MatchArg>::CHOICES.iter().position(|&l| l == choice).map(|i| i as i32 + 1)?;
461                <Self as ::miniextendr_api::RFactor>::from_level_index(idx_1)
462            }
463
464            fn to_choice(self) -> &'static str {
465                <Self as ::miniextendr_api::match_arg::MatchArg>::CHOICES[(<Self as ::miniextendr_api::RFactor>::to_level_index(self) - 1) as usize]
466            }
467        }
468
469        impl #impl_generics ::miniextendr_api::RFactor for #name #ty_generics #where_clause {
470            fn to_level_index(self) -> i32 {
471                match self {
472                    #(#to_index_arms)*
473                }
474            }
475
476            fn from_level_index(idx: i32) -> ::core::option::Option<Self> {
477                match idx {
478                    #(#from_index_arms)*
479                    _ => ::core::option::Option::None,
480                }
481            }
482        }
483
484        impl #impl_generics ::miniextendr_api::IntoR for #name #ty_generics #where_clause {
485            type Error = ::core::convert::Infallible;
486
487            fn try_into_sexp(self) -> ::core::result::Result<::miniextendr_api::SEXP, ::core::convert::Infallible> {
488                ::core::result::Result::Ok(self.into_sexp())
489            }
490
491            unsafe fn try_into_sexp_unchecked(self) -> ::core::result::Result<::miniextendr_api::SEXP, ::core::convert::Infallible> {
492                self.try_into_sexp()
493            }
494
495            fn into_sexp(self) -> ::miniextendr_api::SEXP {
496                static LEVELS_CACHE: ::std::sync::OnceLock<::miniextendr_api::SEXP> =
497                    ::std::sync::OnceLock::new();
498                let levels = *LEVELS_CACHE.get_or_init(|| {
499                    ::miniextendr_api::build_levels_sexp_cached(
500                        <Self as ::miniextendr_api::match_arg::MatchArg>::CHOICES
501                    )
502                });
503                ::miniextendr_api::build_factor(&[<Self as ::miniextendr_api::RFactor>::to_level_index(self)], levels)
504            }
505        }
506
507        impl #impl_generics ::miniextendr_api::TryFromSexp for #name #ty_generics #where_clause {
508            type Error = ::miniextendr_api::SexpError;
509            const CHARACTER_ONLY: bool = true;
510
511            fn try_from_sexp(sexp: ::miniextendr_api::SEXP) -> ::core::result::Result<Self, ::miniextendr_api::SexpError> {
512                ::miniextendr_api::factor_from_sexp(sexp)
513            }
514        }
515    })
516}
517
518#[cfg(test)]
519mod tests {
520    use super::*;
521
522    #[test]
523    fn test_interaction_levels_generation() {
524        let input: DeriveInput = syn::parse_quote! {
525            #[r_factor(interaction = ["Small", "Large"])]
526            enum ColorSize {
527                Red(Size),
528                Green(Size),
529                Blue(Size),
530            }
531        };
532
533        let result = derive_r_factor(input).unwrap();
534        let code = result.to_string();
535
536        // Check that combined levels are generated
537        assert!(code.contains("Red.Small"));
538        assert!(code.contains("Red.Large"));
539        assert!(code.contains("Green.Small"));
540        assert!(code.contains("Green.Large"));
541        assert!(code.contains("Blue.Small"));
542        assert!(code.contains("Blue.Large"));
543
544        // Check that const assertion is generated for level validation
545        assert!(code.contains("const _ : () ="));
546        assert!(code.contains("ACTUAL"));
547        assert!(code.contains("EXPECTED"));
548        assert!(code.contains("inner type level count mismatch"));
549    }
550
551    #[test]
552    fn test_interaction_custom_separator() {
553        let input: DeriveInput = syn::parse_quote! {
554            #[r_factor(interaction = ["X", "Y"], sep = "_")]
555            enum AB {
556                A(Inner),
557                B(Inner),
558            }
559        };
560
561        let result = derive_r_factor(input).unwrap();
562        let code = result.to_string();
563
564        // Check custom separator
565        assert!(code.contains("A_X"));
566        assert!(code.contains("A_Y"));
567        assert!(code.contains("B_X"));
568        assert!(code.contains("B_Y"));
569    }
570
571    #[test]
572    fn test_interaction_with_rename() {
573        let input: DeriveInput = syn::parse_quote! {
574            #[r_factor(interaction = ["S", "L"], rename_all = "lower")]
575            enum ColorSize {
576                Red(Size),
577                Green(Size),
578            }
579        };
580
581        let result = derive_r_factor(input).unwrap();
582        let code = result.to_string();
583
584        // Check renamed outer levels
585        assert!(code.contains("red.S"));
586        assert!(code.contains("red.L"));
587        assert!(code.contains("green.S"));
588        assert!(code.contains("green.L"));
589    }
590}