Skip to main content

miniextendr_macros/
condition_derive.rs

1//! Code generation for `#[derive(RCondition)]`.
2//!
3//! Generates `impl miniextendr_api::condition::RConditionData` from the
4//! type's structure, and `From<T> for miniextendr_api::condition::RCondition`,
5//! so a package keeps one Rust type per condition family and gets the R side
6//! without hand-written glue:
7//!
8//! - `class()`: `c(<member>, <family>)` for enum variants, `c(<family>)` for
9//!   structs. `#[condition(class("…", "…"))]` on a struct is its whole class
10//!   vector, and on a variant it replaces the member. The family is
11//!   `#[condition(class = "…")]` on the type, else the
12//!   type name in snake_case; the member is the same attribute on the
13//!   variant, else `<family>_<variant in snake_case>`.
14//! - `message()`: `#[condition(message = "…")]` on the struct or on a variant
15//!   is a `format!` string with the fields in scope (tuple fields as `_0`,
16//!   `_1`, …). Without it the message is the type's `Display` rendering.
17//! - `data()`: every field as a named entry built with
18//!   `RValue::from(field.clone())`. `#[condition(rename = "…")]` changes the
19//!   name, `#[condition(skip)]` drops the field, `#[condition(debug)]`
20//!   attaches the `Debug` rendering (`RValue::debug`) instead of a
21//!   conversion. Tuple fields need `rename` or `skip`; the condition's own
22//!   slots (`message`, `call`, `kind`) are rejected.
23//!   `#[condition(skip_none)]` on an `Option` field leaves its entry out when
24//!   it is `None`. `#[condition(data(name = value, …))]` on the type or a
25//!   variant adds entries after the fields', each `RValue::from(value)`. With
26//!   no entries, `data()` is `None`.
27//! - `call()`: `#[condition(call = none)]` on the type (every variant) or on
28//!   one variant raises that condition without a call (`conditionCall()` is
29//!   `NULL`, R's `call. = FALSE`). Without it the method keeps the trait's
30//!   default, the call the transport captured.
31//! - `argument_message()`: `#[condition(argument_message = "…")]` on the
32//!   struct or on a variant, a `format!` string like `message`. It is the
33//!   whole message when the error refuses an argument in a conversion
34//!   (#1833); a variant without it keeps the trait's default, `None`.
35//!
36//! - `From<T> for RCondition`: the kind is `#[condition(kind = …)]` on the
37//!   variant, else on the type, else `error`. Only `RCondition::from(x)`
38//!   (`.defer()` / `.raise()`) reads it: a returned `Err(x)` is always an R
39//!   error, and `defer_warning(x)` / `defer_message(x)` choose their own kind.
40//!
41//! The payload feeds `defer_warning` & co. (a condition accompanying a value)
42//! and `Result<T, E>` returns (a classed error): both go through the trait.
43//! Generic types are rejected, like the other derives in this crate.
44
45use proc_macro2::{Span, TokenStream};
46use quote::{format_ident, quote};
47use syn::punctuated::Punctuated;
48use syn::spanned::Spanned;
49use syn::{Data, DeriveInput, Fields};
50
51use crate::naming::{ident_name, to_snake_case};
52
53/// The condition object's own slots. Mirrors
54/// `miniextendr_api::condition::RESERVED_CONDITION_FIELDS`; keep in sync.
55const RESERVED_FIELDS: [&str; 3] = ["message", "call", "kind"];
56
57// region: Attribute parsing
58
59/// `class = "…"` or `class("…", "…")`.
60enum ClassSpec {
61    /// A struct's only class, an enum's family, or a variant's member.
62    One(syn::LitStr),
63    /// A struct's whole class vector, or a variant's classes before the
64    /// family; most specific first.
65    List(Vec<syn::LitStr>, Span),
66}
67
68/// One `data(name = value)` entry.
69struct DataEntry {
70    name: String,
71    value: syn::Expr,
72    span: Span,
73}
74
75/// `#[condition(...)]` options on the type or on one variant.
76#[derive(#[automatically_derived]
impl ::core::default::Default for ContainerAttrs {
    #[inline]
    fn default() -> Self {
        ContainerAttrs {
            class: ::core::default::Default::default(),
            message: ::core::default::Default::default(),
            argument_message: ::core::default::Default::default(),
            call_none: ::core::default::Default::default(),
            kind: ::core::default::Default::default(),
            data: ::core::default::Default::default(),
        }
    }
}Default)]
77struct ContainerAttrs {
78    class: Option<ClassSpec>,
79    message: Option<syn::LitStr>,
80    /// `argument_message = "…"`: the whole message of an argument error.
81    argument_message: Option<syn::LitStr>,
82    /// `call = none`: the condition carries no call.
83    call_none: bool,
84    /// `kind = error | warning | message | condition`: the kind
85    /// `From<T> for RCondition` gives the value.
86    kind: Option<syn::Ident>,
87    /// `data(name = value, …)`: entries after the fields'.
88    data: Vec<DataEntry>,
89}
90
91fn parse_container_attrs(attrs: &[syn::Attribute], on: &str) -> syn::Result<ContainerAttrs> {
92    let mut out = ContainerAttrs::default();
93    for attr in attrs {
94        if !attr.path().is_ident("condition") {
95            continue;
96        }
97        attr.parse_nested_meta(|meta| {
98            if meta.path.is_ident("class") {
99                if out.class.is_some() {
100                    return Err(meta.error("`class` is given twice"));
101                }
102                out.class = Some(if meta.input.peek(syn::token::Paren) {
103                    let span = meta.path.span();
104                    let content;
105                    match ::syn::__private::parse_parens(&meta.input) {
    ::syn::__private::Ok(parens) => {
        content = parens.content;
        _ = content;
        parens.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};syn::parenthesized!(content in meta.input);
106                    let list =
107                        Punctuated::<syn::LitStr, ::syn::token::Commasyn::Token![,]>::parse_terminated(&content)?;
108                    if list.is_empty() {
109                        return Err(meta.error("`class(...)` needs at least one class"));
110                    }
111                    ClassSpec::List(list.into_iter().collect(), span)
112                } else {
113                    ClassSpec::One(meta.value()?.parse()?)
114                });
115            } else if meta.path.is_ident("data") {
116                meta.parse_nested_meta(|entry| {
117                    let ident = entry
118                        .path
119                        .get_ident()
120                        .ok_or_else(|| entry.error("a `data` entry is `name = value`"))?
121                        .clone();
122                    let value: syn::Expr = entry.value()?.parse()?;
123                    out.data.push(DataEntry {
124                        name: ident_name(&ident),
125                        value,
126                        span: ident.span(),
127                    });
128                    Ok(())
129                })?;
130            } else if meta.path.is_ident("message") {
131                if out.message.is_some() {
132                    return Err(meta.error("`message` is given twice"));
133                }
134                out.message = Some(meta.value()?.parse()?);
135            } else if meta.path.is_ident("argument_message") {
136                if out.argument_message.is_some() {
137                    return Err(meta.error("`argument_message` is given twice"));
138                }
139                out.argument_message = Some(meta.value()?.parse()?);
140            } else if meta.path.is_ident("kind") {
141                if out.kind.is_some() {
142                    return Err(meta.error("`kind` is given twice"));
143                }
144                let kind: syn::Ident = meta.value()?.parse()?;
145                if !#[allow(non_exhaustive_omitted_patterns)] match kind.to_string().as_str() {
    "error" | "warning" | "message" | "condition" => true,
    _ => false,
}matches!(
146                    kind.to_string().as_str(),
147                    "error" | "warning" | "message" | "condition"
148                ) {
149                    return Err(syn::Error::new(
150                        kind.span(),
151                        "condition kind must be error, warning, message or condition",
152                    ));
153                }
154                out.kind = Some(kind);
155            } else if meta.path.is_ident("call") {
156                if out.call_none {
157                    return Err(meta.error("`call` is given twice"));
158                }
159                let value: syn::Ident = meta.value()?.parse().map_err(|err| {
160                    syn::Error::new(
161                        err.span(),
162                        "`call` takes `none`: `#[condition(call = none)]` raises the condition \
163                         without a call, like R's `call. = FALSE`",
164                    )
165                })?;
166                if value != "none" {
167                    return Err(syn::Error::new(
168                        value.span(),
169                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unknown `call` value `{0}`; the only one is `none` (`#[condition(call = none)]`: no call, like R\'s `call. = FALSE`)",
                value))
    })format!(
170                            "unknown `call` value `{value}`; the only one is `none` \
171                             (`#[condition(call = none)]`: no call, like R's `call. = FALSE`)"
172                        ),
173                    ));
174                }
175                out.call_none = true;
176            } else {
177                return Err(meta.error(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unknown `condition` option on {0}; expected `class = \"…\"`, `class(\"…\", \"…\")`, `message = \"…\"`, `argument_message = \"…\"`, `data(name = value)`, `call = none` or `kind = warning` (error/message/condition also accepted)",
                on))
    })format!(
178                    "unknown `condition` option on {on}; expected `class = \"…\"`, \
179                     `class(\"…\", \"…\")`, `message = \"…\"`, `argument_message = \"…\"`, \
180                     `data(name = value)`, `call = none` or `kind = warning` \
181                     (error/message/condition also accepted)"
182                )));
183            }
184            Ok(())
185        })?;
186    }
187    Ok(out)
188}
189
190/// `#[condition(...)]` options on one field.
191#[derive(#[automatically_derived]
impl ::core::default::Default for FieldAttrs {
    #[inline]
    fn default() -> Self {
        FieldAttrs {
            skip: ::core::default::Default::default(),
            rename: ::core::default::Default::default(),
            debug: ::core::default::Default::default(),
            skip_none: ::core::default::Default::default(),
        }
    }
}Default)]
192struct FieldAttrs {
193    skip: bool,
194    rename: Option<syn::LitStr>,
195    debug: bool,
196    /// Leave the entry out when the `Option` field is `None`.
197    skip_none: bool,
198}
199
200fn parse_field_attrs(attrs: &[syn::Attribute]) -> syn::Result<FieldAttrs> {
201    let mut out = FieldAttrs::default();
202    for attr in attrs {
203        if !attr.path().is_ident("condition") {
204            continue;
205        }
206        attr.parse_nested_meta(|meta| {
207            if meta.path.is_ident("skip") {
208                out.skip = true;
209            } else if meta.path.is_ident("debug") {
210                out.debug = true;
211            } else if meta.path.is_ident("skip_none") {
212                out.skip_none = true;
213            } else if meta.path.is_ident("rename") {
214                if out.rename.is_some() {
215                    return Err(meta.error("`rename` is given twice"));
216                }
217                out.rename = Some(meta.value()?.parse()?);
218            } else {
219                return Err(meta.error(
220                    "unknown `condition` option on a field; expected `skip`, `skip_none`, `debug` \
221                     or `rename = \"…\"`",
222                ));
223            }
224            Ok(())
225        })?;
226    }
227    if out.skip && (out.rename.is_some() || out.debug || out.skip_none) {
228        return Err(syn::Error::new(
229            attrs
230                .iter()
231                .find(|a| a.path().is_ident("condition"))
232                .map_or_else(Span::call_site, Spanned::span),
233            "`skip` cannot be combined with `rename`, `debug` or `skip_none`",
234        ));
235    }
236    Ok(out)
237}
238
239// endregion
240
241// region: Shape
242
243/// One field of a struct or variant, as the derive sees it.
244struct FieldInfo {
245    /// The binding used in patterns: the field name, or `_0` / `_1` / … for
246    /// tuple fields.
247    binding: syn::Ident,
248    /// The `data` entry name; `None` when the field is skipped.
249    data_name: Option<String>,
250    /// Attach `RValue::debug(field)` instead of `RValue::from(field.clone())`.
251    debug: bool,
252    /// An `Option` field whose entry is left out when it is `None`.
253    skip_none: bool,
254}
255
256/// Whether `ty` is written as `Option<_>` (any path ending in `Option`).
257fn is_option(ty: &syn::Type) -> bool {
258    #[allow(non_exhaustive_omitted_patterns)] match ty {
    syn::Type::Path(path) if
        path.qself.is_none() &&
            path.path.segments.last().is_some_and(|segment|
                    segment.ident == "Option") => true,
    _ => false,
}matches!(ty, syn::Type::Path(path) if path.qself.is_none()
259        && path.path.segments.last().is_some_and(|segment| segment.ident == "Option"))
260}
261
262/// Refuse a name the condition object owns, or one already in `seen`.
263fn check_data_name(name: &str, seen: &[String], span: Span, context: &str) -> syn::Result<()> {
264    if RESERVED_FIELDS.contains(&name) {
265        return Err(syn::Error::new(
266            span,
267            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` is one of the condition\'s own slots (`message`, `call`, `kind`); rename the field with `#[condition(rename = \"…\")]` or exclude it with `#[condition(skip)]`",
                name))
    })format!(
268                "`{name}` is one of the condition's own slots (`message`, `call`, `kind`); \
269                 rename the field with `#[condition(rename = \"…\")]` or exclude it with `#[condition(skip)]`"
270            ),
271        ));
272    }
273    if seen.iter().any(|s| s == name) {
274        return Err(syn::Error::new(
275            span,
276            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("duplicate `data` field name `{0}` in {1}",
                name, context))
    })format!("duplicate `data` field name `{name}` in {context}"),
