multiconst_proc_macros 0.2.2

implementation detail of multiconst
Documentation
use super::{AField, FTVis};

use alloc::vec::Vec;

use proc_macro2::TokenStream;

use syn::{
    parse::{ParseBuffer, ParseStream, Parser},
    Attribute, Error, Token,
};

pub(super) struct Config<'a> {
    pub(super) krate: syn::Path,
    pub(super) vis_override: Option<FTVis>,
    pub(super) fields_cfg: Vec<FieldCfg<'a>>,
}

pub(super) struct FieldCfg<'a> {
    pub(super) vis_override: Option<FTVis>,
    pub(super) field: &'a AField<'a>,
}

enum ParseCtx<'a, 'b> {
    Container(&'a mut Config<'b>),
    Field(&'a mut FieldCfg<'b>),
}

pub(super) fn parse_attributes<'a>(
    input: &'a syn::DeriveInput,
    fields: &'a [AField<'a>],
) -> Result<Config<'a>, Error> {
    let mut cfg = Config {
        krate: syn::parse_quote!(multiconst),
        vis_override: None,
        fields_cfg: Vec::new(),
    };

    let mut errs: Result<(), Error> = Ok(());

    parse_attributes_outer(&mut errs, &input.attrs, &mut ParseCtx::Container(&mut cfg));

    for field in fields.iter() {
        let mut field_cfg = FieldCfg {
            vis_override: cfg.vis_override,
            field,
        };

        parse_attributes_outer(&mut errs, field.attrs, &mut ParseCtx::Field(&mut field_cfg));

        cfg.fields_cfg.push(field_cfg);
    }

    errs.map(|_| cfg)
}

fn parse_attributes_outer(
    errs: &mut Result<(), Error>,
    attributes: &[Attribute],
    pctx: &mut ParseCtx<'_, '_>,
) {
    let mut closure = move |input: &'_ ParseBuffer<'_>| parse_attributes_inner(pctx, input);

    for attr in attributes {
        if attr.path.is_ident("field_type") {
            let res = if attr.tokens.is_empty() {
                Parser::parse2(&mut closure, TokenStream::new())
            } else {
                attr.parse_args_with(&mut closure)
            };

            if let Err(e) = res {
                match *errs {
                    ref mut x @ Ok(()) => *x = Err(e),
                    Err(ref mut x) => x.combine(e),
                }
            }
        }
    }
}

fn parse_attributes_inner(
    pctx: &mut ParseCtx<'_, '_>,
    input: ParseStream<'_>,
) -> Result<(), Error> {
    let lookahead = Lookhead {
        input,
        lookahead: input.lookahead1(),
    };

    let opt_crate = match pctx {
        ParseCtx::Container { .. } => lookahead.peek_parse(Token!(crate))?,
        _ => None,
    };

    if let Some(_) = opt_crate {
        input.parse::<Token!(=)>()?;
        let krate = input.parse::<syn::Path>()?;
        match pctx {
            ParseCtx::Container(cfg) => {
                cfg.krate = krate;
            }
            ParseCtx::Field { .. } => {
                unreachable!();
            }
        }
    } else if let Some(_) = lookahead.peek_parse(Token!(pub))? {
        let vo = match pctx {
            ParseCtx::Container(x) => &mut x.vis_override,
            ParseCtx::Field(x) => &mut x.vis_override,
        };
        *vo = Some(FTVis::Pub);
    } else if let Some(_) = lookahead.peek_parse(Token!(priv))? {
        let vo = match pctx {
            ParseCtx::Container(x) => &mut x.vis_override,
            ParseCtx::Field(x) => &mut x.vis_override,
        };
        *vo = Some(FTVis::Priv);
    } else {
        return Err(lookahead.error());
    }

    Ok(())
}

struct Lookhead<'a> {
    input: ParseStream<'a>,
    lookahead: syn::parse::Lookahead1<'a>,
}

impl Lookhead<'_> {
    fn peek_parse<F, X, P>(&self, f: F) -> Result<Option<P>, syn::Error>
    where
        F: FnOnce(X) -> P + syn::parse::Peek,
        P: syn::parse::Parse,
    {
        if self.lookahead.peek(f) {
            self.input.parse::<P>().map(Some)
        } else {
            Ok(None)
        }
    }

    fn error(self) -> syn::Error {
        self.lookahead.error()
    }
}