inpt_macros/
lib.rs

1use darling::{ast::Data, util::SpannedValue, FromDeriveInput, FromField, FromVariant};
2use proc_macro2::{Ident, Span, TokenStream};
3use quote::{format_ident, quote, quote_spanned};
4use regex_syntax::ast as regex_ast;
5use std::{convert::Infallible, mem::replace};
6use syn::{
7    parse::{Parse, ParseStream},
8    parse_macro_input, parse_quote, parse_quote_spanned,
9    punctuated::Punctuated,
10    spanned::Spanned,
11    DeriveInput, GenericParam, Index, ItemFn, Token,
12};
13
14#[proc_macro]
15pub fn generate_inpt_impls(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
16    // Create tuple impls
17    ('B'..='G')
18        .map(|last| {
19            let names = 'A'..=last;
20            let idents: Vec<Ident> = names.clone().map(|i| format_ident!("{}", i)).collect();
21            let idents = &idents;
22            let is_last = (0..idents.len()).map(|i| i + 1 == idents.len());
23            quote! {
24                impl<'s, #(#idents: ::inpt::Inpt<'s>),*> ::inpt::Inpt<'s> for (#(#idents),*) {
25                    fn step(text: &'s str, end: bool, trimmed: ::inpt::CharClass, guard: &mut ::inpt::RecursionGuard) -> ::inpt::InptStep<'s, Self> {
26                        use ::inpt::ResultExt as _;
27
28                        let mut rest = text;
29                        let mut this = || Ok((#(
30                            {
31                                rest = #idents::default_trim(trimmed).trim(rest, #is_last && end);
32                                let step = #idents::step(rest, #is_last && end, trimmed, guard);
33                                rest = step.rest;
34                                step.data?
35                            },
36                        )*));
37                        ::inpt::InptStep {
38                            data: this().within(text).expected::<Self>(),
39                            rest,
40                        }
41                    }
42                }
43            }
44        })
45        .collect::<TokenStream>()
46        .into()
47}
48
49#[proc_macro]
50pub fn char_class(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
51    let source = parse_macro_input!(args as syn::LitStr);
52    match parse_char_class(&SpannedValue::new(source.value(), source.span())) {
53        Ok(out) => out.into(),
54        Err(err) => err.into_compile_error().into(),
55    }
56}
57
58fn parse_char_class(source: &SpannedValue<String>) -> syn::Result<TokenStream> {
59    use regex_syntax::hir::{Class, HirKind};
60
61    if source.is_empty() {
62        return Ok(quote! { ::inpt::CharClass(&[]) });
63    }
64
65    let regex = format!("[{}]", **source);
66    let hir = match regex_syntax::Parser::new().parse(&regex) {
67        Ok(hir) => hir,
68        Err(err) => return Err(syn::Error::new(source.span(), err.to_string())),
69    };
70    let class = match hir.kind() {
71        HirKind::Class(Class::Unicode(class)) => class,
72        _ => panic!("did not parse as hir class"),
73    };
74    let tuples: TokenStream = class
75        .iter()
76        .map(|range| {
77            let start = range.start();
78            let end = range.end();
79            quote! { (#start, #end), }
80        })
81        .collect();
82    Ok(quote! { ::inpt::CharClass(&[#tuples]) })
83}
84
85struct ImplInptAs {
86    _impl: Token![impl],
87    generics: syn::Generics,
88    ty: syn::Type,
89    _as: Token![as],
90    wrapper: syn::Type,
91    _arrow: Token![=>],
92    x: syn::Expr,
93}
94
95impl Parse for ImplInptAs {
96    fn parse(input: ParseStream) -> syn::Result<Self> {
97        let _impl = input.parse()?;
98        let mut generics: syn::Generics = input.parse()?;
99        let ty = input.parse()?;
100        let _as = input.parse()?;
101        let wrapper = input.parse()?;
102        generics.where_clause = input.parse()?;
103        let _arrow = input.parse()?;
104        let x = input.parse()?;
105        Ok(ImplInptAs {
106            _impl,
107            generics,
108            ty,
109            _as,
110            wrapper,
111            _arrow,
112            x,
113        })
114    }
115}
116
117struct ImplInptAsArgs {
118    impls: Punctuated<ImplInptAs, Token![,]>,
119}
120
121impl Parse for ImplInptAsArgs {
122    fn parse(input: ParseStream) -> syn::Result<Self> {
123        Ok(ImplInptAsArgs {
124            impls: Punctuated::parse_terminated(input)?,
125        })
126    }
127}
128
129#[proc_macro]
130pub fn impl_inpt_as(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
131    let ImplInptAsArgs { impls } = parse_macro_input!(args as ImplInptAsArgs);
132    impls
133        .into_iter()
134        .map(|impl_as| {
135            let ImplInptAs {
136                mut generics,
137                ty,
138                wrapper,
139                x,
140                ..
141            } = impl_as;
142            let lt = generics.lifetimes().next().cloned();
143            let lt = match lt {
144                Some(lt) => lt,
145                None => {
146                    let lt: syn::LifetimeDef = parse_quote! { 's };
147                    generics
148                        .params
149                        .insert(0, GenericParam::Lifetime(lt.clone()));
150                    lt
151                }
152            };
153            let lt = &lt;
154            let (impl_generics, _, where_clause) = generics.split_for_impl();
155            quote! {
156                impl #impl_generics ::inpt::Inpt<#lt> for #ty #where_clause {
157                    fn step(text: &#lt str, end: bool, trimmed: ::inpt::CharClass, guard: &mut ::inpt::RecursionGuard) -> ::inpt::InptStep<#lt, Self> {
158                        use ::inpt::ResultExt as _;
159                        let mut step = <#wrapper as ::inpt::Inpt<#lt>>::step(text, end, trimmed, guard).map(#x);
160                        step.data = step.data.replace_expected::<#wrapper, Self>();
161                        step
162                    }
163
164                    fn default_trim(inherited: ::inpt::CharClass) -> ::inpt::CharClass {
165                        <#wrapper as ::inpt::Inpt<#lt>>::default_trim(inherited)
166                    }
167                }
168            }
169        })
170        .collect::<TokenStream>()
171        .into()
172}
173
174#[derive(FromField)]
175#[darling(attributes(inpt))]
176struct InputField {
177    ty: syn::Type,
178    ident: Option<syn::Ident>,
179    #[darling(default)]
180    from_iter: SpannedValue<Option<SpannedValue<String>>>,
181    #[darling(default)]
182    from_str: SpannedValue<Option<bool>>,
183    #[darling(default)]
184    skip: Option<bool>,
185    #[darling(default)]
186    after: Option<bool>,
187    #[darling(default)]
188    before: Option<bool>,
189    #[darling(default)]
190    trim: Option<SpannedValue<String>>,
191    #[darling(default)]
192    split: Option<SpannedValue<String>>,
193    #[darling(skip)]
194    index: usize,
195}
196
197impl InputField {
198    fn skip(&self) -> bool {
199        self.skip.unwrap_or(false)
200    }
201
202    fn from_str(&self) -> bool {
203        self.from_str.unwrap_or(false)
204    }
205
206    fn after(&self) -> bool {
207        !self.skip() && self.after.unwrap_or(false)
208    }
209
210    fn before(&self) -> bool {
211        !self.skip() && self.before.unwrap_or(false)
212    }
213
214    fn capture(&self) -> bool {
215        !(self.skip() || self.after() || self.before())
216    }
217}
218
219#[derive(FromVariant)]
220#[darling(attributes(inpt))]
221struct InputVariant {
222    ident: syn::Ident,
223    fields: darling::ast::Fields<SpannedValue<InputField>>,
224    #[darling(default)]
225    regex: Option<SpannedValue<String>>,
226}
227
228#[derive(FromDeriveInput)]
229#[darling(attributes(inpt))]
230struct InputOpts {
231    ident: syn::Ident,
232    generics: syn::Generics,
233    data: Data<InputVariant, SpannedValue<InputField>>,
234    #[darling(default)]
235    regex: Option<SpannedValue<String>>,
236    #[darling(default)]
237    bounds: Option<syn::LitStr>,
238    #[darling(default)]
239    trim: Option<SpannedValue<String>>,
240    #[darling(default)]
241    from: Option<syn::LitStr>,
242    #[darling(default)]
243    try_from: Option<syn::LitStr>,
244}
245
246fn from_str_field(ty: &syn::Type, input: Ident) -> TokenStream {
247    quote! {
248        match <#ty as ::std::str::FromStr>::from_str(#input) {
249            Ok(val) => Ok(val),
250            Err(e) => Err(::inpt::InptError::expected_from_str::<#ty>(#input))
251                .message_inside(e.to_string())
252        }
253    }
254}
255
256fn parse_type(ty: &SpannedValue<String>) -> syn::Result<syn::Type> {
257    match syn::parse_str::<syn::Type>(ty) {
258        Ok(ty) => Ok(ty),
259        Err(e) => {
260            let mut err = syn::Error::new(ty.span(), "could not parse type");
261            err.combine(e);
262            Err(err)
263        }
264    }
265}
266
267fn from_iter_field(
268    ty: &syn::Type,
269    item: &syn::Type,
270    item_span: Span,
271    input: Ident,
272    trimmed: Ident,
273) -> syn::Result<TokenStream> {
274    let item = quote_spanned! { item_span => #item };
275    Ok(quote_spanned! { item_span => {
276        let mut iter = ::inpt::InptIter::new(#input, #trimmed);
277        let data = <#ty as ::std::iter::FromIterator<#item>>::from_iter(&mut iter);
278        iter.outcome.map(|_| data)
279    } })
280}
281
282type Predicates = syn::punctuated::Punctuated<syn::WherePredicate, Token![,]>;
283
284fn type_as_option(ty: &syn::Type) -> Option<&syn::Type> {
285    let path = match ty {
286        syn::Type::Path(p) => p,
287        _ => return None,
288    };
289    if path.qself.is_some() {
290        return None;
291    }
292    if path.path.leading_colon.is_some() {
293        return None;
294    }
295    if path.path.segments.len() != 1 {
296        return None;
297    }
298    let segment = &path.path.segments[0];
299    if segment.ident != "Option" {
300        return None;
301    }
302    let args = match &segment.arguments {
303        syn::PathArguments::AngleBracketed(a) => a,
304        _ => return None,
305    };
306    if args.args.len() != 1 {
307        return None;
308    }
309    match &args.args[0] {
310        syn::GenericArgument::Type(ty) => Some(ty),
311        _ => None,
312    }
313}
314
315fn regex_definition<'a>(
316    regex: &str,
317    fields: impl IntoIterator<Item = &'a InputField>,
318    span: Span,
319) -> Result<(TokenStream, TokenStream), syn::Error> {
320    struct CountCaptures(pub u32);
321
322    impl regex_ast::Visitor for CountCaptures {
323        type Output = u32;
324        type Err = Infallible;
325
326        fn visit_pre(&mut self, ast: &regex_ast::Ast) -> Result<(), Infallible> {
327            if let regex_ast::Ast::Group(group) = ast {
328                if let regex_ast::GroupKind::CaptureIndex(idx) = group.kind {
329                    self.0 = self.0.max(idx);
330                }
331            }
332            Ok(())
333        }
334
335        fn finish(self) -> Result<u32, Infallible> {
336            Ok(self.0)
337        }
338    }
339
340    let ast = match regex_ast::parse::Parser::new().parse(regex) {
341        Ok(ast) => ast,
342        Err(err) => return Err(syn::Error::new(span, err.to_string())),
343    };
344    let captures = regex_ast::visit(&ast, CountCaptures(0)).unwrap();
345    let fields = fields.into_iter().filter(|field| field.capture()).count();
346    if captures as usize != fields {
347        return Err(syn::Error::new(
348            span,
349            format!("expected {fields} capture groups for struture fields, found {captures}"),
350        ));
351    }
352    let start_regex = format!("^(?:{})", regex);
353    let end_regex = format!("^(?:{})$", regex);
354    Ok((
355        quote! { ::inpt::LazyRegex::new(#start_regex) },
356        quote! { ::inpt::LazyRegex::new(#end_regex) },
357    ))
358}
359
360fn sequential_field(
361    f: &SpannedValue<InputField>,
362    lt: &syn::LifetimeDef,
363    preds: &mut Predicates,
364    prefix: &str,
365    is_last: bool,
366) -> Result<TokenStream, syn::Error> {
367    let ty = &f.ty;
368    let name = format_ident!("{}_{}", prefix, f.index);
369    let trimmed_name = format_ident!("{}_{}_trim", prefix, f.index);
370    let mut trim_init = quote! { let #trimmed_name = trimmed; };
371    let expr = if f.skip == Some(true) {
372        quote! { <#ty as ::std::default::Default>::default() }
373    } else if let Some(split) = &f.split {
374        let mut split_ty = parse_type(split)?;
375        split_ty = parse_quote! { ::inpt::split::#split_ty<&'s str> };
376        trim_init =
377            quote! { let #trimmed_name = <#split_ty as ::inpt::Inpt>::default_trim(trimmed); };
378        let (inner, is_opt) = regex_field(f, lt, preds, prefix)?;
379        if is_opt {
380            return Err(syn::Error::new(
381                split.span(),
382                "splitting can not produce None",
383            ));
384        }
385        quote! { {
386            let step =  <#split_ty as ::inpt::Inpt>::step(rest, #is_last && end, #trimmed_name, guard);
387            rest = step.rest;
388            step.data.map(|split| split.inner).and_then(|full_group| #inner)
389        } }
390    } else if f.from_str() {
391        if !is_last {
392            return Err(syn::Error::new(
393                f.from_str.span(),
394                "without a regex or split, from_str can only be used on the last field",
395            ));
396        }
397        from_str_field(ty, format_ident!("rest"))
398    } else if let Some(item) = &*f.from_iter {
399        let item_ty = parse_type(item)?;
400        trim_init =
401            quote! { let #trimmed_name = <#item_ty as ::inpt::Inpt>::default_trim(trimmed); };
402        if !is_last {
403            return Err(syn::Error::new(
404                f.from_iter.span(),
405                "without a regex, from_iter can only be used on the last field",
406            ));
407        }
408        from_iter_field(
409            ty,
410            &item_ty,
411            item.span(),
412            format_ident!("rest"),
413            trimmed_name.clone(),
414        )?
415    } else {
416        trim_init = quote! { let #trimmed_name = <#ty as ::inpt::Inpt>::default_trim(trimmed); };
417        preds.push(parse_quote_spanned! { f.ty.span() => #ty: ::inpt::Inpt<#lt> });
418        quote! { {
419            let step =  <#ty as ::inpt::Inpt>::step(rest, #is_last && end, #trimmed_name, guard);
420            rest = step.rest;
421            step.data
422        } }
423    };
424    if let Some(source) = &f.trim {
425        let expr = parse_char_class(source)?;
426        trim_init = quote! { let #trimmed_name = #expr; };
427    }
428    Ok(quote_spanned! { f.span() =>
429        #trim_init
430        let after_trim = #trimmed_name.trim(rest, #is_last && end);
431        rest = after_trim;
432        #name = #expr?;
433    })
434}
435
436fn regex_field(
437    f: &SpannedValue<InputField>,
438    lt: &syn::LifetimeDef,
439    preds: &mut Predicates,
440    prefix: &str,
441) -> Result<(TokenStream, bool), syn::Error> {
442    let trimmed_name = format_ident!("{}_{}_trim", prefix, f.index);
443    let mut trim_init = quote! { let #trimmed_name = trimmed; };
444    let (ty, is_opt) = match type_as_option(&f.ty) {
445        Some(ty) => (ty, true),
446        None => (&f.ty, false),
447    };
448    let trimmed_name = format_ident!("{}_{}_trim", prefix, f.index);
449    let expr = if f.from_str() {
450        from_str_field(ty, format_ident!("group"))
451    } else if let Some(item) = &*f.from_iter {
452        let item_ty = parse_type(item)?;
453        trim_init =
454            quote! { let #trimmed_name = <#item_ty as ::inpt::Inpt>::default_trim(trimmed); };
455        from_iter_field(
456            ty,
457            &item_ty,
458            item.span(),
459            format_ident!("group"),
460            trimmed_name.clone(),
461        )?
462    } else {
463        trim_init = quote! { let #trimmed_name = <#ty as ::inpt::Inpt>::default_trim(trimmed); };
464        preds.push(parse_quote_spanned! { ty.span() => #ty: ::inpt::Inpt<#lt> });
465        quote! {
466            <#ty as ::inpt::Inpt>::step(group, true, trimmed, guard).data
467        }
468    };
469
470    if let Some(source) = &f.trim {
471        let expr = parse_char_class(source)?;
472        trim_init = quote! { let #trimmed_name = #expr; };
473    }
474
475    Ok((
476        quote! { {
477            #trim_init
478            let group = #trimmed_name.trim(full_group, true);
479            #expr.within(full_group)
480        } },
481        is_opt,
482    ))
483}
484
485fn field_exprs(
486    fields: &darling::ast::Fields<SpannedValue<InputField>>,
487    lt: &syn::LifetimeDef,
488    preds: &mut Predicates,
489    prefix: &str,
490    regex: Option<&SpannedValue<String>>,
491) -> Result<TokenStream, syn::Error> {
492    let mut this = TokenStream::new();
493
494    for f in fields.iter() {
495        match (regex.is_some(), f.before(), f.after()) {
496            (false, true, _) | (false, _, true) => {
497                return Err(syn::Error::new(
498                    f.span(),
499                    "before and after only apply when matching a regex",
500                ))
501            }
502            (_, true, true) => {
503                return Err(syn::Error::new(
504                    f.span(),
505                    "a field can not be marked before and after",
506                ));
507            }
508            _ => (),
509        }
510    }
511
512    for f in fields.iter().filter(|f| f.before()) {
513        this.extend(sequential_field(f, lt, preds, prefix, false)?);
514    }
515
516    if let Some(regex) = regex {
517        let (start_regex, end_regex) =
518            regex_definition(&**regex, fields.iter().map(|f| &**f), regex.span())?;
519        let regex = &**regex;
520        let no_after = fields.iter().all(|f| !f.after());
521        this.extend(quote! {
522            static RE_SOURCE: &str = #regex;
523            static RE_START: ::inpt::LazyRegex = #start_regex;
524            static RE_END: ::inpt::LazyRegex = #end_regex;
525            rest = trimmed.trim(rest, #no_after && end);
526            let regex = if #no_after && end { &RE_END } else { &RE_START};
527            let regex_text = rest;
528            let capture = regex
529                .captures(regex_text)
530                .ok_or_else(|| ::inpt::InptError::expected_regex(RE_SOURCE, text))?;
531            matched = true;
532            rest = &rest[capture.get(0).unwrap().end()..];
533        });
534
535        let mut group_idx: usize = 1;
536        for f in fields.iter().filter(|f| f.capture()) {
537            if let Some(split) = &f.split {
538                return Err(syn::Error::new(
539                    split.span(),
540                    "don't use split with a capture group",
541                ));
542            }
543            let name = format_ident!("{}_{}", prefix, f.index);
544            let (expr, is_opt) = regex_field(f, lt, preds, prefix)?;
545            if is_opt {
546                this.extend(quote_spanned! { f.span() =>
547                    #name = match capture.get(#group_idx).map(|m| m.as_str()){
548                        None => None,
549                        Some(full_group) => Some(#expr.regex_group(#group_idx)
550                            .within(regex_text)
551                            .regex(RE_SOURCE)?)
552                    };
553                });
554            } else {
555                this.extend(quote_spanned! { f.span() =>
556                    #name = match capture.get(#group_idx).map(|m| m.as_str()) {
557                        None => return Err(::inpt::InptError::expected_regex_group(#group_idx))
558                            .within(regex_text)
559                            .regex(RE_SOURCE),
560                        Some(full_group) => #expr.regex_group(#group_idx)
561                            .within(regex_text)
562                            .regex(RE_SOURCE)?,
563                    };
564                });
565            }
566            group_idx += 1;
567        }
568    }
569
570    let last_index = fields
571        .iter()
572        .rposition(|f| {
573            if regex.is_some() {
574                f.after()
575            } else {
576                !f.skip()
577            }
578        })
579        .unwrap_or(fields.len());
580    for f in fields
581        .iter()
582        .filter(|f| !f.skip() && (regex.is_none() || f.after()))
583    {
584        this.extend(sequential_field(
585            f,
586            lt,
587            preds,
588            prefix,
589            f.index == last_index,
590        )?);
591    }
592
593    this.extend(quote! { matched = true; });
594
595    for f in fields.iter().filter(|f| f.skip()) {
596        let ty = &f.ty;
597        let name = format_ident!("{}_{}", prefix, f.index);
598        this.extend(quote! { #name = <#ty as ::std::default::Default>::default(); })
599    }
600
601    Ok(this)
602}
603
604fn field_setup(
605    fields: &darling::ast::Fields<SpannedValue<InputField>>,
606    prefix: &str,
607) -> (TokenStream, TokenStream) {
608    let mut head = TokenStream::new();
609    let mut inst = TokenStream::new();
610    for f in fields.iter() {
611        let ty = &f.ty;
612        let name = format_ident!("{}_{}", prefix, f.index);
613        head.extend(quote! { let mut #name: #ty; });
614        if let Some(ident) = &f.ident {
615            inst.extend(quote! { #ident: #name, });
616        } else {
617            let idx = Index {
618                index: f.index as _,
619                span: Span::call_site(),
620            };
621            inst.extend(quote! { #idx: #name, });
622        }
623    }
624    (head, inst)
625}
626
627fn impl_inpt_derive(
628    InputOpts {
629        ident,
630        generics,
631        data,
632        regex,
633        bounds,
634        trim,
635        from,
636        try_from,
637    }: InputOpts,
638) -> Result<TokenStream, syn::Error> {
639    let mut generics_with_lt = generics.clone();
640    let lt = match generics.lifetimes().next() {
641        Some(lt) => lt.clone(),
642        None => {
643            let lt: syn::LifetimeDef = parse_quote! { 's };
644            generics_with_lt
645                .params
646                .insert(0, GenericParam::Lifetime(lt.clone()));
647            lt
648        }
649    };
650    let lt = &lt;
651
652    let default_trim = if let Some(source) = &trim {
653        let expr = parse_char_class(source)?;
654        quote! {
655            fn default_trim(_inherited: ::inpt::CharClass) -> ::inpt::CharClass {
656                #expr
657            }
658        }
659    } else if let Some(from) = from.as_ref().or(try_from.as_ref()) {
660        let from = from.parse::<syn::Type>()?;
661        quote! {
662            fn default_trim(inherited: ::inpt::CharClass) -> ::inpt::CharClass {
663                <#from as inpt::Inpt<#lt>>::default_trim(inherited)
664            }
665        }
666    } else {
667        TokenStream::new()
668    };
669
670    if from.is_some() || try_from.is_some() {
671        if let Some(regex) = &regex {
672            return Err(syn::Error::new(
673                regex.span(),
674                "don't use from or try_from with a regex",
675            ));
676        }
677    }
678
679    let preds = &mut generics_with_lt
680        .where_clause
681        .get_or_insert_with(|| parse_quote! { where })
682        .predicates;
683    preds.push(parse_quote! { Self: #lt });
684
685    let mut head = TokenStream::new();
686    let mut body = TokenStream::new();
687
688    match (from, try_from, data) {
689        (Some(from), _, _) => {
690            let from = from.parse::<syn::Type>()?;
691            preds.push(parse_quote! { #from: Inpt<#lt> });
692            body.extend(quote! {
693                let mut res = <#from as ::inpt::Inpt<#lt>>::step(text, end, trimmed, guard)
694                    .map(From::from);
695                rest = res.rest;
696                res.data
697            });
698        }
699        (_, Some(from), _) => {
700            let from = from.parse::<syn::Type>()?;
701            preds.push(parse_quote! { #from: Inpt<#lt> });
702            body.extend(quote! {
703                let mut res = <#from as ::inpt::Inpt<#lt>>::step(text, end, trimmed, guard)
704                    .try_map(core::convert::TryFrom::try_from);
705                rest = res.rest;
706                res.data
707            });
708        }
709        (_, _, Data::Struct(fields)) => {
710            let (struct_head, inst) = field_setup(&fields, "field");
711            head.extend(struct_head);
712            let inner = field_exprs(&fields, &lt, preds, "field", regex.as_ref())?;
713            body.extend(quote! {
714                #inner
715                Ok(Self {
716                    #inst
717                })
718            });
719        }
720        (_, _, Data::Enum(varis)) => {
721            for (idx, vari) in varis.into_iter().enumerate() {
722                let prefix = format!("field_{}", idx);
723                let (vari_head, inst) = field_setup(&vari.fields, &prefix);
724                // head.extend(vari_head);
725                let inner = field_exprs(&vari.fields, &lt, preds, &prefix, vari.regex.as_ref())?;
726                let vari_ident = &vari.ident;
727                body.extend(quote! {
728                    rest = text;
729                    let mut vari = || {
730                        #vari_head
731                        #inner
732                        Ok(Self::#vari_ident {
733                            #inst
734                        })
735                    };
736                    let res = vari();
737                    if matched {
738                        return res;
739                    }
740                });
741            }
742            body.extend(quote! {
743                if let Err(err) = res {
744                    Err(err).expected::<Self>()
745                } else {
746                   Err(::inpt::InptError::expected::<Self>(text))
747                }
748            });
749        }
750    }
751
752    if let Some(bounds) = bounds {
753        struct Bounds {
754            preds: Predicates,
755        }
756
757        impl Parse for Bounds {
758            fn parse(input: ParseStream) -> syn::Result<Self> {
759                Ok(Self {
760                    preds: Punctuated::parse_terminated(input)?,
761                })
762            }
763        }
764
765        preds.clear();
766        *preds = bounds.parse::<Bounds>()?.preds;
767    }
768
769    let (impl_generics, _, where_clause) = generics_with_lt.split_for_impl();
770    let (_, ty_generics, _) = generics.split_for_impl();
771
772    Ok(quote! {
773        impl #impl_generics ::inpt::Inpt<#lt> for #ident #ty_generics #where_clause {
774            fn step(text: &#lt str, end: bool, trimmed: ::inpt::CharClass, guard: &mut ::inpt::RecursionGuard) -> ::inpt::InptStep<#lt, Self> {
775                use ::inpt::ResultExt as _;
776
777                guard.check(text, |guard| {
778                    let mut rest = text;
779                    let mut matched = false;
780                    let mut this = || { #head #body };
781                    ::inpt::InptStep {
782                        data: this().within(text).expected::<Self>(),
783                        rest,
784                    }
785                })
786            }
787
788            #default_trim
789        }
790    })
791}
792
793#[proc_macro_derive(Inpt, attributes(inpt))]
794pub fn inpt_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
795    let input = parse_macro_input!(input as DeriveInput);
796    let mut input = match InputOpts::from_derive_input(&input) {
797        Ok(opts) => opts,
798        Err(err) => return err.write_errors().into(),
799    };
800    match &mut input.data {
801        Data::Enum(e) => {
802            for v in e {
803                for (idx, f) in v.fields.fields.iter_mut().enumerate() {
804                    f.index = idx;
805                }
806            }
807        }
808        Data::Struct(s) => {
809            for (idx, f) in s.fields.iter_mut().enumerate() {
810                f.index = idx;
811            }
812        }
813    }
814    match impl_inpt_derive(input) {
815        Ok(out) => out.into(),
816        Err(err) => err.into_compile_error().into(),
817    }
818}
819
820fn impl_main_attribute(args: TokenStream, mut item: ItemFn) -> Result<TokenStream, syn::Error> {
821    let mut field_defs = Vec::new();
822    let mut field_pats = Vec::new();
823    for (index, arg) in item.sig.inputs.iter_mut().enumerate() {
824        if let syn::FnArg::Typed(arg) = arg {
825            let name = format_ident!("arg{}", index);
826            let ty = &arg.ty;
827            let pat = &arg.pat;
828            let (inpt_attrs, other_attrs): (Vec<_>, Vec<_>) = arg
829                .attrs
830                .drain(..)
831                .partition(|attr| attr.path.is_ident("inpt"));
832            arg.attrs.extend(other_attrs.into_iter());
833            field_defs.push(quote! { #(#inpt_attrs)* #name: #ty });
834            field_pats.push(quote! { #name: #pat });
835        }
836    }
837    let id = &item.sig.ident;
838    let gen = &item.sig.generics;
839    let output = &item.sig.output;
840    let vis = &item.vis;
841    let body = &item.block;
842    let attrs = replace(&mut item.attrs, Default::default());
843
844    Ok(quote! {
845        #(#attrs)*
846        #vis fn #id() #output {
847            #[derive(::inpt::Inpt)]
848            #[inpt(#args)]
849            struct Arguments #gen {
850                #(#field_defs,)*
851            }
852
853            let Arguments { #(#field_pats,)* } = ::inpt::inpt_stdio();
854            #body
855        }
856    })
857}
858
859#[proc_macro_attribute]
860pub fn main(
861    args: proc_macro::TokenStream,
862    item: proc_macro::TokenStream,
863) -> proc_macro::TokenStream {
864    let item = parse_macro_input!(item as ItemFn);
865    match impl_main_attribute(args.into(), item) {
866        Ok(out) => out.into(),
867        Err(err) => err.into_compile_error().into(),
868    }
869}