procmeta 0.3.5

integration procmeta-core and procmeta-proc
Documentation
#[cfg(test)]
mod tests {
    use proc_macro2::Span;
    use procmeta_core::{expr::FromMetaExpr, parser::MetaParser};
    use syn::{
        parse::ParseStream, parse_quote, Error, ItemStruct, LitInt, LitStr, Meta, MetaNameValue,
        Token,
    };

    #[test]
    fn test_parse() -> anyhow::Result<()> {
        enum Context {
            Note(LitStr),
            NoteNameAge { name: LitStr, age: LitInt },
            NoteName { name: LitStr },
        }

        impl MetaParser for Context {
            fn parse(meta: &Meta) -> syn::Result<Self> {
                let meta_path = meta.path();
                if meta_path.is_ident("note") {
                    let result = meta.require_list();
                    if let Ok(list) = result {
                        let result =
                            list.parse_args_with(|stream: ParseStream| -> syn::Result<Context> {
                                let mut uname_1: Option<LitStr> = None;
                                let mut index = 0;
                                loop {
                                    if stream.is_empty() {
                                        break;
                                    }
                                    index += 1;
                                    if index == 1i32 {
                                        uname_1 = Some(stream.parse()?);
                                    }
                                    if stream.is_empty() {
                                        break;
                                    }
                                    let _comma: Token ! [,] = stream.parse()?;
                                }
                                Ok(Context::Note(uname_1.ok_or_else(|| {
                                    syn::Error::new(stream.span(), "Expected list")
                                })?))
                            });
                        if let Ok(result) = result {
                            return Ok(result);
                        }
                    }
                    let result = meta.require_list();
                    if let Ok(list) = result {
                        let result =
                            list.parse_args_with(|stream: ParseStream| -> syn::Result<Context> {
                                let mut name: Option<LitStr> = None;
                                let mut age: Option<LitInt> = None;
                                loop {
                                    if stream.is_empty() {
                                        break;
                                    }
                                    let nv: MetaNameValue = stream.parse()?;
                                    if nv.path.is_ident("name") {
                                        name = Some(LitStr::try_from_expr(nv.value.clone())?);
                                    }
                                    if nv.path.is_ident("age") {
                                        age = Some(LitInt::try_from_expr(nv.value.clone())?);
                                    }
                                    if stream.is_empty() {
                                        break;
                                    }
                                    let _comma: Token ! [,] = stream.parse()?;
                                }
                                Ok(Context::NoteNameAge {
                                    name: name.ok_or_else(|| {
                                        syn::Error::new(stream.span(), "missing`name`")
                                    })?,
                                    age: age.ok_or_else(|| {
                                        syn::Error::new(stream.span(), "missing`age`")
                                    })?,
                                })
                            });
                        if let Ok(result) = result {
                            return Ok(result);
                        }
                    }
                    let result = meta.require_list();
                    if let Ok(list) = result {
                        let result =
                            list.parse_args_with(|stream: ParseStream| -> syn::Result<Context> {
                                let mut name: Option<LitStr> = None;
                                loop {
                                    if stream.is_empty() {
                                        break;
                                    }
                                    let nv: MetaNameValue = stream.parse()?;
                                    if nv.path.is_ident("name") {
                                        name = Some(LitStr::try_from_expr(nv.value.clone())?);
                                    }
                                    if stream.is_empty() {
                                        break;
                                    }
                                    let _comma: Token ! [,] = stream.parse()?;
                                }
                                Ok(Context::NoteName {
                                    name: name.ok_or_else(|| {
                                        syn::Error::new(stream.span(), "missing`name`")
                                    })?,
                                })
                            });
                        if let Ok(result) = result {
                            return Ok(result);
                        }
                    };
                }
                Err(Error::new(Span::call_site(), "unrecognized writing method"))
            }
        }

        let input: ItemStruct = parse_quote! {
            #[derive(ApiModel)]
            #[note("nihao")]
            #[note(name = "zhangsan", age = 18)]
            #[note(name = "lisi")]
            pub struct MyStruct {
                pub name: String,
            }
        };
        let attrs = input.attrs;
        for attr in attrs {
            let context = Context::parse(&attr.meta);
            if let Ok(context) = context {
                match context {
                    Context::Note(lit) => {
                        println!("{}", lit.value());
                    }
                    Context::NoteNameAge { name, age } => {
                        println!(
                            "name = {}, age = {}",
                            name.value(),
                            age.base10_parse::<i64>().unwrap()
                        );
                    }
                    Context::NoteName { name } => {
                        println!("name = {}", name.value());
                    }
                }
            }
        }
        Ok(())
    }
}