pareg_core 0.13.0

This library contains the implementation for the pareg library.
Documentation
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{
    Expr, Ident, LitStr, Token, parse::Parser, parse2, punctuated::Punctuated,
};

pub fn proc_parsef(args: TokenStream, part: bool) -> TokenStream {
    let mut input =
        Parser::parse2(Punctuated::<Expr, Token![,]>::parse_terminated, args)
            .unwrap()
            .into_iter();
    let reader = input.next().expect("Missing reader as first argument");
    let pattern: LitStr = parse2(
        input
            .next()
            .expect("Missing literal as second argument.")
            .to_token_stream(),
    )
    .unwrap();
    let span = pattern.span();
    let pattern = pattern.value();
    let mut p = pattern.as_str();

    let mut args = TokenStream::new();

    while !p.is_empty() {
        let Some(pos) = p.find(['{', '}']) else {
            let lit = LitStr::new(p, span);
            args.extend(quote! { pareg::ParseFArg::Str(#lit.into()), });
            p = &p[p.len()..];
            continue;
        };

        if p[pos..].starts_with("{{") || p[pos..].starts_with("}}") {
            let lit = LitStr::new(&p[..=pos], span);
            args.extend(quote! { pareg::ParseFArg::Str(#lit.into()), });
            p = &p[pos + 2..];
            continue;
        }

        if p[pos..].starts_with('}') {
            panic!("Invalid closing bracket.");
        }

        let lit = LitStr::new(&p[..pos], span);
        args.extend(quote! { pareg::ParseFArg::Str(#lit.into()), });
        p = &p[pos + 1..];

        let Some(pos) = p.find("}") else {
            panic!("Missing closing '}}'");
        };

        let inline = &p[..pos];
        let (id, fmt) = if let Some((i, f)) = inline.split_once(':') {
            (i.trim(), f)
        } else {
            (inline.trim(), "")
        };

        if id.is_empty() {
            let arg = input.next();
            if let Some(fid) = fmt.strip_prefix('$') {
                let fid = Ident::new(fid, span);
                args.extend(quote! { pareg::ParseFArg::Arg(#arg, &#fid), });
            } else {
                args.extend(
                    quote! { pareg::ParseFArg::Arg(#arg, &#fmt.into()), },
                );
            }
        } else {
            let id = Ident::new(id, span);
            if let Some(fid) = fmt.strip_prefix('$') {
                let fid = Ident::new(fid, span);
                args.extend(
                    quote! { pareg::ParseFArg::Arg(&mut #id, &#fid), },
                );
            } else {
                args.extend(
                    quote! { pareg::ParseFArg::Arg(&mut #id, &#fmt.into()), },
                );
            }
        }

        p = &p[pos + 1..];
    }

    if part {
        quote! {
            pareg::parsef_part(#reader, [#args])
        }
    } else {
        quote! {
            pareg::parsef(#reader, [#args])
        }
    }
}