277        ));
278    }
279    Ok(())
280}
281
282enum Style {
283    Named,
284    Tuple,
285    Unit,
286}
287
288/// The fields of a struct or of one enum variant, validated, with the
289/// `data(...)` entries that follow them.
290struct Shape<'a> {
291    style: Style,
292    fields: Vec<FieldInfo>,
293    extra: Vec<&'a DataEntry>,
294}
295
296impl<'a> Shape<'a> {
297    fn from_fields(
298        fields: &Fields,
299        extra: Vec<&'a DataEntry>,
300        context: &str,
301    ) -> syn::Result<Shape<'a>> {
302        let (style, iter): (Style, Vec<&syn::Field>) = match fields {
303            Fields::Named(named) => (Style::Named, named.named.iter().collect()),
304            Fields::Unnamed(unnamed) => (Style::Tuple, unnamed.unnamed.iter().collect()),
305            Fields::Unit => (Style::Unit, Vec::new()),
306        };
307        let mut infos = Vec::with_capacity(iter.len());
308        let mut seen: Vec<String> = Vec::new();
309        for (index, field) in iter.iter().enumerate() {
310            let attrs = parse_field_attrs(&field.attrs)?;
311            let binding = match &field.ident {
312                Some(ident) => ident.clone(),
313                None => ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("_{0}", index))
            }),
    ::quote::__private::Option::Some::<::quote::__private::Span>(field.span()))format_ident!("_{index}", span = field.span()),
