structmeta-derive-0.2.0/.cargo_vcs_info.json0000644000000001570000000000100145050ustar { "git": { "sha1": "6247378b5d56d02887461d62f46f59873d097a92" }, "path_in_vcs": "structmeta-derive" }structmeta-derive-0.2.0/Cargo.toml0000644000000021560000000000100125040ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "structmeta-derive" version = "0.2.0" authors = ["frozenlib"] description = "derive macro for structmeta crate." documentation = "https://docs.rs/structmeta/" readme = "README.md" keywords = [ "derive", "parse", "attribute", "syn", "totokens", ] categories = ["development-tools::procedural-macro-helpers"] license = "MIT OR Apache-2.0" repository = "https://github.com/frozenlib/structmeta" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.52" [dependencies.quote] version = "1.0.26" [dependencies.syn] version = "2.0.4" [dev-dependencies.syn] version = "2.0.2" features = ["extra-traits"] structmeta-derive-0.2.0/Cargo.toml.orig000064400000000000000000000012700072674642500162110ustar 00000000000000[package] name = "structmeta-derive" version = "0.2.0" authors = ["frozenlib"] license = "MIT OR Apache-2.0" readme = "../README.md" repository = "https://github.com/frozenlib/structmeta" documentation = "https://docs.rs/structmeta/" keywords = ["derive", "parse", "attribute", "syn", "totokens"] categories = ["development-tools::procedural-macro-helpers"] description = "derive macro for structmeta crate." edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] proc-macro = true [dependencies] proc-macro2 = "1.0.52" syn = "2.0.4" quote = "1.0.26" [dev-dependencies] syn = { version = "2.0.2", features = ["extra-traits"] } structmeta-derive-0.2.0/README.md000064400000000000000000000025470072674642500146110ustar 00000000000000# StructMeta [![Crates.io](https://img.shields.io/crates/v/structmeta.svg)](https://crates.io/crates/structmeta) [![Docs.rs](https://docs.rs/structmeta/badge.svg)](https://docs.rs/structmeta/) [![Actions Status](https://github.com/frozenlib/structmeta/workflows/CI/badge.svg)](https://github.com/frozenlib/structmeta/actions) Parse Rust's attribute arguments by defining a struct. ## Documentation See [`#[derive(StructMeta)]` documentation](https://docs.rs/structmeta/latest/structmeta/derive.StructMeta.html) for details. ## Install Add this to your Cargo.toml: ```toml [dependencies] structmeta = "0.2.0" syn = "2.0.4" ``` ## Example ```rust use structmeta::StructMeta; use syn::{parse_quote, Attribute, LitInt, LitStr}; #[derive(StructMeta, Debug)] struct MyAttr { x: LitInt, y: LitStr, } let attr: Attribute = parse_quote!(#[my_attr(x = 10, y = "abc")]); let attr: MyAttr = attr.parse_args().unwrap(); println!("x = {}, y = {}", attr.x, attr.y.value()); ``` This code outputs: ```txt x = 10, y = abc ``` ## License This project is dual licensed under Apache-2.0/MIT. See the two LICENSE-\* files for details. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. structmeta-derive-0.2.0/src/lib.rs000064400000000000000000000020100072674642500152160ustar 00000000000000//! The documentation for this crate is found in the structmeta crate. extern crate proc_macro; #[macro_use] mod syn_utils; mod parse; mod struct_meta; mod to_tokens; mod to_tokens_attribute; use syn::{parse_macro_input, DeriveInput}; use syn_utils::*; #[proc_macro_derive(ToTokens, attributes(to_tokens))] pub fn derive_to_tokens(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); into_macro_output(to_tokens::derive_to_tokens(input)) } #[proc_macro_derive(Parse, attributes(to_tokens, parse))] pub fn derive_parse(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); into_macro_output(parse::derive_parse(input)) } #[proc_macro_derive(StructMeta, attributes(struct_meta))] pub fn derive_struct_meta(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); into_macro_output(struct_meta::derive_struct_meta(input)) } structmeta-derive-0.2.0/src/parse.rs000064400000000000000000000273600072674642500156010ustar 00000000000000use crate::{syn_utils::*, to_tokens_attribute::*}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use std::unreachable; use syn::{ parse::{Parse, ParseStream}, parse_quote, spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput, Fields, Ident, Result, Token, }; pub fn derive_parse(input: DeriveInput) -> Result { let mut dump = false; for attr in &input.attrs { if attr.path().is_ident("parse") { let attr: ParseAttribute = attr.parse_args()?; dump = dump || attr.dump.is_some(); } } let ts = match &input.data { Data::Struct(data) => code_from_struct(data)?, Data::Enum(data) => code_from_enum(&input.ident, data)?, Data::Union(_) => { bail!(Span::call_site(), "Not supported for union.") } }; let ts = quote! { fn parse(input: ::syn::parse::ParseStream<'_>) -> ::syn::Result { #ts } }; let ts = impl_trait_result(&input, &parse_quote!(::syn::parse::Parse), &[], ts, dump)?; Ok(ts) } fn code_from_struct(data: &DataStruct) -> Result { code_from_fields(quote!(Self), &data.fields, None) } fn code_from_enum(self_ident: &Ident, data: &DataEnum) -> Result { let mut ts = TokenStream::new(); ts.extend(quote!( use syn::ext::*; )); let mut input_is_forked = false; let mut input_is_moved = false; for index in 0..data.variants.len() { let variant = &data.variants[index]; let variant_ident = &variant.ident; let is_last = index == data.variants.len() - 1; let fn_ident = format_ident!("_parse_{}", &variant.ident); let mut peeks = Vec::new(); let fn_expr = code_from_fields( quote!(#self_ident::#variant_ident), &variant.fields, Some(&mut peeks), )?; let fn_def = quote! { #[allow(non_snake_case)] fn #fn_ident(input: ::syn::parse::ParseStream<'_>) -> ::syn::Result<#self_ident> { #fn_expr } }; let code = if peeks.is_empty() { if is_last && !input_is_forked { input_is_moved = true; quote! { #fn_ident(input) } } else { input_is_forked = true; quote! { let fork = input.fork(); if let Ok(value) = #fn_ident(&fork) { ::syn::parse::discouraged::Speculative::advance_to(input, &fork); return Ok(value); } } } } else { let mut preds = Vec::new(); for (index, peek) in peeks.into_iter().enumerate() { preds.push(to_predicate(index, &peek)?); } quote! { if #(#preds )&&* { return #fn_ident(&input); } } }; ts.extend(quote! { #fn_def #code }); } if !input_is_moved { ts.extend(quote! { Err(input.error("parse failed.")) }); } Ok(ts) } fn to_predicate(index: usize, peek: &PeekItem) -> Result { let peek_ident: Ident = match index { 0 => parse_quote!(peek), 1 => parse_quote!(peek2), 2 => parse_quote!(peek3), _ => bail!(peek.span, "more than three `#[parse(peek)]` was specified."), }; let peek_arg = &peek.arg; Ok(quote!(input.#peek_ident(#peek_arg))) } struct Scope { input: Ident, close: Option, } struct PeekItem { span: Span, arg: TokenStream, } fn to_parse_bracket(c: char) -> Ident { match c { '(' => parse_quote!(parenthesized), '[' => parse_quote!(bracketed), '{' => parse_quote!(braced), _ => unreachable!(), } } fn code_from_fields( self_path: TokenStream, fields: &Fields, mut peeks: Option<&mut Vec>, ) -> Result { let mut scopes = vec![Scope { input: parse_quote!(input), close: None, }]; let mut ts = TokenStream::new(); let mut inits = Vec::new(); let mut non_peek_field = None; for (index, field) in fields.iter().enumerate() { let ty = &field.ty; let var_ident = to_var_ident(index, &field.ident); let mut use_parse = true; let mut peek = None; let mut is_any = false; let mut is_terminated = false; let mut is_root = scopes.len() == 1; for attr in &field.attrs { if attr.path().is_ident("to_tokens") { let attr: ToTokensAttribute = attr.parse_args()?; for token in attr.token { for c in token.value().chars() { match c { '(' | '[' | '{' => { use_parse = false; let parse_bracket = if is_macro_delimiter(&field.ty) { quote!(::structmeta::helpers_parse_macro_delimiter) } else { let parse_bracket = to_parse_bracket(c); quote!(::syn::#parse_bracket) }; let input_old = &scopes.last().unwrap().input; let input = format_ident!("input_{}", index); let ty = &field.ty; let code = quote_spanned!(field.span()=> let #input; let #var_ident = #parse_bracket!(#input in #input_old); let #var_ident : #ty = #var_ident; let #input = &#input; ); // We need `let #var_ident : #ty = #var_ident;` to make error messages easier to understand. // Try remove this line and run compile fail tests for details. ts.extend(code); scopes.push(Scope { close: Some(to_close(c)), input, }); } ')' | ']' | '}' => { if scopes.last().unwrap().close != Some(c) { bail!(token.span(), "mismatched closing delimiter `{}`.", c); } scopes.pop(); if scopes.len() == 1 { is_root = true; } } _ => { bail!( token.span(), "expected '(', ')', '[', ']', '{{' or '}}', found `{}`.", c ); } } } } } if attr.path().is_ident("parse") { let attr: ParseAttribute = attr.parse_args()?; peek = peek.or(attr.peek); is_any = is_any || attr.any.is_some(); is_terminated = is_terminated || attr.terminated.is_some(); } } if let Some(peeks) = &mut peeks { if let Some(peek) = peek { let span = peek.span(); if !is_root { bail!(span, "`#[parse(peek)]` cannot be specified with a field enclosed by `[]`, `()` or `{}`."); } if let Some(non_peek_field) = &non_peek_field { bail!( span, "you need to peek all previous tokens. consider specifying `#[parse(peek)]` for field `{}`.", non_peek_field ); } let arg = if is_any { quote!(#ty::peek_any) } else { quote!(#ty) }; peeks.push(PeekItem { span, arg }); } } if is_root && peek.is_none() && non_peek_field.is_none() { non_peek_field = Some(to_display(index, &field.ident)); } if use_parse { let input = &scopes.last().unwrap().input; let expr = match (is_terminated, is_any) { (false, false) => quote!(::syn::parse::Parse::parse(#input)), (false, true) => quote!(<#ty>::parse_any(#input)), (true, false) => { quote!(<#ty>::parse_terminated(#input)) } (true, true) => { quote!(<#ty>::parse_terminated_with(#input, ::syn::ext::IdentExt::parse_any)) } }; let code = quote_spanned!(field.span()=>let #var_ident = #expr?;); ts.extend(code); } if let Some(field_ident) = &field.ident { inits.push(quote!(#field_ident : #var_ident)); } else { inits.push(quote!(#var_ident)); } } let init = match &fields { Fields::Named(_) => quote!({#(#inits,)*}), Fields::Unnamed(_) => quote!((#(#inits,)*)), Fields::Unit => quote!(), }; Ok(quote! { use syn::ext::*; #ts Ok(#self_path #init) }) } fn to_var_ident(index: usize, ident: &Option) -> Ident { if let Some(ident) = ident { format_ident!("_{}", ident) } else { format_ident!("_{}", index) } } fn to_display(index: usize, ident: &Option) -> String { if let Some(ident) = ident { format!("{ident}") } else { format!("{index}") } } struct ParseAttribute { any: Option, peek: Option, terminated: Option, dump: Option, } impl Parse for ParseAttribute { fn parse(input: ParseStream) -> Result { let mut any = None; let mut peek = None; let mut terminated = None; let mut dump = None; let args = input.parse_terminated(ParseAttributeArg::parse, Token![,])?; for arg in args.into_iter() { match arg { ParseAttributeArg::Any(kw_any) => any = any.or(Some(kw_any)), ParseAttributeArg::Peek(kw_peek) => peek = peek.or(Some(kw_peek)), ParseAttributeArg::Terminated(kw_terminated) => { terminated = terminated.or(Some(kw_terminated)) } ParseAttributeArg::Dump(kw_dump) => dump = dump.or(Some(kw_dump)), } } Ok(Self { any, peek, terminated, dump, }) } } mod kw { use syn::custom_keyword; custom_keyword!(any); custom_keyword!(peek); custom_keyword!(terminated); custom_keyword!(dump); } enum ParseAttributeArg { Any(kw::any), Peek(kw::peek), Terminated(kw::terminated), Dump(kw::dump), } impl Parse for ParseAttributeArg { fn parse(input: ParseStream) -> Result { if input.peek(kw::any) { Ok(Self::Any(input.parse()?)) } else if input.peek(kw::peek) { Ok(Self::Peek(input.parse()?)) } else if input.peek(kw::terminated) { Ok(Self::Terminated(input.parse()?)) } else if input.peek(kw::dump) { Ok(Self::Dump(input.parse()?)) } else { Err(input.error("expected `any`, `peek`, `terminated` or `dump`.")) } } } structmeta-derive-0.2.0/src/struct_meta.rs000064400000000000000000000662400072674642500170210ustar 00000000000000use crate::syn_utils::*; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use std::collections::BTreeMap; use syn::{ ext::IdentExt, parse::{Parse, ParseStream}, parse_quote, punctuated::Punctuated, spanned::Spanned, Attribute, Data, DeriveInput, Field, Fields, GenericArgument, Ident, LitStr, PathArguments, Result, Token, Type, }; pub fn derive_struct_meta(input: DeriveInput) -> Result { if let Data::Struct(data) = &input.data { let mut args = ArgsForStruct::default(); for attr in &input.attrs { if attr.path().is_ident("struct_meta") { args.parse_from_attr(attr)?; } } let ps = Params::from_fields(&data.fields, &args)?; let body = ps.build(); impl_trait_result( &input, &parse_quote!(::syn::parse::Parse), &[], quote! { fn parse(input: ::syn::parse::ParseStream<'_>) -> ::syn::Result { #body } }, args.dump, ) } else { let span = input.span(); bail!(span, "`#[derive(StructMeta)]` supports only struct.") } } struct Params<'a> { fields: &'a Fields, unnamed_required: Vec>, unnamed_optional: Vec>, unnamed_variadic: Option>, named: BTreeMap>, rest: Option>, name_filter: NameFilter, } impl<'a> Params<'a> { fn from_fields(fields: &'a Fields, args: &ArgsForStruct) -> Result { let mut unnamed_required = Vec::new(); let mut unnamed_optional = Vec::new(); let mut unnamed_variadic = None; let mut named = BTreeMap::new(); let mut rest = None; for (index, field) in fields.iter().enumerate() { let span = field.span(); match Param::from_field(index, field)? { Param::Unnamed(p) => { if unnamed_variadic.is_some() { bail!( span, "cannot use unnamed parameter after variadic parameter." ) } if p.is_vec { unnamed_variadic = Some(p); } else if p.is_option { unnamed_optional.push(p); } else { if !unnamed_optional.is_empty() { bail!( span, "cannot use non optional parameter after variadic parameter." ) } unnamed_required.push(p); } } Param::Named(p) => { if named.contains_key(&p.name) { bail!(p.name_span, "`{}` is already exists.", p.name); } named.insert(p.name.clone(), p); } Param::Rest(p) => { if rest.is_some() { bail!(span, "cannot use rest parameter twice.") } rest = Some(p); } } } Ok(Self { fields, unnamed_required, unnamed_optional, unnamed_variadic, named, rest, name_filter: args.name_filter(), }) } fn build(&self) -> TokenStream { let mut is_next = false; let mut ts = TokenStream::new(); let mut ctor_args = vec![TokenStream::new(); self.fields.len()]; for (index, p) in self.unnamed_required.iter().enumerate() { if is_next { let msg = format!( "expected least {} arguments but {} argument was supplied", self.unnamed_required.len(), index, ); ts.extend(quote! { if input.is_empty () { return Err(::syn::Error::new(input.span(), #msg)); } input.parse::<::syn::Token![,]>()?; }); } is_next = true; ts.extend(p.info.build_let_parse()); p.build_ctor_arg(false, &mut ctor_args); } let mut arms_unnamed = Vec::new(); for (index, p) in self.unnamed_optional.iter().enumerate() { ts.extend(p.info.build_let_none()); arms_unnamed.push(p.build_arm_parse_value(index)); p.build_ctor_arg(true, &mut ctor_args); } if let Some(p) = &self.unnamed_variadic { ts.extend(p.info.build_let_vec_new()); arms_unnamed.push(p.build_arm_parse_vec_item()); p.build_ctor_arg(false, &mut ctor_args); } else { arms_unnamed.push(quote! { _ => { return Err(input.error("too many unnamed parameter")); } }); } for p in self.named.values() { ts.extend(p.build_let()); p.build_ctor_arg(&mut ctor_args); } let (flag_ps, flag_rest) = self.named_ps(|p| p.is_flag()); let (name_value_ps, name_value_rest) = self.named_ps(|p| p.is_name_value()); let (name_args_ps, name_args_rest) = self.named_ps(|p| p.is_name_args()); let mut arms_named = Vec::new(); for (index, p) in flag_ps.iter().enumerate() { arms_named.push(p.build_arm_parse(index, ArgKind::Flag)); } for (index, p) in name_value_ps.iter().enumerate() { arms_named.push(p.build_arm_parse(index, ArgKind::NameValue)); } for (index, p) in name_args_ps.iter().enumerate() { arms_named.push(p.build_arm_parse(index, ArgKind::NameArgs)); } if let Some(p) = &self.rest { ts.extend(p.build_let()); p.build_ctor_arg(&mut ctor_args); if flag_rest { arms_named.push(p.build_arm_parse(ArgKind::Flag)); } if name_value_rest { arms_named.push(p.build_arm_parse(ArgKind::NameValue)); } if name_args_rest { arms_named.push(p.build_arm_parse(ArgKind::NameArgs)); } } let flag_names = NamedParam::names(&flag_ps); let name_value_names = NamedParam::names(&name_value_ps); let name_args_names = NamedParam::names(&name_args_ps); let no_unnamed = self.unnamed_optional.is_empty() && self.unnamed_variadic.is_none(); let ctor_args = match &self.fields { Fields::Named(_) => { quote!({ #(#ctor_args,)*}) } Fields::Unnamed(_) => { quote!(( #(#ctor_args,)*)) } Fields::Unit => { quote!() } }; let ts_parse_unnamed = if !self.unnamed_optional.is_empty() || self.unnamed_variadic.is_some() { quote! { if named_used { return Err(input.error("cannot use unnamed parameter after named parameter")); } match unnamed_index { #(#arms_unnamed)* } unnamed_index += 1; } } else { quote! { return Err(input.error("cannot use unnamed parameter")); } }; let name_filter = self.name_filter.to_code(); ts.extend(quote! { let mut is_next = #is_next; let mut unnamed_index = 0; let mut named_used = false; while !input.is_empty() { if is_next { input.parse::<::syn::Token![,]>()?; if input.is_empty() { break; } } is_next = true; if let Some((index, span)) = ::structmeta::helpers::try_parse_name(input, &[#(#flag_names,)*], #flag_rest, &[#(#name_value_names,)*], #name_value_rest, &[#(#name_args_names,)*], #name_args_rest, #no_unnamed, #name_filter)? { named_used = true; match index { #(#arms_named)* _ => unreachable!() } } else { #ts_parse_unnamed } } Ok(Self #ctor_args) }); ts } fn named_ps(&self, f: impl Fn(&NamedParamType<'a>) -> bool) -> (Vec<&NamedParam<'a>>, bool) { ( self.named.values().filter(|p| f(&p.ty)).collect(), if let Some(p) = &self.rest { f(&p.ty) } else { false }, ) } } enum Param<'a> { Unnamed(UnnamedParam<'a>), Named(NamedParam<'a>), Rest(RestParam<'a>), } impl<'a> Param<'a> { fn from_field(index: usize, field: &'a Field) -> Result { let mut name = None; let mut name_specified = false; let mut unnamed = false; for attr in &field.attrs { if attr.path().is_ident("struct_meta") { let a = attr.parse_args::()?; if let Some(a_name) = a.name { name = Some((a_name.value(), a_name.span())); name_specified = true; } if a.unnamed { unnamed = true; } } } if name.is_none() { if let Some(ident) = &field.ident { name = Some((ident.unraw().to_string(), ident.span())); } } if unnamed { name = None; } let mut is_map = false; let mut is_option = false; let ty = if let (false, Some(ty)) = (name_specified, get_hash_map_string_element(&field.ty)) { is_map = true; ty } else if let Some(ty) = get_option_element(&field.ty) { is_option = true; ty } else { &field.ty }; let info = ParamInfo::new(index, field, ty); let ty = NamedParamType::from_type(ty, !is_map && !is_option); let this = if is_map { Param::Rest(RestParam { info, ty }) } else if let Some((name, name_span)) = name { Param::Named(NamedParam { info, name, name_span, ty, is_option, }) } else if let NamedParamType::Value { ty, is_vec } = ty { Param::Unnamed(UnnamedParam { info, ty, is_option, is_vec, }) } else { bail!( info.span(), "this field type cannot be used as unnamed parameter." ) }; Ok(this) } } struct ParamInfo<'a> { index: usize, field: &'a Field, ty: &'a Type, temp_ident: Ident, } impl<'a> ParamInfo<'a> { fn new(index: usize, field: &'a Field, ty: &'a Type) -> Self { let temp_ident = format_ident!("_value_{}", index); Self { index, field, ty, temp_ident, } } fn span(&self) -> Span { self.field.span() } fn build_let_none(&self) -> TokenStream { let temp_ident = &self.temp_ident; let ty = &self.ty; quote!(let mut #temp_ident : Option<#ty> = None;) } fn build_let_vec_new(&self) -> TokenStream { let temp_ident = &self.temp_ident; let ty = &self.ty; quote!(let mut #temp_ident = <#ty>::new();) } fn build_let_parse(&self) -> TokenStream { let temp_ident = &self.temp_ident; let ty = &self.field.ty; quote_spanned!(self.span()=> let #temp_ident = input.parse::<#ty>()?;) } } struct RestParam<'a> { info: ParamInfo<'a>, ty: NamedParamType<'a>, } struct NamedParam<'a> { info: ParamInfo<'a>, name: String, name_span: Span, ty: NamedParamType<'a>, is_option: bool, } struct UnnamedParam<'a> { info: ParamInfo<'a>, ty: &'a Type, is_option: bool, is_vec: bool, } impl<'a> NamedParam<'a> { fn build_let(&self) -> TokenStream { let temp_ident = &self.info.temp_ident; quote!(let mut #temp_ident = None;) } fn build_arm_parse(&self, index: usize, kind: ArgKind) -> TokenStream { let temp_ident = &self.info.temp_ident; let msg = format!("parameter `{}` specified more than once", self.name); let span = self.info.field.span(); let expr = self.ty.build_parse_expr(kind, span); let var = kind.to_helper_name_index_variant(); quote_spanned! { span=> ::structmeta::helpers::NameIndex::#var(Ok(#index)) => { if #temp_ident.is_some() { return Err(::syn::Error::new(span, #msg)); } #temp_ident = Some(#expr); } } } fn names<'b>(ps: &[&'b Self]) -> Vec<&'b str> { ps.iter().map(|x| x.name.as_str()).collect() } fn build_ctor_arg(&self, ctor_args: &mut [TokenStream]) { let temp_ident = &self.info.temp_ident; let value = if self.is_option { quote!(#temp_ident) } else { match self.ty { NamedParamType::Flag => quote!(::structmeta::Flag { span: #temp_ident }), NamedParamType::Bool => quote!(#temp_ident.is_some()), NamedParamType::Value { .. } | NamedParamType::NameValue { .. } => { let msg = format!("missing argument `{} = ...`", self.name); quote!(#temp_ident.ok_or_else(|| ::syn::Error::new(::proc_macro2::Span::call_site(), #msg))?) } NamedParamType::NameArgs { .. } => { let msg = format!("missing argument `{}(...)`", self.name); quote!(#temp_ident.ok_or_else(|| ::syn::Error::new(::proc_macro2::Span::call_site(), #msg))?) } } }; build_ctor_arg(&self.info, value, ctor_args) } } impl<'a> RestParam<'a> { fn build_let(&self) -> TokenStream { let temp_ident = &self.info.temp_ident; quote!(let mut #temp_ident = ::std::collections::HashMap::new();) } fn build_arm_parse(&self, kind: ArgKind) -> TokenStream { let temp_ident = &self.info.temp_ident; let span = self.info.field.span(); let expr = self.ty.build_parse_expr(kind, span); let var = kind.to_helper_name_index_variant(); quote_spanned! { span=> ::structmeta::helpers::NameIndex::#var(Err(name)) => { if #temp_ident.insert(name.to_string(), #expr).is_some() { return Err(::syn::Error::new(span, format!("parameter `{}` specified more than once", name))); } } } } fn build_ctor_arg(&self, ctor_args: &mut [TokenStream]) { let temp_ident = &self.info.temp_ident; build_ctor_arg(&self.info, quote!(#temp_ident), ctor_args) } } impl<'a> UnnamedParam<'a> { fn build_arm_parse_value(&self, index: usize) -> TokenStream { let temp_ident = &self.info.temp_ident; let span = self.info.field.span(); let expr = build_parse_expr(self.ty, span); quote_spanned! { span=> #index => { #temp_ident = Some(#expr); } } } fn build_arm_parse_vec_item(&self) -> TokenStream { let temp_ident = &self.info.temp_ident; let span = self.info.field.span(); let expr = build_parse_expr(self.ty, span); quote_spanned! { self.info.field.span()=> _ => { #temp_ident.push(#expr); } } } fn build_ctor_arg(&self, var_is_option: bool, ctor_args: &mut [TokenStream]) { let temp_ident = &self.info.temp_ident; let value = match (var_is_option, self.is_option) { (false, false) | (true, true) => { quote!(#temp_ident) } (true, false) => { quote!(#temp_ident.unwrap()) } _ => { unreachable!() } }; build_ctor_arg(&self.info, value, ctor_args) } } fn build_ctor_arg(info: &ParamInfo, value: TokenStream, ctor_args: &mut [TokenStream]) { let value = if let Some(ident) = &info.field.ident { quote!(#ident : #value) } else { value }; ctor_args[info.index] = value; } mod kw { use syn::custom_keyword; custom_keyword!(dump); custom_keyword!(name_filter); custom_keyword!(name); custom_keyword!(unnamed); } #[derive(Debug, Clone, Copy)] enum NameFilter { None, SnakeCase, } impl NameFilter { fn to_code(self) -> TokenStream { match self { NameFilter::None => quote!(&|_| true), NameFilter::SnakeCase => quote!(&::structmeta::helpers::is_snake_case), } } } #[derive(Debug, Default, Clone, Copy)] struct ArgsForStruct { dump: bool, name_filter: Option, } impl ArgsForStruct { fn parse_from_attr(&mut self, attr: &Attribute) -> Result<()> { let args = attr.parse_args_with(Punctuated::::parse_terminated)?; for arg in args.into_iter() { match arg { ArgForStruct::Dump(_) => self.dump = true, ArgForStruct::NameFilter { span, value } => { if self.name_filter.is_some() { bail!(span, "`name_filter` cannot be specified twice"); } self.name_filter = Some(value); } } } Ok(()) } fn name_filter(&self) -> NameFilter { self.name_filter.unwrap_or(NameFilter::None) } } enum ArgForStruct { Dump(kw::dump), NameFilter { span: Span, value: NameFilter }, } impl Parse for ArgForStruct { fn parse(input: ParseStream) -> Result { if input.peek(kw::dump) { return Ok(Self::Dump(input.parse()?)); } if input.peek(kw::name_filter) { let kw_name_filter: kw::name_filter = input.parse()?; let _eq: Token![=] = input.parse()?; let s: LitStr = input.parse()?; let value = match s.value().as_str() { "snake_case" => NameFilter::SnakeCase, _ => { bail!(s.span(), "expected \"snake_case\"") } }; return Ok(Self::NameFilter { span: kw_name_filter.span, value, }); } Err(input.error("usage : #[struct_meta(dump)]")) } } struct ArgsForField { name: Option, unnamed: bool, } impl Parse for ArgsForField { fn parse(input: ParseStream) -> Result { let mut name = None; let mut unnamed = false; for p in Punctuated::<_, Token![,]>::parse_terminated(input)?.into_iter() { match p { ArgForField::Name { value, .. } => name = Some(value), ArgForField::Unnamed { .. } => unnamed = true, } } Ok(Self { name, unnamed }) } } enum ArgForField { Name { _name_token: kw::name, _eq_token: Token![=], value: LitStr, }, Unnamed { _unnamed_token: kw::unnamed, }, } impl Parse for ArgForField { fn parse(input: ParseStream) -> Result { if input.peek(kw::name) && input.peek2(Token![=]) { let name_token = input.parse()?; let eq_token = input.parse()?; let value = input.parse()?; Ok(Self::Name { _name_token: name_token, _eq_token: eq_token, value, }) } else if input.peek(kw::unnamed) { Ok(Self::Unnamed { _unnamed_token: input.parse()?, }) } else { Err(input.error("expected `name = \"...\"` or `unnamed`.")) } } } enum NamedParamType<'a> { Bool, Flag, Value { ty: &'a Type, is_vec: bool, }, NameValue { ty: &'a Type, }, NameArgs { ty: &'a Type, is_option: bool, is_vec: bool, }, } impl<'a> NamedParamType<'a> { fn from_type(ty: &'a Type, may_flag: bool) -> Self { if may_flag && is_bool(ty) { Self::Bool } else if may_flag && is_flag(ty) { Self::Flag } else if let Some(ty) = get_name_value_element(ty) { Self::NameValue { ty } } else if let Some(mut ty) = get_name_args_element(ty) { let mut is_option = false; if let Some(e) = get_option_element(ty) { is_option = true; ty = e; } let mut is_vec = false; if let Some(e) = get_vec_element(ty) { is_vec = true; ty = e; } Self::NameArgs { ty, is_option, is_vec, } } else { let mut ty = ty; let mut is_vec = false; if let Some(e) = get_vec_element(ty) { is_vec = true; ty = e; } Self::Value { ty, is_vec } } } fn is_flag(&self) -> bool { match self { NamedParamType::Bool | NamedParamType::Flag => true, NamedParamType::Value { .. } | NamedParamType::NameValue { .. } => false, NamedParamType::NameArgs { is_option, .. } => *is_option, } } fn is_name_value(&self) -> bool { match self { NamedParamType::Bool | NamedParamType::Flag => false, NamedParamType::Value { is_vec, .. } => !is_vec, NamedParamType::NameValue { .. } => true, NamedParamType::NameArgs { .. } => false, } } fn is_name_args(&self) -> bool { match self { NamedParamType::Bool | NamedParamType::Flag => false, NamedParamType::Value { is_vec, .. } => *is_vec, NamedParamType::NameValue { .. } => false, NamedParamType::NameArgs { .. } => true, } } fn build_parse_expr(&self, kind: ArgKind, span: Span) -> TokenStream { match self { NamedParamType::Bool | NamedParamType::Flag => quote!(span), NamedParamType::Value { ty, is_vec } => { if *is_vec { build_parse_expr_name_args(ty, *is_vec, span) } else { build_parse_expr(ty, span) } } NamedParamType::NameValue { ty } => { quote!(::structmeta::NameValue { name_span : span, value: input.parse::<#ty>()? }) } NamedParamType::NameArgs { ty, is_option, is_vec, } => { let args = if kind == ArgKind::Flag && *is_option { quote!(None) } else { let args = build_parse_expr_name_args(ty, *is_vec, span); if *is_option { quote!(Some(#args)) } else { args } }; quote!(structmeta::NameArgs { name_span : span, args: #args }) } } } } fn build_parse_expr(ty: &Type, span: Span) -> TokenStream { quote_spanned!(span=> input.parse::<#ty>()?) } fn build_parse_expr_name_args(ty: &Type, is_vec: bool, span: Span) -> TokenStream { let value = if is_vec { quote_spanned!(span=> ::syn::punctuated::Punctuated::<#ty, ::syn::Token![,]>::parse_terminated(&content)?.into_iter().collect()) } else { quote_spanned!(span=> content.parse::<#ty>()?) }; quote! { { let content; ::syn::parenthesized!(content in input); #value } } } #[derive(Eq, PartialEq, Debug, Copy, Clone)] enum ArgKind { Flag, NameValue, NameArgs, } impl ArgKind { fn to_helper_name_index_variant(self) -> TokenStream { match self { Self::Flag => quote!(Flag), Self::NameValue => quote!(NameValue), Self::NameArgs => quote!(NameArgs), } } } fn get_option_element(ty: &Type) -> Option<&Type> { get_element(ty, &[&["std", "option"], &["core", "option"]], "Option") } fn get_vec_element(ty: &Type) -> Option<&Type> { get_element(ty, &[&["std", "vec"], &["alloc", "vec"]], "Vec") } fn get_name_value_element(ty: &Type) -> Option<&Type> { get_element(ty, NS_STRUCTMETA, "NameValue") } fn get_name_args_element(ty: &Type) -> Option<&Type> { get_element(ty, NS_STRUCTMETA, "NameArgs") } fn get_hash_map_element(ty: &Type) -> Option<(&Type, &Type)> { get_element2( ty, &[&["std", "collections"], &["std", "collections", "hash_map"]], "HashMap", ) } fn get_hash_map_string_element(ty: &Type) -> Option<&Type> { let (ty_key, ty_value) = get_hash_map_element(ty)?; if is_string(ty_key) { Some(ty_value) } else { None } } fn is_bool(ty: &Type) -> bool { is_type(ty, NS_PRIMITIVE, "bool") } fn is_flag(ty: &Type) -> bool { is_type(ty, NS_STRUCTMETA, "Flag") } fn is_string(ty: &Type) -> bool { is_type(ty, &[&["std", "string"], &["alloc", "string"]], "String") } fn get_element<'a>(ty: &'a Type, ns: &[&[&str]], name: &str) -> Option<&'a Type> { if let PathArguments::AngleBracketed(args) = get_arguments_of(ty, ns, name)? { if args.args.len() == 1 { if let GenericArgument::Type(ty) = &args.args[0] { return Some(ty); } } } None } fn get_element2<'a>(ty: &'a Type, ns: &[&[&str]], name: &str) -> Option<(&'a Type, &'a Type)> { if let PathArguments::AngleBracketed(args) = get_arguments_of(ty, ns, name)? { if args.args.len() == 2 { if let (GenericArgument::Type(ty0), GenericArgument::Type(ty1)) = (&args.args[0], &args.args[1]) { return Some((ty0, ty1)); } } } None } const NS_STRUCTMETA: &[&[&str]] = &[&["structmeta"]]; const NS_PRIMITIVE: &[&[&str]] = &[&["std", "primitive"], &["core", "primitive"]]; #[cfg(test)] mod tests { use super::*; #[test] fn test_is_option() { assert_eq!( get_option_element(&parse_quote!(Option)), Some(&parse_quote!(u8)) ); } #[test] fn test_is_option_mod() { assert_eq!( get_option_element(&parse_quote!(option::Option)), Some(&parse_quote!(u8)) ); } #[test] fn test_is_option_core() { assert_eq!( get_option_element(&parse_quote!(core::option::Option)), Some(&parse_quote!(u8)) ); } #[test] fn test_is_option_std() { assert_eq!( get_option_element(&parse_quote!(std::option::Option)), Some(&parse_quote!(u8)) ); } } structmeta-derive-0.2.0/src/syn_utils.rs000064400000000000000000000056440072674642500165210ustar 00000000000000use proc_macro2::TokenStream; use quote::quote; use syn::{ punctuated::Punctuated, DeriveInput, Path, PathArguments, PathSegment, Result, Token, Type, WherePredicate, }; macro_rules! bail { ($span:expr, $message:literal $(,)?) => { return std::result::Result::Err(syn::Error::new($span, $message)) }; ($span:expr, $err:expr $(,)?) => { return std::result::Result::Err(syn::Error::new($span, $err)) }; ($span:expr, $fmt:expr, $($arg:tt)*) => { return std::result::Result::Err(syn::Error::new($span, std::format!($fmt, $($arg)*))) }; } pub fn into_macro_output(input: Result) -> proc_macro::TokenStream { match input { Ok(s) => s, Err(e) => e.to_compile_error(), } .into() } pub fn impl_trait( input: &DeriveInput, trait_path: &Path, wheres: &[WherePredicate], contents: TokenStream, ) -> TokenStream { let ty = &input.ident; let (impl_g, ty_g, where_clause) = input.generics.split_for_impl(); let mut wheres = wheres.to_vec(); if let Some(where_clause) = where_clause { wheres.extend(where_clause.predicates.iter().cloned()); } let where_clause = if wheres.is_empty() { quote! {} } else { quote! { where #(#wheres,)*} }; quote! { #[automatically_derived] impl #impl_g #trait_path for #ty #ty_g #where_clause { #contents } } } pub fn impl_trait_result( input: &DeriveInput, trait_path: &Path, wheres: &[WherePredicate], contents: TokenStream, dump: bool, ) -> Result { let ts = impl_trait(input, trait_path, wheres, contents); if dump { panic!("macro result: \n{ts}"); } Ok(ts) } pub fn is_type(ty: &Type, ns: &[&[&str]], name: &str) -> bool { if let Some(a) = get_arguments_of(ty, ns, name) { a.is_empty() } else { false } } pub fn get_arguments_of<'a>(ty: &'a Type, ns: &[&[&str]], name: &str) -> Option<&'a PathArguments> { if let Type::Path(ty) = ty { if ty.qself.is_some() { return None; } let ss = &ty.path.segments; if let Some(last) = ty.path.segments.last() { if last.ident != name { return None; } return if ns.iter().any(|ns| is_match_ns(ss, ns)) { Some(&last.arguments) } else { None }; } } None } pub fn is_match_ns(ss: &Punctuated, ns: &[&str]) -> bool { let mut i_ss = ss.len() - 1; let mut i_ns = ns.len(); while i_ss > 0 && i_ns > 0 { i_ns -= 1; i_ss -= 1; let s = &ss[i_ss]; if s.ident != ns[i_ns] || !s.arguments.is_empty() { return false; } } i_ss == 0 } pub const NS_SYN: &[&[&str]] = &[&["syn"]]; pub fn is_macro_delimiter(ty: &Type) -> bool { is_type(ty, NS_SYN, "MacroDelimiter") } structmeta-derive-0.2.0/src/to_tokens.rs000064400000000000000000000163170072674642500164740ustar 00000000000000use crate::{syn_utils::*, to_tokens_attribute::*}; use proc_macro2::{Delimiter, Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use std::unreachable; use syn::{ parse_quote, spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Result, }; pub fn derive_to_tokens(input: DeriveInput) -> Result { let mut dump = false; for attr in &input.attrs { if attr.path().is_ident("to_tokens") { let attr: ToTokensAttribute = attr.parse_args()?; dump = dump || attr.dump.is_some(); } } let ts = match &input.data { Data::Struct(data) => code_from_struct(data)?, Data::Enum(data) => code_from_enum(data)?, Data::Union(_) => { bail!(Span::call_site(), "Not supported for union.") } }; let ts = quote! { fn to_tokens(&self, tokens: &mut ::proc_macro2::TokenStream) { #ts } }; let ts = impl_trait_result(&input, &parse_quote!(::quote::ToTokens), &[], ts, dump)?; Ok(ts) } fn code_from_struct(data: &DataStruct) -> Result { let p = to_pattern(quote!(Self), &data.fields); let ts = code_from_fields(&data.fields)?; let ts = quote! { let #p = self; #ts }; Ok(ts) } fn code_from_enum(data: &DataEnum) -> Result { let mut arms = Vec::new(); for variant in &data.variants { let ident = &variant.ident; let p = to_pattern(quote!(Self::#ident), &variant.fields); let code = code_from_fields(&variant.fields)?; arms.push(quote! { #p => { #code } }); } Ok(quote! { match self { #(#arms)* } }) } fn to_pattern(self_path: TokenStream, fields: &Fields) -> TokenStream { let mut vars = Vec::new(); match fields { Fields::Unit => self_path, Fields::Unnamed(_) => { for (index, field) in fields.iter().enumerate() { let var_ident = to_var_ident(Some(index), &field.ident); vars.push(quote!(#var_ident)); } quote!( #self_path( #(#vars,)*)) } Fields::Named(_) => { for field in fields.iter() { let field_ident = &field.ident; let var_ident = to_var_ident(None, field_ident); vars.push(quote!(#field_ident : #var_ident)); } quote!( #self_path { #(#vars,)* } ) } } } struct Scope<'a> { ts: TokenStream, surround: Option>, } struct Surround<'a> { ident: Ident, field: &'a Field, delimiter: Delimiter, } fn delimiter_from_open_char(value: char) -> Option { match value { '[' => Some(Delimiter::Bracket), '{' => Some(Delimiter::Brace), '(' => Some(Delimiter::Parenthesis), _ => None, } } fn delimiter_from_close_char(value: char) -> Option { match value { ']' => Some(Delimiter::Bracket), '}' => Some(Delimiter::Brace), ')' => Some(Delimiter::Parenthesis), _ => None, } } impl<'a> Scope<'a> { fn new(surround: Option>) -> Self { Self { ts: TokenStream::new(), surround, } } } fn close_char_of(delimiter: Delimiter) -> char { match delimiter { Delimiter::Bracket => ']', Delimiter::Brace => '}', Delimiter::Parenthesis => ')', _ => unreachable!("unsupported delimiter"), } } impl<'a> Surround<'a> { fn token_type_ident(&self) -> Ident { match self.delimiter { Delimiter::Bracket => parse_quote!(Bracket), Delimiter::Brace => parse_quote!(Brace), Delimiter::Parenthesis => parse_quote!(Paren), _ => unreachable!("unsupported delimiter"), } } } impl<'a> Scope<'a> { fn into_code(self, delimiter: Option, span: Span) -> Result { if let Some(s) = self.surround { if let Some(delimiter) = delimiter { if s.delimiter != delimiter { bail!( span, "mismatched closing delimiter expected `{}`, found `{}`.", close_char_of(s.delimiter), close_char_of(delimiter), ) } } let ident = &s.ident; let ts = self.ts; let ty = &s.field.ty; let span = s.field.span(); let func = if is_macro_delimiter(ty) { quote_spanned!(span=> ::structmeta::helpers::surround_macro_delimiter) } else { let ty = s.token_type_ident(); quote_spanned!(span=> ::syn::token::#ty::surround) }; let code = quote_spanned!(span=> #func(#ident, tokens, |tokens| { #ts });); return Ok(code); } Ok(quote!()) } } fn code_from_fields(fields: &Fields) -> Result { let mut scopes = vec![Scope::new(None)]; for (index, field) in fields.iter().enumerate() { let ident = to_var_ident(Some(index), &field.ident); let mut field_to_tokens = true; for attr in &field.attrs { if attr.path().is_ident("to_tokens") { let attr: ToTokensAttribute = attr.parse_args()?; for token in &attr.token { for c in token.value().chars() { if let Some(delimiter) = delimiter_from_open_char(c) { scopes.push(Scope::new(Some(Surround { ident: ident.clone(), field, delimiter, }))); field_to_tokens = false; } else if let Some(delimiter) = delimiter_from_close_char(c) { let scope = scopes.pop().unwrap(); scopes .last_mut() .unwrap() .ts .extend(scope.into_code(Some(delimiter), token.span())?); } else { bail!( token.span(), "expected '(', ')', '[', ']', '{{' or '}}', found `{}`.", c ); } } } } } if field_to_tokens { let code = quote_spanned!(field.span()=> ::quote::ToTokens::to_tokens(#ident, tokens);); scopes.last_mut().unwrap().ts.extend(code); } } while let Some(scope) = scopes.pop() { if scopes.is_empty() { return Ok(scope.ts); } scopes .last_mut() .unwrap() .ts .extend(scope.into_code(None, Span::call_site())?); } unreachable!() } fn to_var_ident(index: Option, ident: &Option) -> Ident { if let Some(ident) = ident { format_ident!("_{}", ident) } else { format_ident!("_{}", index.unwrap()) } } structmeta-derive-0.2.0/src/to_tokens_attribute.rs000064400000000000000000000030010072674642500205410ustar 00000000000000use proc_macro2::Span; use syn::{parse::Parse, spanned::Spanned, LitStr, Result, Token}; pub struct ToTokensAttribute { pub dump: Option, pub token: Vec, } impl Parse for ToTokensAttribute { fn parse(input: syn::parse::ParseStream) -> Result { let args = input.parse_terminated(ToTokensAttributeArg::parse, Token![,])?; let mut token = Vec::new(); let mut dump = None; for arg in args.into_iter() { match arg { ToTokensAttributeArg::Token(token_value) => { token.push(token_value); } ToTokensAttributeArg::Dump(kw_dump) => { if dump.is_none() { dump = Some(kw_dump.span()); } } } } Ok(Self { dump, token }) } } mod kw { use syn::custom_keyword; custom_keyword!(dump); } enum ToTokensAttributeArg { Token(LitStr), Dump(kw::dump), } impl Parse for ToTokensAttributeArg { fn parse(input: syn::parse::ParseStream) -> Result { if input.peek(LitStr) { Ok(Self::Token(input.parse()?)) } else if input.peek(kw::dump) { Ok(Self::Dump(input.parse()?)) } else { Err(input.error("expected string literal.")) } } } pub fn to_close(c: char) -> char { match c { '(' => ')', '[' => ']', '{' => '}', _ => panic!("not found closing delimiter for {c}"), } }