#[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 { 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 { let mut uname_1: Option = 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 { let mut name: Option = None; let mut age: Option = 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 { let mut name: Option = 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::().unwrap() ); } Context::NoteName { name } => { println!("name = {}", name.value()); } } } } Ok(()) } }