314            };
315            let data_name = if attrs.skip {
316                None
317            } else {
318                let name = match (&attrs.rename, &field.ident) {
319                    (Some(lit), _) => lit.value(),
320                    (None, Some(ident)) => ident_name(ident),
321                    (None, None) => {
322                        return Err(syn::Error::new(
323                            field.span(),
324                            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("tuple field {0} of {1} needs a `data` name: add `#[condition(rename = \"name\")]`, or `#[condition(skip)]` to leave it out",
                index, context))
    })format!(
325                                "tuple field {index} of {context} needs a `data` name: add \
326                                 `#[condition(rename = \"name\")]`, or `#[condition(skip)]` to leave it out"
327                            ),
328                        ));
329                    }
330                };
331                check_data_name(&name, &seen, field.span(), context)?;
332                seen.push(name.clone());
333                Some(name)
334            };
335            if attrs.skip_none && !is_option(&field.ty) {
336                return Err(syn::Error::new(
337                    field.ty.span(),
338                    "`skip_none` needs an `Option<_>` field: it leaves the entry out when the field is `None`",
339                ));
340            }
341            infos.push(FieldInfo {
342                binding,
343                data_name,
344                debug: attrs.debug,
345                skip_none: attrs.skip_none,
346            });
347        }
348        for entry in &extra {
349            check_data_name(&entry.name, &seen, entry.span, context)?;
350            seen.push(entry.name.clone());
351        }
352        Ok(Shape {
353            style,
354            fields: infos,
355            extra,
356        })
357    }
358
359    /// A pattern binding every field (for `message` format strings).
360    fn pattern_all(&self, path: &TokenStream) -> TokenStream {
361        let bindings = self.fields.iter().map(|f| &f.binding);
362        match self.style {
363            Style::Named => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            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 bindings, i) = bindings.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let bindings =
                        match bindings.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&bindings, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { #path { #(#bindings),* } },
364            Style::Tuple => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            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 bindings, i) = bindings.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let bindings =
                        match bindings.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&bindings, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { #path ( #(#bindings),* ) },
365            Style::Unit => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    _s
}quote! { #path },
366        }
367    }
368
369    /// A pattern binding only the fields that produce `data` entries.
370    fn pattern_data(&self, path: &TokenStream) -> TokenStream {
371        match self.style {
372            Style::Named => {
373                let bindings = self
374                    .fields
375                    .iter()
376                    .filter(|f| f.data_name.is_some())
377                    .map(|f| &f.binding);
378                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    ::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 bindings, i) = bindings.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let bindings =
                        match bindings.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&bindings, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                }
            }
            ::quote::__private::push_dot2(&mut _s);
            _s
        });
    _s
}quote! { #path { #(#bindings,)* .. } }
379            }
380            Style::Tuple => {
381                let slots = self.fields.iter().map(|f| {
382                    if f.data_name.is_some() {
383                        let b = &f.binding;
384                        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&b, &mut _s);
    _s
}quote! { #b }
385                    } else {
386                        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_underscore(&mut _s);
    _s
}quote! { _ }
387                    }
388                });
389                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            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 slots, i) = slots.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let slots =
                        match slots.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&slots, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { #path ( #(#slots),* ) }
390            }
391            Style::Unit => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    _s
}quote! { #path },
392        }
393    }
394
395    /// A pattern that matches without binding anything.
396    fn pattern_wild(&self, path: &TokenStream) -> TokenStream {
397        match self.style {
398            Style::Unit => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    _s
}quote! { #path },
399            Style::Named | Style::Tuple => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&path, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_dot2(&mut _s);
            _s
        });
    _s
}quote! { #path { .. } },
400        }
401    }
402
403    /// The `Option<ConditionData>` expression over the bindings of
404    /// [`Self::pattern_data`]: the fields' entries, then the `data(...)` ones.
405    fn data_expr(&self) -> TokenStream {
406        let value = |f: &FieldInfo, binding: TokenStream| {
407            if f.debug {
408                {
    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, "RValue");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "debug");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&binding, &mut _s);
            _s
        });
    _s
}quote! { ::miniextendr_api::RValue::debug(#binding) }
409            } else {
410                {
    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, "RValue");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "from");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            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, "clone");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Clone");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "clone");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&binding, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote! { ::miniextendr_api::RValue::from(::core::clone::Clone::clone(#binding)) }
411            }
412        };
413        let extra = self.extra.iter().map(|entry| {
414            let name = &entry.name;
415            let expr = &entry.value;
416            {
    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_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "std");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "string");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "String");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "from");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&name, &mut _s);
                    _s
                });
            ::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, "RValue");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "from");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&expr, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote! { (::std::string::String::from(#name), ::miniextendr_api::RValue::from(#expr)) }
417        });
418        if !self
419            .fields
420            .iter()
421            .any(|f| f.skip_none && f.data_name.is_some())
422        {
423            let entries: Vec<TokenStream> = self
424                .fields
425                .iter()
426                .filter_map(|f| {
427                    let name = f.data_name.as_ref()?;
428                    let binding = &f.binding;
429                    let value = value(f, {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&binding, &mut _s);
    _s
}quote! { #binding });
430                    Some({
    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_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "std");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "string");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "String");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "from");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&name, &mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&value, &mut _s);
            _s
        });
    _s
}quote! { (::std::string::String::from(#name), #value) })
431                })
432                .chain(extra)
433                .collect();
434            return if entries.is_empty() {
435                {
    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, "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");
    _s
}quote! { ::core::option::Option::None }
436            } else {
437                {
    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, "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_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "std");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "vec");
            ::quote::__private::push_bang(&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 entries, i) = entries.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let entries =
                                match entries.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::ToTokens::to_tokens(&entries, &mut _s);
                        }
                    }
                    _s
                });
            _s
        });
    _s
}quote! { ::core::option::Option::Some(::std::vec![#(#entries),*]) }
438            };
439        }
440        // A `None` in a `skip_none` field leaves its entry out, so the
441        // entries are pushed one by one, and none at all is `None`.
442        let pushes = self.fields.iter().filter_map(|f| {
443            let name = f.data_name.as_ref()?;
444            let binding = &f.binding;
445            Some(if f.skip_none {
446                let value = value(f, {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "__value");
    _s
}quote! { __value });
447                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "if");
    ::quote::__private::push_ident(&mut _s, "let");
    ::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, "__value");
            _s
        });
    ::quote::__private::push_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&binding, &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, "__data");
            ::quote::__private::push_dot(&mut _s);
            ::quote::__private::push_ident(&mut _s, "push");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    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_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "std");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "string");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "String");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "from");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::ToTokens::to_tokens(&name, &mut _s);
                                    _s
                                });
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::ToTokens::to_tokens(&value, &mut _s);
                            _s
                        });
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            _s
        });
    _s
}quote! {
448                    if let ::core::option::Option::Some(__value) = #binding {
449                        __data.push((::std::string::String::from(#name), #value));
450                    }
451                }
452            } else {
453                let value = value(f, {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&binding, &mut _s);
    _s
}quote! { #binding });
454                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "__data");
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "push");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            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_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "std");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "string");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "String");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "from");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&name, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::ToTokens::to_tokens(&value, &mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! { __data.push((::std::string::String::from(#name), #value)); }
455            })
456        });
457        let extra = extra.map(|entry| {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "__data");
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "push");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&entry, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! { __data.push(#entry); });
458        {
    let mut _s = ::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, "mut");
            ::quote::__private::push_ident(&mut _s, "__data");
            ::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, "condition");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ConditionData");
            ::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, "vec");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Vec");
            ::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);
            {
                use ::quote::__private::ext::*;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut pushes, i) = pushes.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let pushes =
                        match pushes.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&pushes, &mut _s);
                }
            }
            {
                use ::quote::__private::ext::*;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut extra, i) = extra.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let extra =
                        match extra.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&extra, &mut _s);
                }
            }
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "__data");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "is_empty");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    _s
                });
            ::quote::__private::push_dot(&mut _s);
            ::quote::__private::push_ident(&mut _s, "then_some");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "__data");
                    _s
                });
            _s
        });
    _s
}quote! {{
459            let mut __data: ::miniextendr_api::condition::ConditionData = ::std::vec::Vec::new();
460            #(#pushes)*
461            #(#extra)*
462            (!__data.is_empty()).then_some(__data)
463        }}
464    }
465}
466
467// endregion
468
469// region: Entry point
470
471/// Main entry point for `#[derive(RCondition)]`: the `RConditionData`
472/// metadata and `From<T> for RCondition` with the declared kind.
473pub fn derive_r_condition(input: DeriveInput) -> syn::Result<TokenStream> {
474    let name = &input.ident;
475    if let Some(param) = input
476        .generics
477        .params
478        .iter()
479        .find(|param| !#[allow(non_exhaustive_omitted_patterns)] match param {
    syn::GenericParam::Lifetime(_) => true,
    _ => false,
}matches!(param, syn::GenericParam::Lifetime(_)))
480    {
481        return Err(syn::Error::new(
482            param.span(),
483            "`#[derive(RCondition)]` does not support type or const parameters; \
484             lifetime parameters are fine",
485        ));
486    }
487    let container = parse_container_attrs(&input.attrs, "the type")?;
488    derive_condition(&input, name, container)
489}
490
491/// The `RCondition` variant for a declared kind; no kind is an error.
492fn condition_expr(kind: Option<&syn::Ident>) -> TokenStream {
493    let variant = match kind.map(ToString::to_string).as_deref() {
494        Some("error") | None => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Error");
    _s
}quote! { Error },
495        Some("warning") => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Warning");
    _s
}quote! { Warning },
496        Some("message") => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Message");
    _s
}quote! { Message },
497        Some("condition") => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Condition");
    _s
}quote! { Condition },
498        _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("kind was validated while parsing")));
}unreachable!("kind was validated while parsing"),
499    };
500    {
    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, &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, "message");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "class");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "data");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "call");
            _s
        });
    _s
}quote! { Self::#variant { message, class, data, call } }
501}
502
503fn derive_condition(
504    input: &DeriveInput,
505    name: &syn::Ident,
506    container: ContainerAttrs,
507) -> syn::Result<TokenStream> {
508    let default_family = || to_snake_case(&ident_name(name));
509
510    let condition_body;
511    let display = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::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, "string");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ToString");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "to_string");
    ::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! { ::std::string::ToString::to_string(self) };
512    let call_none = {
    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, "condition");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ConditionCall");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "None");
    _s
}quote! { ::miniextendr_api::condition::ConditionCall::None };
513
514    // `call_body` is `None` when no part of the type asks for `call = none`,
515    // and `argument_message_body` when none gives an `argument_message`: the
516    // impl then keeps the trait's default.
517    let (message_body, class_body, data_body, call_body, argument_message_body) = match &input.data
518    {
519        Data::Struct(data) => {
520            let shape = Shape::from_fields(
521                &data.fields,
522                container.data.iter().collect(),
523                &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`", name))
    })format!("`{name}`"),
524            )?;
525            condition_body = condition_expr(container.kind.as_ref());
526            let self_path = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    _s
}quote! { Self };
527            let message = match &container.message {
528                Some(fmt) => {
529                    let pattern = shape.pattern_all(&self_path);
530                    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "unused_variables");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&pattern, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_ident(&mut _s, "self");
    ::quote::__private::push_semi(&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, "format");
    ::quote::__private::push_bang(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&fmt, &mut _s);
            _s
        });
    _s
}quote! {
531                        #[allow(unused_variables)]
532                        let #pattern = self;
533                        ::std::format!(#fmt)
534                    }
535                }
536                None => display.clone(),
537            };
538            let classes: Vec<String> = match &container.class {
539                Some(ClassSpec::One(lit)) => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [lit.value()]))vec![lit.value()],
540                Some(ClassSpec::List(list, _)) => list.iter().map(syn::LitStr::value).collect(),
541                None => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [default_family()]))vec![default_family()],
542            };
543            let class = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::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, "vec");
    ::quote::__private::push_bang(&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 classes, i) = classes.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let classes =
                        match classes.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::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, "string");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "String");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "from");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&classes, &mut _s);
                            _s
                        });
                }
            }
            _s
        });
    _s
}quote! { ::std::vec![#(::std::string::String::from(#classes)),*] };
544            let pattern = shape.pattern_data(&self_path);
545            let data_expr = shape.data_expr();
546            let data = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&pattern, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_ident(&mut _s, "self");
    ::quote::__private::push_semi(&mut _s);
    ::quote::ToTokens::to_tokens(&data_expr, &mut _s);
    _s
}quote! {
547                let #pattern = self;
548                #data_expr
549            };
550            let call = container.call_none.then(|| call_none.clone());
551            let argument_message = container.argument_message.as_ref().map(|fmt| {
552                let pattern = shape.pattern_all(&self_path);
553                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "unused_variables");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&pattern, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_ident(&mut _s, "self");
    ::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, "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_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "std");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "format");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&fmt, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote! {
554                    #[allow(unused_variables)]
555                    let #pattern = self;
556                    ::core::option::Option::Some(::std::format!(#fmt))
557                }
558            });
559            (message, class, data, call, argument_message)
560        }
561        Data::Enum(data) => {
562            if let Some(message) = &container.message {
563                return Err(syn::Error::new(
564                    message.span(),
565                    "`message` goes on the variants of an enum, not on the enum itself",
566                ));
567            }
568            if let Some(message) = &container.argument_message {
569                return Err(syn::Error::new(
570                    message.span(),
571                    "`argument_message` goes on the variants of an enum, not on the enum itself",
572                ));
573            }
574            if data.variants.is_empty() {
575                return Err(syn::Error::new(
576                    name.span(),
577                    "`#[derive(RCondition)]` needs at least one variant",
578                ));
579            }
580            let family = match &container.class {
581                Some(ClassSpec::One(lit)) => lit.value(),
582                Some(ClassSpec::List(_, span)) => {
583                    return Err(syn::Error::new(
584                        *span,
585                        "on an enum, `class(...)` goes on a variant; the enum's \
586                         `class = \"…\"` names the family every variant ends with",
587                    ));
588                }
589                None => default_family(),
590            };
591            let variant_count = data.variants.len();
592            let mut condition_arms = Vec::new();
593            let mut message_arms = Vec::new();
594            let mut class_arms = Vec::new();
595            let mut data_arms = Vec::new();
596            let mut call_none_arms = Vec::new();
597            let mut argument_message_arms = Vec::new();
598            let mut needs_display = false;
599            for variant in &data.variants {
600                let variant_ident = &variant.ident;
601                let attrs = parse_container_attrs(&variant.attrs, "a variant")?;
602                let members: Vec<String> = match &attrs.class {
603                    Some(ClassSpec::One(lit)) => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [lit.value()]))vec![lit.value()],
604                    Some(ClassSpec::List(list, _)) => list.iter().map(syn::LitStr::value).collect(),
605                    None => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("{1}_{0}",
                                to_snake_case(&ident_name(variant_ident)), family))
                    })]))vec![format!(
606                        "{family}_{}",
607                        to_snake_case(&ident_name(variant_ident))
608                    )],
609                };
610                let shape = Shape::from_fields(
611                    &variant.fields,
612                    container.data.iter().chain(&attrs.data).collect(),
613                    &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("variant `{0}::{1}`", name,
                variant_ident))
    })format!("variant `{name}::{variant_ident}`"),
614                )?;
615                let pattern = shape.pattern_wild(&{
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&variant_ident, &mut _s);
    _s
}quote! { #name::#variant_ident });
616                let expr = condition_expr(attrs.kind.as_ref().or(container.kind.as_ref()));
617                condition_arms.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&pattern, &mut _s);
    ::quote::__private::push_fat_arrow(&mut _s);
    ::quote::ToTokens::to_tokens(&expr, &mut _s);
    ::quote::__private::push_comma(&mut _s);
    _s
}quote! { #pattern => #expr, });
618                let path = {
    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_ident, &mut _s);
    _s
}quote! { Self::#variant_ident };
619                match &attrs.message {
620                    Some(fmt) => {
621                        let pattern = shape.pattern_all(&path);
622                        message_arms.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "unused_variables");
                    _s
                });
            _s
        });
    ::quote::ToTokens::to_tokens(&pattern, &mut _s);
    ::quote::__private::push_fat_arrow(&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, "format");
    ::quote::__private::push_bang(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&fmt, &mut _s);
            _s
        });
    ::quote::__private::push_comma(&mut _s);
    _s
}quote! {
623                            #[allow(unused_variables)]
624                            #pattern => ::std::format!(#fmt),
625                        });
626                    }
627                    None => needs_display = true,
628                }
629                let wild = shape.pattern_wild(&path);
630                class_arms.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&wild, &mut _s);
    ::quote::__private::push_fat_arrow(&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, "vec");
    ::quote::__private::push_bang(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut members, i) = members.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let members =
                        match members.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::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, "string");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "String");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "from");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&members, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&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, "string");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "String");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "from");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&family, &mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    ::quote::__private::push_comma(&mut _s);
    _s
}quote! {
631                    #wild => ::std::vec![
632                        #(::std::string::String::from(#members),)*
633                        ::std::string::String::from(#family),
634                    ],
635                });
636                let pattern = shape.pattern_data(&path);
637                let data_expr = shape.data_expr();
638                data_arms.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&pattern, &mut _s);
    ::quote::__private::push_fat_arrow(&mut _s);
    ::quote::ToTokens::to_tokens(&data_expr, &mut _s);
    ::quote::__private::push_comma(&mut _s);
    _s
}quote! { #pattern => #data_expr, });
639                if let Some(fmt) = &attrs.argument_message {
640                    let pattern = shape.pattern_all(&path);
641                    argument_message_arms.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "unused_variables");
                    _s
                });
            _s
        });
    ::quote::ToTokens::to_tokens(&pattern, &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_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "std");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "format");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&fmt, &mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_comma(&mut _s);
    _s
}quote! {
642                        #[allow(unused_variables)]
643                        #pattern => ::core::option::Option::Some(::std::format!(#fmt)),
644                    });
645                }
646                if attrs.call_none {
647                    call_none_arms.push(wild);
648                }
649            }
650            condition_body = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "match");
    ::quote::__private::push_ident(&mut _s, "value");
    ::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 condition_arms, i) =
                    condition_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let condition_arms =
                        match condition_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&condition_arms, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { match value { #(#condition_arms)* } };
651            let message = if message_arms.is_empty() {
652                display
653            } else if needs_display {
654                {
    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 message_arms, i) = message_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let message_arms =
                        match message_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&message_arms, &mut _s);
                }
            }
            ::quote::__private::push_underscore(&mut _s);
            ::quote::__private::push_fat_arrow(&mut _s);
            ::quote::ToTokens::to_tokens(&display, &mut _s);
            _s
        });
    _s
}quote! { match self { #(#message_arms)* _ => #display } }
655            } else {
656                {
    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 message_arms, i) = message_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let message_arms =
                        match message_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&message_arms, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { match self { #(#message_arms)* } }
657            };
658            let class = {
    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 class_arms, i) = class_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let class_arms =
                        match class_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&class_arms, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { match self { #(#class_arms)* } };
659            let data = {
    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 data_arms, i) = data_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let data_arms =
                        match data_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&data_arms, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { match self { #(#data_arms)* } };
660            let call = if container.call_none || call_none_arms.len() == variant_count {
661                Some(call_none.clone())
662            } else if call_none_arms.is_empty() {
663                None
664            } else {
665                Some({
    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 mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut call_none_arms, i) =
                    call_none_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let call_none_arms =
                        match call_none_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_or(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&call_none_arms, &mut _s);
                }
            }
            ::quote::__private::push_fat_arrow(&mut _s);
            ::quote::ToTokens::to_tokens(&call_none, &mut _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, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "condition");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ConditionCall");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Inherit");
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    _s
}quote! {
666                    match self {
667                        #(#call_none_arms)|* => #call_none,
668                        _ => ::miniextendr_api::condition::ConditionCall::Inherit,
669                    }
670                })
671            };
672            let argument_message = if argument_message_arms.is_empty() {
673                None
674            } else if argument_message_arms.len() == variant_count {
675                Some({
    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 argument_message_arms, i) =
                    argument_message_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let argument_message_arms =
                        match argument_message_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&argument_message_arms,
                        &mut _s);
                }
            }
            _s
        });
    _s
}quote! { match self { #(#argument_message_arms)* } })
676            } else {
677                Some({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s,
                        "unreachable_patterns");
                    _s
                });
            _s
        });
    ::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 argument_message_arms, i) =
                    argument_message_arms.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let argument_message_arms =
                        match argument_message_arms.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&argument_message_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
}quote! {
678                    #[allow(unreachable_patterns)]
679                    match self {
680                        #(#argument_message_arms)*
681                        _ => ::core::option::Option::None,
682                    }
683                })
684            };
685            (message, class, data, call, argument_message)
686        }
687        Data::Union(_) => {
688            return Err(syn::Error::new(
689                name.span(),
690                "`#[derive(RCondition)]` supports structs and enums, not unions",
691            ));
692        }
693    };
694
695    let call_method = call_body.map(|body| {
696        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::__private::push_ident(&mut _s, "call");
    ::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_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, "condition");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ConditionCall");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&body, &mut _s);
            _s
        });
    _s
}quote! {
697            fn call(&self) -> ::miniextendr_api::condition::ConditionCall {
698                #body
699            }
700        }
701    });
702    let argument_message_method = argument_message_body.map(|body| {
703        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::__private::push_ident(&mut _s, "argument_message");
    ::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_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, "option");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Option");
    ::quote::__private::push_lt(&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, "string");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "String");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&body, &mut _s);
            _s
        });
    _s
}quote! {
704            fn argument_message(&self) -> ::core::option::Option<::std::string::String> {
705                #body
706            }
707        }
708    });
709
710    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
711    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "automatically_derived");
            _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, "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, "From");
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "for");
    ::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, "condition");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "RCondition");
    ::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, "from");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "value");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::ToTokens::to_tokens(&name, &mut _s);
                    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::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, "message");
                    ::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, "condition");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "RConditionData");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "message");
                    ::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_ident(&mut _s, "value");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "class");
                    ::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, "condition");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "RConditionData");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "class");
                    ::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_ident(&mut _s, "value");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "data");
                    ::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, "condition");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "RConditionData");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "data");
                    ::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_ident(&mut _s, "value");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "call");
                    ::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, "condition");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "RConditionData");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "call");
                    ::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_ident(&mut _s, "value");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::ToTokens::to_tokens(&condition_body, &mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "automatically_derived");
            _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, "condition");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "RConditionData");
    ::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, "message");
            ::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_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&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, "string");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "String");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&message_body, &mut _s);
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "class");
            ::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_ident(&mut _s, "self");
                    _s
                });
            ::quote::__private::push_rarrow(&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, "vec");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Vec");
            ::quote::__private::push_lt(&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, "string");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "String");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&class_body, &mut _s);
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "data");
            ::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_ident(&mut _s, "self");
                    ::quote::__private::push_comma(&mut _s);
                    _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_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "condition");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ConditionData");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&data_body, &mut _s);
                    _s
                });
            ::quote::ToTokens::to_tokens(&call_method, &mut _s);
            ::quote::ToTokens::to_tokens(&argument_message_method, &mut _s);
            _s
        });
    _s
}quote! {
712        #[automatically_derived]
713        impl #impl_generics ::core::convert::From<#name #ty_generics>
714            for ::miniextendr_api::condition::RCondition #where_clause
715        {
716            fn from(value: #name #ty_generics) -> Self {
717                let message = ::miniextendr_api::condition::RConditionData::message(&value);
718                let class = ::miniextendr_api::condition::RConditionData::class(&value);
719                let data = ::miniextendr_api::condition::RConditionData::data(&value);
720                let call = ::miniextendr_api::condition::RConditionData::call(&value);
721                #condition_body
722            }
723        }
724
725        #[automatically_derived]
726        impl #impl_generics ::miniextendr_api::condition::RConditionData
727            for #name #ty_generics #where_clause
728        {
729            fn message(&self) -> ::std::string::String {
730                #message_body
731            }
732
733            fn class(&self) -> ::std::vec::Vec<::std::string::String> {
734                #class_body
735            }
736
737            fn data(
738                &self,
739            ) -> ::core::option::Option<::miniextendr_api::condition::ConditionData> {
740                #data_body
741            }
742
743            #call_method
744
745            #argument_message_method
746        }
747    })
748}
749
750// endregion
751
752// region: Tests
753
754#[cfg(test)]
755mod tests {
756    use super::*;
757
758    fn derive(input: DeriveInput) -> String {
759        derive_r_condition(input).unwrap().to_string()
760    }
761
762    fn derive_err(input: DeriveInput) -> String {
763        derive_r_condition(input).unwrap_err().to_string()
764    }
765
766    #[test]
767    fn condition_rejects_invalid_and_duplicate_kinds() {
768        for input in [
769            syn::parse_quote! {
770                #[condition(kind = panic)]
771                struct Invalid;
772            },
773            syn::parse_quote! {
774                #[condition(kind = warning, kind = error)]
775                struct Duplicate;
776            },
777            syn::parse_quote! {
778                enum InvalidVariant {
779                    #[condition(kind = conversion)]
780                    Bad,
781                }
782            },
783        ] {
784            assert!(derive_r_condition(input).is_err());
785        }
786    }
787
788    #[test]
789    fn kind_defaults_to_error_and_a_variant_overrides_the_type() {
790        let code = derive(syn::parse_quote! {
791            struct Plain;
792        });
793        assert!(code.contains("Self :: Error { message , class , data , call }"));
794        let code = derive(syn::parse_quote! {
795            #[condition(kind = warning)]
796            enum Mixed {
797                #[condition(kind = message)]
798                Note,
799                #[condition(kind = error)]
800                Fatal,
801                Drift,
802            }
803        });
804        assert!(code.contains("Mixed :: Note => Self :: Message"));
805        assert!(code.contains("Mixed :: Fatal => Self :: Error"));
806        assert!(code.contains("Mixed :: Drift => Self :: Warning"));
807    }
808
809    #[test]
810    fn enum_classes_default_to_snake_case_family_and_member() {
811        let code = derive(syn::parse_quote! {
812            enum PkgError {
813                MissingField { field: String },
814                OutOfRange { value: f64, max: f64 },
815                Plain,
816            }
817        });
818        assert!(code.contains("\"pkg_error_missing_field\""));
819        assert!(code.contains("\"pkg_error_out_of_range\""));
820        assert!(code.contains("\"pkg_error_plain\""));
821        assert!(code.contains("\"pkg_error\""));
822        // No `message` anywhere: Display is the message.
823        assert!(code.contains("ToString :: to_string (self)"));
824        assert!(!code.contains("format !"));
825        // Fields become data entries under their own names.
826        assert!(code.contains("\"field\""));
827        assert!(code.contains("\"max\""));
828        assert!(code.contains("Clone :: clone (max)"));
829    }
830
831    #[test]
832    fn explicit_classes_messages_and_field_options() {
833        let code = derive(syn::parse_quote! {
834            #[condition(class = "pkg_warning")]
835            enum PkgWarning {
836                #[condition(message = "dropped {dropped} of {total} rows")]
837                Truncated { dropped: i32, total: i32 },
838                #[condition(class = "pkg_warning_slow_path", message = "slow")]
839                Slow,
840                Other {
841                    #[condition(rename = "n_attempts")]
842                    attempts: i32,
843                    #[condition(debug)]
844                    range: std::ops::Range<i32>,
845                    #[condition(skip)]
846                    detail: Vec<u8>,
847                },
848            }
849        });
850        assert!(code.contains("\"pkg_warning_truncated\""));
851        assert!(code.contains("\"pkg_warning_slow_path\""));
852        assert!(code.contains("\"pkg_warning_other\""));
853        assert!(code.contains("format ! (\"dropped {dropped} of {total} rows\")"));
854        // A variant without `message` keeps the Display fallback arm.
855        assert!(code.contains("_ => :: std :: string :: ToString :: to_string (self)"));
856        assert!(code.contains("\"n_attempts\""));
857        assert!(!code.contains("\"attempts\""));
858        assert!(code.contains("RValue :: debug (range)"));
859        assert!(!code.contains("\"detail\""));
860        // The skipped field is not bound in the data pattern.
861        assert!(code.contains("Self :: Other { attempts , range , .. } =>"));
862    }
863
864    #[test]
865    fn struct_shapes() {
866        let named = derive(syn::parse_quote! {
867            #[condition(message = "took {attempts} attempts")]
868            struct RetryNotice {
869                attempts: i32,
870            }
871        });
872        assert!(named.contains("\"retry_notice\""));
873        assert!(named.contains("let Self { attempts } = self"));
874        assert!(named.contains("format ! (\"took {attempts} attempts\")"));
875
876        let tuple = derive(syn::parse_quote! {
877            struct Pair(#[condition(rename = "left")] i32, #[condition(skip)] String);
878        });
879        assert!(tuple.contains("\"left\""));
880        assert!(tuple.contains("Self (_0 , _)"));
881
882        let unit = derive(syn::parse_quote! {
883            struct Nothing;
884        });
885        assert!(unit.contains("Option :: None"));
886        assert!(unit.contains("\"nothing\""));
887    }
888
889    #[test]
890    fn call_none_on_the_type_or_a_variant() {
891        // On a variant: only that variant is call-less.
892        let code = derive(syn::parse_quote! {
893            #[condition(class = "pkg_warning")]
894            enum PkgWarning {
895                #[condition(call = none, message = "row {row} overrides an earlier one")]
896                Overridden { row: i32 },
897                Truncated { dropped: i32 },
898            }
899        });
900        assert!(code.contains("fn call (& self)"));
901        assert!(code.contains(
902            "Self :: Overridden { .. } => :: miniextendr_api :: condition :: ConditionCall :: None"
903        ));
904        assert!(code.contains("_ => :: miniextendr_api :: condition :: ConditionCall :: Inherit"));
905
906        // On the type, or on every variant: no match at all.
907        for code in [
908            derive(syn::parse_quote! {
909                #[condition(call = none)]
910                enum E { A, B { n: i32 } }
911            }),
912            derive(syn::parse_quote! {
913                enum E {
914                    #[condition(call = none)]
915                    A,
916                    #[condition(call = none)]
917                    B { n: i32 },
918                }
919            }),
920            derive(syn::parse_quote! {
921                #[condition(call = none, message = "plain")]
922                struct Notice;
923            }),
924        ] {
925            assert!(code.contains("fn call (& self) -> :: miniextendr_api :: condition :: ConditionCall { :: miniextendr_api :: condition :: ConditionCall :: None }"), "{code}");
926        }
927
928        // Without `call = none` the trait default stays.
929        let code = derive(syn::parse_quote! {
930            enum E { A, B }
931        });
932        assert!(!code.contains("fn call"));
933    }
934
935    #[test]
936    fn rejects_bad_call_values() {
937        let err = derive_err(syn::parse_quote! {
938            #[condition(call = wrapper)]
939            struct S;
940        });
941        assert!(err.contains("unknown `call` value `wrapper`"), "{err}");
942
943        let err = derive_err(syn::parse_quote! {
944            #[condition(call = "none")]
945            struct S;
946        });
947        assert!(err.contains("`call` takes `none`"), "{err}");
948
949        let err = derive_err(syn::parse_quote! {
950            enum E {
951                #[condition(call = none, call = none)]
952                A,
953            }
954        });
955        assert!(err.contains("`call` is given twice"), "{err}");
956
957        let err = derive_err(syn::parse_quote! {
958            struct S {
959                #[condition(call = none)]
960                a: i32,
961            }
962        });
963        assert!(
964            err.contains("unknown `condition` option on a field"),
965            "{err}"
966        );
967    }
968
969    #[test]
970    fn rejects_type_and_const_parameters_and_unions() {
971        let err = derive_err(syn::parse_quote! {
972            struct Generic<T> {
973                value: T,
974            }
975        });
976        assert!(err.contains("type or const parameters"), "{err}");
977
978        let err = derive_err(syn::parse_quote! {
979            struct Sized<'a, const N: usize> {
980                value: &'a [i32; N],
981            }
982        });
983        assert!(err.contains("type or const parameters"), "{err}");
984
985        let err = derive_err(syn::parse_quote! {
986            union U {
987                a: u32,
988                b: f32,
989            }
990        });
991        assert!(err.contains("not unions"));
992    }
993
994    #[test]
995    fn rejects_unnamed_tuple_fields_and_reserved_or_duplicate_names() {
996        let err = derive_err(syn::parse_quote! {
997            enum E {
998                Raw(i32),
999            }
1000        });
1001        assert!(err.contains("tuple field 0 of variant `E::Raw` needs a `data` name"));
1002
1003        let err = derive_err(syn::parse_quote! {
1004            struct S {
1005                kind: String,
1006            }
1007        });
1008        assert!(err.contains("`kind` is one of the condition's own slots"));
1009
1010        let err = derive_err(syn::parse_quote! {
1011            struct S {
1012                #[condition(rename = "message")]
1013                text: String,
1014            }
1015        });
1016        assert!(err.contains("`message` is one of the condition's own slots"));
1017
1018        let err = derive_err(syn::parse_quote! {
1019            struct S {
1020                a: i32,
1021                #[condition(rename = "a")]
1022                b: i32,
1023            }
1024        });
1025        assert!(err.contains("duplicate `data` field name `a`"));
1026    }
1027
1028    #[test]
1029    fn rejects_misplaced_or_unknown_options() {
1030        let err = derive_err(syn::parse_quote! {
1031            #[condition(message = "nope")]
1032            enum E {
1033                A,
1034            }
1035        });
1036        assert!(err.contains("goes on the variants"));
1037
1038        let err = derive_err(syn::parse_quote! {
1039            #[condition(prefix = "x")]
1040            struct S {
1041                a: i32,
1042            }
1043        });
1044        assert!(err.contains("unknown `condition` option on the type"));
1045
1046        let err = derive_err(syn::parse_quote! {
1047            struct S {
1048                #[condition(class = "x")]
1049                a: i32,
1050            }
1051        });
1052        assert!(err.contains("unknown `condition` option on a field"));
1053
1054        let err = derive_err(syn::parse_quote! {
1055            struct S {
1056                #[condition(skip, rename = "b")]
1057                a: i32,
1058            }
1059        });
1060        assert!(err.contains("`skip` cannot be combined"));
1061
1062        let err = derive_err(syn::parse_quote! {
1063            enum Empty {}
1064        });
1065        assert!(err.contains("at least one variant"));
1066
1067        let err = derive_err(syn::parse_quote! {
1068            #[condition(argument_message = "nope")]
1069            enum E {
1070                A,
1071            }
1072        });
1073        assert!(
1074            err.contains("`argument_message` goes on the variants"),
1075            "{err}"
1076        );
1077
1078        let err = derive_err(syn::parse_quote! {
1079            #[condition(argument_message = "a", argument_message = "b")]
1080            struct S;
1081        });
1082        assert!(err.contains("`argument_message` is given twice"), "{err}");
1083    }
1084
1085    /// `argument_message` on a struct, or on some variants of an enum (the
1086    /// rest keep `None`), is a `format!` string over the fields; without it
1087    /// the impl keeps the trait's default (#1833).
1088    #[test]
1089    fn argument_message_on_a_struct_or_on_variants() {
1090        let code = derive(syn::parse_quote! {
1091            #[condition(argument_message = "use model_from_df() for a {what}")]
1092            struct ModelRefused {
1093                what: String,
1094            }
1095        });
1096        assert!(code.contains("fn argument_message (& self)"), "{code}");
1097        assert!(
1098            code.contains("Some (:: std :: format ! (\"use model_from_df() for a {what}\"))"),
1099            "{code}"
1100        );
1101
1102        let code = derive(syn::parse_quote! {
1103            enum Refused {
1104                #[condition(argument_message = "use model_from_df() for a data frame")]
1105                DataFrame,
1106                Other { class: String },
1107            }
1108        });
1109        assert!(code.contains("fn argument_message (& self)"), "{code}");
1110        assert!(
1111            code.contains("_ => :: core :: option :: Option :: None"),
1112            "{code}"
1113        );
1114
1115        let code = derive(syn::parse_quote! {
1116            struct Plain;
1117        });
1118        assert!(!code.contains("fn argument_message"), "{code}");
1119    }
1120
1121    #[test]
1122    fn lifetime_parameters_reach_both_impls() {
1123        let code = derive(syn::parse_quote! {
1124            #[condition(class = "pkg_engine_warning")]
1125            struct EngineWarning<'a>(#[condition(skip)] &'a str);
1126        });
1127        assert!(
1128            code.contains("impl < 'a > :: core :: convert :: From < EngineWarning < 'a > >"),
1129            "{code}"
1130        );
1131        assert!(
1132            code.contains("fn from (value : EngineWarning < 'a >)"),
1133            "{code}"
1134        );
1135        assert!(
1136            code.contains("RConditionData for EngineWarning < 'a >"),
1137            "{code}"
1138        );
1139    }
1140
1141    #[test]
1142    fn class_list_on_a_struct_is_its_whole_class_vector() {
1143        let code = derive(syn::parse_quote! {
1144            #[condition(class("pkg_message_selection_dropped", "pkg_message"))]
1145            struct SelectionDropped {
1146                missing: Vec<String>,
1147            }
1148        });
1149        assert!(
1150            code.contains(
1151                r#"vec ! [:: std :: string :: String :: from ("pkg_message_selection_dropped") , :: std :: string :: String :: from ("pkg_message")]"#
1152            ),
1153            "{code}"
1154        );
1155    }
1156
1157    #[test]
1158    fn class_list_on_a_variant_replaces_the_member() {
1159        let code = derive(syn::parse_quote! {
1160            #[condition(class = "pkg_warning")]
1161            enum PkgWarning {
1162                #[condition(class("pkg_warning_engine_masked", "pkg_engine_warning"))]
1163                Masked,
1164                Plain,
1165            }
1166        });
1167        assert!(
1168            code.contains(
1169                r#"Self :: Masked => :: std :: vec ! [:: std :: string :: String :: from ("pkg_warning_engine_masked") , :: std :: string :: String :: from ("pkg_engine_warning") , :: std :: string :: String :: from ("pkg_warning") ,]"#
1170            ),
1171            "{code}"
1172        );
1173        assert!(code.contains(r#""pkg_warning_plain""#), "{code}");
1174    }
1175
1176    #[test]
1177    fn class_list_errors() {
1178        let err = derive_err(syn::parse_quote! {
1179            #[condition(class("a", "b"))]
1180            enum E {
1181                One,
1182            }
1183        });
1184        assert!(
1185            err.contains("on an enum, `class(...)` goes on a variant"),
1186            "{err}"
1187        );
1188
1189        let err = derive_err(syn::parse_quote! {
1190            #[condition(class())]
1191            struct S;
1192        });
1193        assert!(err.contains("needs at least one class"), "{err}");
1194
1195        let err = derive_err(syn::parse_quote! {
1196            #[condition(class = "a", class("b"))]
1197            struct S;
1198        });
1199        assert!(err.contains("`class` is given twice"), "{err}");
1200    }
1201
1202    #[test]
1203    fn data_entries_follow_the_fields() {
1204        let code = derive(syn::parse_quote! {
1205            #[condition(class = "pkg_warning", data(param = "unit_table"))]
1206            struct UnitTableIgnored;
1207        });
1208        assert!(
1209            code.contains(
1210                r#"Some (:: std :: vec ! [(:: std :: string :: String :: from ("param") , :: miniextendr_api :: RValue :: from ("unit_table"))])"#
1211            ),
1212            "{code}"
1213        );
1214
1215        // On an enum: the fields' entries, then the type's, then the variant's.
1216        let code = derive(syn::parse_quote! {
1217            #[condition(data(source = "engine"))]
1218            enum E {
1219                #[condition(data(r#type = 2i32))]
1220                One { n: i32 },
1221            }
1222        });
1223        let n = code.find(r#""n""#).expect("field entry");
1224        let source = code.find(r#""source""#).expect("type entry");
1225        let ty = code.find(r#""type""#).expect("variant entry");
1226        assert!(n < source && source < ty, "{code}");
1227    }
1228
1229    #[test]
1230    fn data_entry_names_are_checked_like_fields() {
1231        let err = derive_err(syn::parse_quote! {
1232            #[condition(data(kind = "x"))]
1233            struct S;
1234        });
1235        assert!(
1236            err.contains("`kind` is one of the condition's own slots"),
1237            "{err}"
1238        );
1239
1240        let err = derive_err(syn::parse_quote! {
1241            #[condition(data(n = 1i32))]
1242            struct S {
1243                n: i32,
1244            }
1245        });
1246        assert!(err.contains("duplicate `data` field name `n`"), "{err}");
1247
1248        let err = derive_err(syn::parse_quote! {
1249            #[condition(data(a::b = 1i32))]
1250            struct S;
1251        });
1252        assert!(err.contains("a `data` entry is `name = value`"), "{err}");
1253    }
1254
1255    #[test]
1256    fn skip_none_leaves_a_none_entry_out() {
1257        let code = derive(syn::parse_quote! {
1258            struct Masked {
1259                verb: String,
1260                #[condition(skip_none)]
1261                given_to: Option<String>,
1262            }
1263        });
1264        assert!(
1265            code.contains("if let :: core :: option :: Option :: Some (__value) = given_to"),
1266            "{code}"
1267        );
1268        assert!(
1269            code.contains("(! __data . is_empty ()) . then_some (__data)"),
1270            "{code}"
1271        );
1272        // The plain field is pushed whatever its value.
1273        assert!(
1274            code.contains(r#"__data . push ((:: std :: string :: String :: from ("verb")"#),
1275            "{code}"
1276        );
1277    }
1278
1279    #[test]
1280    fn skip_none_errors() {
1281        let err = derive_err(syn::parse_quote! {
1282            struct S {
1283                #[condition(skip_none)]
1284                n: i32,
1285            }
1286        });
1287        assert!(
1288            err.contains("`skip_none` needs an `Option<_>` field"),
1289            "{err}"
1290        );
1291
1292        let err = derive_err(syn::parse_quote! {
1293            struct S {
1294                #[condition(skip, skip_none)]
1295                n: Option<i32>,
1296            }
1297        });
1298        assert!(
1299            err.contains("`skip` cannot be combined with `rename`, `debug` or `skip_none`"),
1300            "{err}"
1301        );
1302    }
1303}
1304
1305// endregion