structmeta-0.2.0/.cargo_vcs_info.json0000644000000001500000000000100132220ustar { "git": { "sha1": "6247378b5d56d02887461d62f46f59873d097a92" }, "path_in_vcs": "structmeta" }structmeta-0.2.0/Cargo.toml0000644000000022520000000000100112250ustar # 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" version = "0.2.0" authors = ["frozenlib"] description = "Parse Rust's attribute arguments by defining a struct." 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" [dependencies.proc-macro2] version = "1.0.52" [dependencies.quote] version = "1.0.26" [dependencies.structmeta-derive] version = "=0.2.0" [dependencies.syn] version = "2.0.4" [dev-dependencies.syn] version = "2.0.4" features = [ "extra-traits", "full", ] structmeta-0.2.0/Cargo.toml.orig000064400000000000000000000013770072674642500147450ustar 00000000000000[package] name = "structmeta" 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 = "Parse Rust's attribute arguments by defining a struct." edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] structmeta-derive = { version = "=0.2.0", path = "../structmeta-derive" } proc-macro2 = "1.0.52" syn = "2.0.4" quote = "1.0.26" [dev-dependencies] syn = { version = "2.0.4", features = ["extra-traits", "full"] } structmeta-0.2.0/README.md000064400000000000000000000025470072674642500133350ustar 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-0.2.0/src/arg_types.rs000064400000000000000000000025310072674642500152010ustar 00000000000000use proc_macro2::Span; /// `name` style attribute argument. /// /// See [`#[derive(StructMeta)]`](macro@crate::StructMeta) documentation for details. #[derive(Clone, Debug, Default)] pub struct Flag { pub span: Option, } impl Flag { pub const NONE: Flag = Flag { span: None }; pub fn value(&self) -> bool { self.span.is_some() } } impl PartialEq for Flag { fn eq(&self, other: &Self) -> bool { self.value() == other.value() } } impl From for Flag { fn from(value: bool) -> Self { Self { span: if value { Some(Span::call_site()) } else { None }, } } } /// `name = value` style attribute argument. /// /// See [`#[derive(StructMeta)]`](macro@crate::StructMeta) documentation for details. #[derive(Copy, Clone, Debug)] pub struct NameValue { pub name_span: Span, pub value: T, } impl PartialEq for NameValue { fn eq(&self, other: &Self) -> bool { self.value == other.value } } /// `name(value)` style attribute argument. /// /// See [`#[derive(StructMeta)]`](macro@crate::StructMeta) documentation for details. #[derive(Copy, Clone, Debug)] pub struct NameArgs { pub name_span: Span, pub args: T, } impl PartialEq for NameArgs { fn eq(&self, other: &Self) -> bool { self.args == other.args } } structmeta-0.2.0/src/easy_syntax.rs000064400000000000000000000041630072674642500155560ustar 00000000000000use proc_macro2::{Ident, Span}; use std::{fmt::Display, str::FromStr}; use syn::{ ext::IdentExt, parse::{Parse, ParseStream}, Error, LitFloat, LitInt, LitStr, Result, }; pub struct Keyword { pub span: Span, pub value: T, } impl Parse for Keyword where T::Err: Display, { fn parse(input: ParseStream) -> Result { let name = Ident::parse_any(input)?; let span = name.span(); match name.to_string().parse() { Ok(value) => Ok(Keyword { span, value }), Err(e) => Err(Error::new(span, e)), } } } #[derive(Copy, Clone, Debug)] pub struct LitValue { span: Span, value: T, } impl PartialEq for LitValue { fn eq(&self, other: &Self) -> bool { self.value == other.value } } impl PartialEq for LitValue { fn eq(&self, other: &T) -> bool { self.value.eq(other) } } macro_rules! impl_parse_lit_value_int { ($($ty:ty),*) => { $( impl Parse for LitValue<$ty> { fn parse(input: ParseStream) -> Result { let lit = input.parse::()?; Ok(Self { span: lit.span(), value: lit.base10_parse()?, }) } } )* }; } impl_parse_lit_value_int!(u8, u16, u32, u64, u128); impl_parse_lit_value_int!(i8, i16, i32, i64, i128); impl Parse for LitValue { fn parse(input: ParseStream) -> Result { let lit = input.parse::()?; Ok(LitValue { span: lit.span(), value: lit.value(), }) } } impl Parse for LitValue { fn parse(input: ParseStream) -> Result { let lit = input.parse::()?; Ok(LitValue { span: lit.span(), value: lit.base10_parse()?, }) } } impl Parse for LitValue { fn parse(input: ParseStream) -> Result { let lit = input.parse::()?; Ok(LitValue { span: lit.span(), value: lit.base10_parse()?, }) } } structmeta-0.2.0/src/helpers.rs000064400000000000000000000213540072674642500146520ustar 00000000000000use proc_macro2::{Ident, Spacing, Span, TokenStream}; use syn::{ braced, bracketed, ext::IdentExt, parenthesized, parse::{discouraged::Speculative, ParseBuffer, ParseStream}, token::{self}, MacroDelimiter, Result, Token, }; pub enum NameIndex { Flag(std::result::Result), NameValue(std::result::Result), NameArgs(std::result::Result), } #[allow(clippy::too_many_arguments)] pub fn try_parse_name( input: ParseStream, flag_names: &[&str], flag_rest: bool, name_value_names: &[&str], name_value_rest: bool, name_args_names: &[&str], name_args_rest: bool, no_unnamed: bool, name_filter: &dyn Fn(&str) -> bool, ) -> Result> { let may_flag = !flag_names.is_empty() || flag_rest; let may_name_value = !name_value_names.is_empty() || name_value_rest; let may_name_args = !name_args_names.is_empty() || name_args_rest; let fork = input.fork(); if let Ok(ident) = Ident::parse_any(&fork) { if name_filter(&ident.to_string()) { let span = ident.span(); let mut kind = None; if (no_unnamed || may_flag) && (fork.peek(Token![,]) || fork.is_empty()) { if let Some(i) = name_index_of(flag_names, flag_rest, &ident) { input.advance_to(&fork); return Ok(Some((NameIndex::Flag(i), span))); } kind = Some(ArgKind::Flag); } else if (no_unnamed || may_name_value) && peek_eq_op(&fork) { if let Some(i) = name_index_of(name_value_names, name_value_rest, &ident) { fork.parse::()?; input.advance_to(&fork); return Ok(Some((NameIndex::NameValue(i), span))); } kind = Some(ArgKind::NameValue); } else if (no_unnamed || may_name_args) && fork.peek(token::Paren) { if let Some(i) = name_index_of(name_args_names, name_args_rest, &ident) { input.advance_to(&fork); return Ok(Some((NameIndex::NameArgs(i), span))); } kind = Some(ArgKind::NameArgs); }; if kind.is_some() || no_unnamed { let mut expected = Vec::new(); if let Some(name) = name_of(flag_names, flag_rest, &ident) { expected.push(format!("flag `{name}`")); } if let Some(name) = name_of(name_value_names, name_value_rest, &ident) { expected.push(format!("`{name} = ...`")); } if let Some(name) = name_of(name_args_names, name_args_rest, &ident) { expected.push(format!("`{name}(...)`")); } if !expected.is_empty() { return Err(input.error(msg( &expected, kind.map(|kind| Arg { kind, ident: &ident, }), ))); } let help = if let Some(similar_name) = find_similar_name(&[flag_names, name_value_names, name_args_names], &ident) { format!(" (help: a parameter with a similar name exists: `{similar_name}`)",) } else { "".into() }; return Err(input.error(format!( "cannot find parameter `{ident}` in this scope{help}" ))); } } } if no_unnamed { let message = if may_flag || may_name_value || may_name_args { "too many unnamed arguments." } else { "too many arguments." }; return Err(input.error(message)); } Ok(None) } fn peek_eq_op(input: ParseStream) -> bool { if let Some((p, _)) = input.cursor().punct() { p.as_char() == '=' && p.spacing() == Spacing::Alone } else { false } } fn name_index_of( names: &[&str], rest: bool, ident: &Ident, ) -> Option> { if let Some(index) = find(names, ident) { Some(Ok(index)) } else if rest { Some(Err(ident.clone())) } else { None } } fn name_of(names: &[&str], rest: bool, ident: &Ident) -> Option { if rest { Some(ident.to_string()) } else { find(names, ident).map(|i| names[i].to_string()) } } fn find(names: &[&str], ident: &Ident) -> Option { names.iter().position(|name| ident == name) } fn msg(expected: &[String], found: Option) -> String { if expected.is_empty() { return "unexpected token.".into(); } let mut m = String::new(); m.push_str("expected "); for i in 0..expected.len() { if i != 0 { let sep = if i == expected.len() - 1 { " or " } else { ", " }; m.push_str(sep); } m.push_str(&expected[i]); } if let Some(arg) = found { m.push_str(", found "); m.push_str(&match arg.kind { ArgKind::Flag => format!("`{}`", arg.ident), ArgKind::NameValue => format!("`{} = ...`", arg.ident), ArgKind::NameArgs => format!("`{}`(...)", arg.ident), }); } m } fn find_similar_name<'a>(names: &[&[&'a str]], ident: &Ident) -> Option<&'a str> { let c0: Vec<_> = ident.to_string().chars().collect(); let mut c1 = Vec::new(); let mut r = None; let mut r_d = usize::max_value(); for &names in names { for &name in names { c1.clear(); c1.extend(name.chars()); if let Some(d) = distance(&c0, &c1) { if d < r_d { r_d = d; r = Some(name); } if d == r_d && Some(name) != r { return None; } } } } r } fn distance(s0: &[char], s1: &[char]) -> Option { if s0.len() > s1.len() { return distance(s1, s0); } if s0.len() + 1 < s1.len() { return None; } let mut start = 0; while start < s0.len() && start < s1.len() && s0[start] == s1[start] { start += 1; } let mut end = 0; while start + end < s0.len() && start + end < s1.len() && s0[s0.len() - end - 1] == s1[s1.len() - end - 1] { end += 1; } if s0.len() == s1.len() { if start + end == s0.len() { return Some(0); } if start + end + 1 == s0.len() { return Some(1); } if start + end + 2 == s0.len() && s0[start] == s1[start + 1] && s0[start + 1] == s1[start] { return Some(2); } } else if start + end == s0.len() { return Some(1); } None } pub fn is_snake_case(s: &str) -> bool { s.chars() .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_') } pub fn surround_macro_delimiter(this: &MacroDelimiter, tokens: &mut TokenStream, f: F) where F: FnOnce(&mut TokenStream), { match this { MacroDelimiter::Paren(p) => p.surround(tokens, f), MacroDelimiter::Bracket(b) => b.surround(tokens, f), MacroDelimiter::Brace(b) => b.surround(tokens, f), } } pub fn parse_macro_delimiter<'a>( input: &ParseBuffer<'a>, ) -> Result<(MacroDelimiter, ParseBuffer<'a>)> { let content; let token = if input.peek(token::Paren) { MacroDelimiter::Paren(parenthesized!(content in input)) } else if input.peek(token::Bracket) { MacroDelimiter::Bracket(bracketed!(content in input)) } else if input.peek(token::Brace) { MacroDelimiter::Brace(braced!(content in input)) } else { return Err(input.error("expected `(`, `[` or `{`")); }; Ok((token, content)) } #[doc(hidden)] #[macro_export] macro_rules! helpers_parse_macro_delimiter { ($content:ident in $input:ident) => { match $crate::helpers::parse_macro_delimiter($input) { Ok((token, content)) => { $content = content; token } Err(e) => return Err(e), } }; } #[test] fn test_is_near() { fn check(s0: &str, s1: &str, e: Option) { let c0: Vec<_> = s0.chars().collect(); let c1: Vec<_> = s1.chars().collect(); assert_eq!(distance(&c0, &c1), e, "{s0} , {s1}") } check("a", "a", Some(0)); check("a", "b", Some(1)); check("a", "ab", Some(1)); check("ab", "a", Some(1)); check("a", "aa", Some(1)); check("ab", "ba", Some(2)); } enum ArgKind { Flag, NameValue, NameArgs, } struct Arg<'a> { kind: ArgKind, ident: &'a Ident, } structmeta-0.2.0/src/lib.rs000064400000000000000000001023220072674642500137510ustar 00000000000000/*! Parse Rust's attribute arguments by defining a struct. See [`#[derive(StructMeta)]`](macro@StructMeta) documentation for details. */ #[doc(hidden)] pub mod helpers; // mod easy_syntax; // pub use easy_syntax::*; mod arg_types; pub use arg_types::*; // #[include_doc("../../doc/to_tokens.md", start)] /// Derive [`quote::ToTokens`] for syntax tree node. /// /// - [Example](#example) /// - [Helper attributes](#helper-attributes) /// - [`#[to_tokens("[", "]", "(", ")", "{", "}"]`](#to_tokens-----) /// - [`#[to_tokens(dump)]`](#to_tokensdump) /// /// # Example /// /// `#[derive(ToTokens)]` generates an implementation of `ToTokens` that calls [`ToTokens::to_tokens`](quote::ToTokens::to_tokens) for each field. /// /// ```rust /// use syn::LitInt; /// /// #[derive(structmeta::ToTokens)] /// struct Example(LitInt, LitInt); /// ``` /// /// Code like this will be generated: /// /// ```rust /// # use syn::LitInt; /// # struct Example(LitInt, LitInt); /// impl quote::ToTokens for Example { /// fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { /// self.0.to_tokens(tokens); /// self.1.to_tokens(tokens); /// } /// } /// ``` /// /// `#[derive(ToTokens)]` can also be specified for enum. /// /// ```rust /// use syn::{LitInt, LitStr}; /// /// #[derive(structmeta::ToTokens)] /// enum Example { /// A(LitInt), /// B(LitStr), /// } /// ``` /// /// Code like this will be generated: /// /// ```rust /// # use syn::{LitInt, LitStr}; /// # enum Example { /// # A(LitInt), /// # B(LitStr), /// # } /// impl quote::ToTokens for Example { /// fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { /// match self { /// Self::A(l) => l.to_tokens(tokens), /// Self::B(l) => l.to_tokens(tokens), /// } /// } /// } /// ``` /// /// # Helper attributes /// /// | | struct | enum | varaint | field | /// | ----------------------------------------------------------- | ------ | ---- | ------- | ----- | /// | [`#[to_tokens("[")]`, `#[to_tokens("]")]`](#to_tokens-----) | | | | ✔ | /// | [`#[to_tokens("(")]`, `#[to_tokens(")")]`](#to_tokens-----) | | | | ✔ | /// | [`#[to_tokens("{")]`, `#[to_tokens("}")]`](#to_tokens-----) | | | | ✔ | /// | [`#[to_tokens(dump)]`](#to_tokensdump) | ✔ | ✔ | | | /// /// ## `#[to_tokens("[", "]", "(", ")", "{", "}"]` /// /// By specifying `#[to_tokens("[")]` or `#[to_tokens("(")]` or `#[to_tokens("[")]` , subsequent tokens will be enclosed in `[]` or `()` or `{}`. /// /// By default, all subsequent fields are enclosed. /// To restrict the enclosing fields, specify `#[to_tokens("]")]` or `#[to_tokens(")")]` or `#[to_tokens("}")]` for the field after the end of the enclosure. /// /// ```rust /// use syn::{token, LitInt}; /// /// #[derive(structmeta::ToTokens)] /// struct Example { /// x: LitInt, /// #[to_tokens("[")] /// bracket_token: token::Bracket, /// y: LitInt, /// #[to_tokens("]")] /// z: LitInt, /// } /// ``` /// /// Code like this will be generated: /// /// ```rust /// # use syn::{token, LitInt}; /// # /// # struct Example { /// # x: LitInt, /// # bracket_token: token::Bracket, /// # y: LitInt, /// # z: LitInt, /// # } /// impl quote::ToTokens for Example { /// fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { /// self.x.to_tokens(tokens); /// token::Bracket::surround(&self.bracket_token, tokens, |tokens| { /// self.y.to_tokens(tokens); /// }); /// self.z.to_tokens(tokens); /// } /// } /// ``` /// /// If the field type is `Braket` or `Paren` or `Brace`, the symbol corresponding to the token type must be specified. /// /// If the field type is `MacroDelimiter`, any symbol can be used and there is no difference in behavior. (Three types of parentheses are available, no matter which symbol is specified.) /// /// | field type | start | end | /// | ------------------------------ | ----------------------- | ----------------------- | /// | [`struct@syn::token::Bracket`] | `"["` | `"]"` | /// | [`struct@syn::token::Paren`] | `"("` | `")"` | /// | [`struct@syn::token::Brace`] | `"{"` | `"}"` | /// | [`enum@syn::MacroDelimiter`] | `"["` or `"("` or `"{"` | `"]"` or `")"` or `"}"` | /// /// ## `#[to_tokens(dump)]` /// /// Causes a compile error and outputs the code generated by `#[derive(ToTokens)]` as an error message. // #[include_doc("../../doc/to_tokens.md", end)] pub use structmeta_derive::ToTokens; // #[include_doc("../../doc/parse.md", start)] /// Derive [`syn::parse::Parse`] for syntax tree node. /// /// - [Example](#example) /// - [Helper attributes](#helper-attributes) /// - [`#[to_tokens("[", "]", "(", ")", "{", "}")]`](#to_tokens-----) /// - [`#[parse(peek)]`](#parsepeek) /// - [`#[parse(any)]`](#parseany) /// - [`#[parse(terminated)]`](#parseterminated) /// - [`#[parse(dump)]`](#parsedump) /// /// # Example /// /// `#[derive(Parse)]` generates an implementation of `Parse` that calls [`Parse::parse`](syn::parse::Parse::parse) for each field. /// /// ```rust /// use syn::{LitInt, LitStr}; /// /// #[derive(structmeta::Parse)] /// struct Example(LitInt, LitStr); /// ``` /// /// Code like this will be generated: /// /// ```rust /// # use syn::{LitInt, LitStr}; /// # struct Example(LitInt, LitStr); /// impl syn::parse::Parse for Example { /// fn parse(input: syn::parse::ParseStream) -> syn::Result { /// let _0 = input.parse()?; /// let _1 = input.parse()?; /// return Ok(Example(_0, _1)); /// } /// } /// ``` /// /// `#[derive(Parse)]` can also be specified for enum. /// /// ```rust /// use syn::{LitInt, LitStr}; /// /// #[derive(structmeta::Parse)] /// enum Example { /// A(LitInt, LitInt), /// B(LitStr), /// } /// ``` /// /// Code like this will be generated: /// /// ```rust /// # use syn::{LitInt, LitStr}; /// # enum Example { /// # A(LitInt, LitInt), /// # B(LitStr), /// # } /// use syn::parse::discouraged::Speculative; /// impl syn::parse::Parse for Example { /// fn parse(input: syn::parse::ParseStream) -> syn::Result { /// let fork = input.fork(); /// if let Ok(value) = fork.call(|input| Ok(Example::A(input.parse()?, input.parse()?))) { /// input.advance_to(&fork); /// return Ok(value); /// } /// /// let fork = input.fork(); /// if let Ok(value) = fork.call(|input| Ok(Example::B(input.parse()?))) { /// input.advance_to(&fork); /// return Ok(value); /// } /// /// Err(input.error("parse failed.")) /// } /// } /// ``` /// /// # Helper attributes /// /// | | struct | enum | varaint | field | /// | --------------------------------------------------------------- | ------ | ---- | ------- | ----- | /// | [`#[to_tokens("[", "]", "(", ")", "{", "}")]`](#to_tokens-----) | | | | ✔ | /// | [`#[parse(peek)]`](#parsepeek) | | | | ✔ | /// | [`#[parse(any)]`](#parseany) | | | | ✔ | /// | [`#[parse(terminated)]`](#parseterminated) | | | | ✔ | /// | [`#[parse(dump)]`](#parsedump) | ✔ | ✔ | | | /// /// ## `#[to_tokens("[", "]", "(", ")", "{", "}")]` /// /// By specifying `#[to_tokens("[")]` or `#[to_tokens("(")]` or `#[to_tokens("[")]` , subsequent tokens will be enclosed in `[]` or `()` or `{}`. /// /// By default, all subsequent fields are enclosed. /// To restrict the enclosing fields, specify `#[to_tokens("]")]` or `#[to_tokens(")")]` or `#[to_tokens("}")]` for the field after the end of the enclosure. /// /// ```rust /// use syn::{token, LitInt}; /// /// #[derive(structmeta::Parse)] /// struct Example { /// x: LitInt, /// #[to_tokens("[")] /// bracket_token: token::Bracket, /// y: LitInt, /// #[to_tokens("]")] /// z: LitInt, /// } /// ``` /// /// Code like this will be generated: /// /// ```rust /// # use syn::{token, LitInt}; /// # /// # struct Example { /// # x: LitInt, /// # bracket_token: token::Bracket, /// # y: LitInt, /// # z: LitInt, /// # } /// impl syn::parse::Parse for Example { /// fn parse(input: syn::parse::ParseStream) -> syn::Result { /// let x = input.parse()?; /// let content; /// let bracket_token = syn::bracketed!(content in input); /// let y = content.parse()?; /// let z = input.parse()?; /// Ok(Self { /// x, /// bracket_token, /// y, /// z, /// }) /// } /// } /// ``` /// /// If the field type is `Braket` or `Paren` or `Brace`, the symbol corresponding to the token type must be specified. /// /// If the field type is `MacroDelimiter`, any symbol can be used and there is no difference in behavior. (Three types of parentheses are available, no matter which symbol is specified.) /// /// | field type | start | end | /// | ------------------------------ | ----------------------- | ----------------------- | /// | [`struct@syn::token::Bracket`] | `"["` | `"]"` | /// | [`struct@syn::token::Paren`] | `"("` | `")"` | /// | [`struct@syn::token::Brace`] | `"{"` | `"}"` | /// | [`enum@syn::MacroDelimiter`] | `"["` or `"("` or `"{"` | `"]"` or `")"` or `"}"` | /// /// ## `#[parse(peek)]` /// /// When parsing an enum, it will peek the field with this attribute set, /// and if successful, will parse the variant containing the field. /// If the peek succeeds, the subsequent variant will not be parsed even if the parse failed. /// /// Variant where `#[parse(peek)]` is not specified will fork input and parse. /// /// If the peek fails or the parsing of the forked input fails, the subsequent variant will be parsed. /// /// ```rust /// use syn::{LitInt, LitStr}; /// #[derive(structmeta::Parse)] /// enum Example { /// A(#[parse(peek)] LitInt, LitInt), /// B(LitStr), /// } /// ``` /// /// Code like this will be generated: /// /// ```rust /// # use syn::{LitInt, LitStr}; /// # enum Example { /// # A(LitInt, LitInt), /// # B(LitStr), /// # } /// impl syn::parse::Parse for Example { /// fn parse(input: syn::parse::ParseStream) -> syn::Result { /// if input.peek(LitInt) { /// let a_0 = input.parse()?; /// let a_1 = input.parse()?; /// return Ok(Example::A(a_0, a_1)); /// } /// let b_0 = input.parse()?; /// Ok(Example::B(b_0)) /// } /// } /// ``` /// /// `#[parse(peek)]` can be specified on the first three `TokenTree` for each variant. /// /// ```rust /// use syn::{LitInt, LitStr}; /// #[derive(structmeta::Parse)] /// enum Example { /// A(#[parse(peek)] LitInt, #[parse(peek)]LitInt, #[parse(peek)]LitInt), /// B(#[parse(peek)] LitStr), /// } /// ``` /// /// Since the tokens enclosed by the delimiter is treated as a single token tree, you can also specify `#[parse(peek)]` to the field with `#[to_tokens("]")]`, `#[to_tokens("}")]`, `#[to_tokens(")")]`. /// /// ```rust /// use syn::{token, LitInt, LitStr}; /// #[derive(structmeta::Parse)] /// enum Example { /// A { /// #[parse(peek)] /// #[to_tokens("{")] /// a: token::Brace, /// b: LitInt, /// c: LitInt, /// #[to_tokens("}")] /// #[parse(peek)] /// d: LitInt, /// }, /// } /// ``` /// /// To use `#[parse(peek)]` for a field that type is `Ident`, use `syn::Ident` insted of `proc_macro2::Ident`. /// /// ```compile_fail /// #[derive(structmeta::Parse)] /// enum ExampleNg { /// A(#[parse(peek)] proc_macro2::Ident), /// } /// ``` /// /// ```rust /// #[derive(structmeta::Parse)] /// enum ExampleOk { /// A(#[parse(peek)] syn::Ident), /// } /// ``` /// /// ## `#[parse(any)]` /// /// When parsing `Ident`, allow values that cannot be used as identifiers, such as keywords. /// /// In other words, `Ident::parse_any` and `Ident::peek_any` was generated instead of `Ident::parse` and `Ident::peek`. /// /// ```rust /// use quote::quote; /// use structmeta::Parse; /// use syn::{parse2, Ident}; /// /// #[derive(Parse)] /// struct WithAny(#[parse(any)] Ident); /// /// #[derive(Parse)] /// struct WithoutAny(Ident); /// /// assert_eq!(parse2::(quote!(self)).is_ok(), true); /// assert_eq!(parse2::(quote!(self)).is_ok(), false); /// ``` /// /// ## `#[parse(terminated)]` /// /// Use [`Punctuated::parse_terminated`](syn::punctuated::Punctuated::parse_terminated) to parse. /// /// ```rust /// use quote::quote; /// use structmeta::Parse; /// use syn::{parse2, punctuated::Punctuated, Ident, Token}; /// #[derive(Parse)] /// struct Example(#[parse(terminated)] Punctuated); /// assert_eq!(parse2::(quote!(a, b, c)).is_ok(), true); /// ``` /// /// `terminated` can also be used with `any`. /// /// ```rust /// use quote::quote; /// use structmeta::Parse; /// use syn::{parse2, punctuated::Punctuated, Ident, Token}; /// /// #[derive(Parse)] /// struct WithAny(#[parse(terminated, any)] Punctuated); /// /// #[derive(Parse)] /// struct WithoutAny(#[parse(terminated)] Punctuated); /// /// assert_eq!(parse2::(quote!(self, self)).is_ok(), true); /// assert_eq!(parse2::(quote!(self, self)).is_ok(), false); /// ``` /// /// ## `#[parse(dump)]` /// /// Causes a compile error and outputs the code generated by `#[derive(Parse)]` as an error message. // #[include_doc("../../doc/parse.md", end)] pub use structmeta_derive::Parse; // #[include_doc("../../doc/struct_meta.md", start)] /// Derive [`syn::parse::Parse`] for parsing attribute arguments. /// /// - [Example](#example) /// - [Named parameter](#named-parameter) /// - [Supported field types for named parameter](#supported-field-types-for-named-parameter) /// - [Flag style](#flag-style) /// - [NameValue style](#namevalue-style) /// - [NameArgs style](#nameargs-style) /// - [NameArgs or Flag style](#nameargs-or-flag-style) /// - [NameArgList style](#namearglist-style) /// - [NameArgList or Flag style](#namearglist-or-flag-style) /// - [Optional named parameter](#optional-named-parameter) /// - [Rest named parameter](#rest-named-parameter) /// - [Unnamed parameter](#unnamed-parameter) /// - [Required unnamed parameter](#required-unnamed-parameter) /// - [Optional unnamed parameter](#optional-unnamed-parameter) /// - [Variadic unnamed parameter](#variadic-unnamed-parameter) /// - [Parameter order](#parameter-order) /// - [Helper attribute `#[struct_meta(...)]`](#helper-attribute-struct_meta) /// - [Uses with `#[proc_macro_derive]`](#uses-with-proc_macro_derive) /// - [Uses with `#[proc_macro_attribute]`](#uses-with-proc_macro_attribute) /// - [Parsing ambiguous arguments](#parsing-ambiguous-arguments) /// - [`#[struct_meta(name_filter = "...")]`](#struct_metaname_filter--) /// /// # Example /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute}; /// use syn::{LitInt, LitStr}; /// /// #[derive(StructMeta)] /// struct Args { /// #[struct_meta(unnamed)] /// a: LitStr, /// b: LitInt, /// c: Option, /// } /// /// let attr: Attribute = parse_quote!(#[attr("xyz", b = 10)]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a.value(), "xyz"); /// assert_eq!(args.b.base10_parse::()?, 10); /// assert!(args.c.is_none()); /// # syn::Result::Ok(()) /// ``` /// /// # Named parameter /// /// The following field will be "Named parameter". /// /// - field in record struct. /// - field with `#[struct_meta(name = "...")]` in tuple struct. /// - However, fields that meet the following conditions are excluded /// - field with `#[struct_meta(unnamed)]` /// - field with type `HashMap` /// /// "Named parameter" is a parameter that specifies with a name, such as `#[attr(flag, x = 10, y(1, 2, 3))]`. /// /// ## Supported field types for named parameter /// /// "Named parameter" has the following four styles, and the style is determined by the type of the field. /// /// - Flag style : `name` /// - NameValue style : `name = value` /// - NameArgs style : `name(args)` /// - NameArgList style : `name(arg, arg, ...)` /// /// | field type | field type (with span) | style | example | /// | ---------- | ---------------------------- | ------------------------------------------------- | ------------------------------- | /// | `bool` | [`Flag`] | [Flag](#flag-style) | `name` | /// | `T` | [`NameValue`] | [NameValue](#namevalue-style) | `name = value` | /// | | [`NameArgs`] | [NameArgs](#nameargs-or-flag-style) | `name(args)` | /// | | [`NameArgs>`] | [NameArgs or Flag](#nameargs-or-flag-style) | `name(args)` or `name` | /// | `Vec` | [`NameArgs>`] | [NameArgList](#namearglist-style) | `name(arg, arg, ...)` | /// | | [`NameArgs>>`] | [NameArgList or Flag](#namearglist-or-flag-style) | `name(arg, arg, ...)` or `name` | /// /// Note: the type `T` in the table above needs to implement `syn::parse::Parse`. /// /// With the above type as P (`bool` and `Flag` are excluded), you can also use the following types. /// /// | field type | effect | /// | -------------------- | ----------------------------------------------- | /// | `Option

` | [optional parameter](#optional-named-parameter) | /// | `HashMap` | [rest parameter](#rest-named-parameter) | /// /// ## Flag style /// /// A field with the type `bool` will be a parameter that specifies only its name. /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute}; /// /// #[derive(StructMeta)] /// struct Args { /// a: bool, /// b: bool, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a)]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a, true); /// assert_eq!(args.b, false); /// /// let attr: Attribute = parse_quote!(#[attr(a, b)]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a, true); /// assert_eq!(args.b, true); /// # syn::Result::Ok(()) /// ``` /// /// If you use `Flag` instead of `bool`, you will get its `Span` when the argument is specified. /// /// ```rust /// use structmeta::{Flag, StructMeta}; /// use syn::{parse_quote, Attribute}; /// /// #[derive(StructMeta)] /// struct Args { /// a: Flag, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a)]); /// let args: Args = attr.parse_args()?; /// if let Some(_span) = args.a.span { /// // Use span. /// } /// # syn::Result::Ok(()) /// ``` /// /// ## NameValue style /// /// A field with type `T` or `NameValue` will be `name = value` style parameter. /// /// ```rust /// use structmeta::{NameValue, StructMeta}; /// use syn::{parse_quote, Attribute, LitInt, LitStr}; /// /// #[derive(StructMeta)] /// struct Args { /// a: LitStr, /// b: NameValue, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a = "abc", b = 10)]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a.value(), "abc"); /// assert_eq!(args.b.value.base10_parse::()?, 10); /// # syn::Result::Ok(()) /// ``` /// /// ## NameArgs style /// /// A field with type `NameArgs` will be `name(args)` style parameter. /// /// ```rust /// use structmeta::{NameArgs, StructMeta}; /// use syn::{parse_quote, Attribute, LitInt, LitStr}; /// /// #[derive(StructMeta)] /// struct Args { /// a: NameArgs, /// b: NameArgs, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a("abc"), b(10))]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a.args.value(), "abc"); /// assert_eq!(args.b.args.base10_parse::()?, 10); /// # syn::Result::Ok(()) /// ``` /// /// ## NameArgs or Flag style /// /// A field with type `NameArgs` will be `name(args)` or `name` style parameter. /// /// ```rust /// use structmeta::{NameArgs, StructMeta}; /// use syn::{parse_quote, Attribute, LitInt, LitStr}; /// /// #[derive(StructMeta)] /// struct Args { /// a: NameArgs>, /// b: NameArgs>, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a, b(10))]); /// let args: Args = attr.parse_args()?; /// assert!(args.a.args.is_none()); /// assert_eq!(args.b.args.unwrap().base10_parse::()?, 10); /// # syn::Result::Ok(()) /// ``` /// /// ## NameArgList style /// /// A field with type `NameArgs>` will be `name(arg, arg, ...)` style parameter. /// /// ```rust /// use structmeta::{NameArgs, StructMeta}; /// use syn::{parse_quote, Attribute, LitStr}; /// /// #[derive(StructMeta)] /// struct Args { /// a: NameArgs>, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a())]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a.args.len(), 0); /// /// let attr: Attribute = parse_quote!(#[attr(a("x"))]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a.args.len(), 1); /// /// let attr: Attribute = parse_quote!(#[attr(a("x", "y"))]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.a.args.len(), 2); /// # syn::Result::Ok(()) /// ``` /// /// ## NameArgList or Flag style /// /// A field with type `NameArgs>>` will be `name(arg, arg, ...)` or `name` style parameter. /// /// ```rust /// use structmeta::{NameArgs, StructMeta}; /// use syn::{parse_quote, Attribute, LitStr}; /// /// #[derive(StructMeta)] /// struct Args { /// abc: NameArgs>>, /// } /// /// let attr: Attribute = parse_quote!(#[attr(abc)]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.abc.args.is_none(), true); /// /// let attr: Attribute = parse_quote!(#[attr(abc())]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.abc.args.unwrap().len(), 0); /// /// let attr: Attribute = parse_quote!(#[attr(abc("x"))]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.abc.args.unwrap().len(), 1); /// /// let attr: Attribute = parse_quote!(#[attr(abc("x", "y"))]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.abc.args.unwrap().len(), 2); /// # syn::Result::Ok(()) /// ``` /// /// ## Optional named parameter /// /// If you use `Option` for the field type, it becomes an optional parameter. /// /// ```rust /// use structmeta::{NameValue, StructMeta}; /// use syn::{parse_quote, Attribute, LitInt, LitStr}; /// /// #[derive(StructMeta)] /// struct Args { /// a: Option, /// b: Option>, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a = "abc")]); /// let args: Args = attr.parse_args()?; /// assert!(args.a.is_some()); /// assert!(args.b.is_none()); /// /// let attr: Attribute = parse_quote!(#[attr(b = 10)]); /// let args: Args = attr.parse_args()?; /// assert!(args.a.is_none()); /// assert!(args.b.is_some()); /// # syn::Result::Ok(()) /// ``` /// /// ## Rest named parameter /// /// If `HashMap` is used for the field type, the field will contain named arguments that are not associated with the field. /// /// ```rust /// use std::collections::HashMap; /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute, LitInt}; /// /// #[derive(StructMeta)] /// struct Args { /// a: Option, /// rest: HashMap, /// } /// /// let attr: Attribute = parse_quote!(#[attr(a = 10, b = 20, c = 30)]); /// let args: Args = attr.parse_args()?; /// assert!(args.a.is_some()); /// let mut keys: Vec<_> = args.rest.keys().collect(); /// keys.sort(); /// assert_eq!(keys, vec!["b", "c"]); /// # syn::Result::Ok(()) /// ``` /// /// # Unnamed parameter /// /// The following field will be "Unnamed parameter". /// /// - field in tuple struct. /// - field with `#[struct_meta(unnamed)]` in record struct. /// - However, fields that meet the following conditions are excluded /// - field with `#[struct_meta(name = "...")]` /// - field with type `HashMap` /// /// "Unnamed parameter" is a value-only parameter, such as `#[attr("abc", 10, 20)]`. /// /// | field type | effect | /// | ----------- | ------------------------------------------------- | /// | `T` | [required parameter](#required-unnamed-parameter) | /// | `Option` | [optional parameter](#optional-unnamed-parameter) | /// | `Vec` | [variadic parameter](#variadic-unnamed-parameter) | /// /// The type `T` in the table above needs to implement `syn::parse::Parse`. /// /// ## Required unnamed parameter /// /// Fields of the type that implement `syn::parse::Parse` will be required parameters. /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute, LitStr, Result}; /// /// #[derive(StructMeta)] /// struct Args(LitStr); /// /// let attr: Attribute = parse_quote!(#[attr()]); /// let args: Result = attr.parse_args(); /// assert!(args.is_err()); /// /// let attr: Attribute = parse_quote!(#[attr("a")]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.0.value(), "a"); /// # syn::Result::Ok(()) /// ``` /// /// ## Optional unnamed parameter /// /// Fields of type `Option` will be optional parameters. /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute, LitStr}; /// /// #[derive(StructMeta)] /// struct Args(Option); /// /// let attr: Attribute = parse_quote!(#[attr()]); /// let args: Args = attr.parse_args()?; /// assert!(args.0.is_none()); /// /// let attr: Attribute = parse_quote!(#[attr("a")]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.0.unwrap().value(), "a"); /// # syn::Result::Ok(()) /// ``` /// /// ## Variadic unnamed parameter /// /// If you use `Vec` as the field type, multiple arguments can be stored in a single field. /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute, LitStr}; /// /// #[derive(StructMeta)] /// struct Args(Vec); /// /// let attr: Attribute = parse_quote!(#[attr()]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.0.len(), 0); /// /// let attr: Attribute = parse_quote!(#[attr("a")]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.0.len(), 1); /// assert_eq!(args.0[0].value(), "a"); /// /// let attr: Attribute = parse_quote!(#[attr("a", "b")]); /// let args: Args = attr.parse_args()?; /// assert_eq!(args.0.len(), 2); /// assert_eq!(args.0[0].value(), "a"); /// assert_eq!(args.0[1].value(), "b"); /// # syn::Result::Ok(()) /// ``` /// /// # Parameter order /// /// The parameters must be in the following order. /// /// - Unnamed /// - Required /// - Optional /// - Variadic /// - Named /// /// # Helper attribute `#[struct_meta(...)]` /// /// | argument | struct | field | effect | /// | -------------------------------------------------- | ------ | ----- | ---------------------------------------------------------------------------------------- | /// | `dump` | ✔ | | Causes a compile error and outputs the automatically generated code as an error message. | /// | [`name_filter = "..."`](#struct_metaname_filter--) | ✔ | | Specify how to distinguish between a parameter name and a value of an unnamed parameter. | /// | `name = "..."` | | ✔ | Specify a parameter name. | /// | `unnamed` | | ✔ | Make the field be treated as an unnamed parameter. | /// /// # Uses with `#[proc_macro_derive]` /// /// A type with `#[derive(StructMeta)]` can be used with [`syn::Attribute::parse_args`]. /// /// ```rust /// # extern crate proc_macro; /// use proc_macro::TokenStream; /// use quote::quote; /// use structmeta::StructMeta; /// use syn::{parse, parse_macro_input, DeriveInput, LitStr}; /// /// #[derive(StructMeta)] /// struct MyAttr { /// msg: LitStr, /// } /// /// # const IGNORE_TOKENS: &str = stringify! { /// #[proc_macro_derive(MyMsg, attributes(my_msg))] /// # }; /// pub fn derive_my_msg(input: TokenStream) -> TokenStream { /// let input = parse_macro_input!(input as DeriveInput); /// let mut msg = String::new(); /// for attr in input.attrs { /// if attr.path().is_ident("my_msg") { /// let attr = attr.parse_args::().unwrap(); /// msg = attr.msg.value(); /// } /// } /// quote!(const MSG: &str = #msg;).into() /// } /// ``` /// /// ```ignore /// #[derive(MyMsg)] /// #[my_msg(msg = "abc")] /// struct TestType; /// /// assert_eq!(MSG, "abc"); /// ``` /// /// # Uses with `#[proc_macro_attribute]` /// /// A type with `#[derive(StructMeta)]` can be used with `attr` parameter in attribute proc macro. /// /// ```rust /// # extern crate proc_macro; /// use proc_macro::TokenStream; /// use quote::quote; /// use structmeta::StructMeta; /// use syn::{parse, parse_macro_input, DeriveInput, LitStr}; /// /// #[derive(StructMeta)] /// struct MyAttr { /// msg: LitStr, /// } /// # const IGNORE_TOKENS: &str = stringify! { /// #[proc_macro_attribute] /// # }; /// pub fn my_attr(attr: TokenStream, _item: TokenStream) -> TokenStream { /// let attr = parse::(attr).unwrap(); /// let msg = attr.msg.value(); /// quote!(const MSG: &str = #msg;).into() /// } /// ``` /// /// ```ignore /// #[my_attr(msg = "xyz")] /// struct TestType; /// /// assert_eq!(MSG, "xyz"); /// ``` /// /// # Parsing ambiguous arguments /// /// If one or more `name = value` style parameters are defined, arguments beginning with `name =` will be parsed as `name = value` style, /// even if the name is different from what it is defined as. /// /// This specification is intended to prevent `name = value` from being treated as unnamed parameter due to typo. /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute, Expr, LitInt, Result}; /// /// #[derive(StructMeta)] /// struct WithNamed { /// #[struct_meta(unnamed)] /// unnamed: Option, /// x: Option, /// } /// /// let attr_x: Attribute = parse_quote!(#[attr(x = 10)]); /// let result: WithNamed = attr_x.parse_args().unwrap(); /// assert_eq!(result.unnamed.is_some(), false); /// assert_eq!(result.x.is_some(), true); /// /// // `y = 10` is parsed as a wrong named parameter. /// let attr_y: Attribute = parse_quote!(#[attr(y = 10)]); /// let result: Result = attr_y.parse_args(); /// assert!(result.is_err()); /// ``` /// /// If `name = value` style parameter is not defined, it will be parsed as unnamed parameter. /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute, Expr, LitInt, Result}; /// /// #[derive(StructMeta)] /// struct WithoutNamed { /// #[struct_meta(unnamed)] /// unnamed: Option, /// } /// /// // `y = 10` is parsed as a unnamed parameter. /// let attr_y: Attribute = parse_quote!(#[attr(y = 10)]); /// let result: WithoutNamed = attr_y.parse_args().unwrap(); /// assert_eq!(result.unnamed, Some(parse_quote!(y = 10))); /// ``` /// /// Similarly, if one or more `name(args)` style parameters are defined, arguments with `name(args)` will be parsed as `name(args)` style. /// If `name(args)` style parameter is not defined, it will be parsed as unnamed parameter. /// /// The same is true for `name` or `name = value` style parameter. /// /// ## `#[struct_meta(name_filter = "...")]` /// /// By attaching `#[struct_meta(name_filter = "...")]` to struct definition, you can restrict the names that can be used as parameter names and treat other identifiers as a value of unnamed parameter. /// /// The following value can be used. /// /// - `"snake_case"` /// /// ```rust /// use structmeta::StructMeta; /// use syn::{parse_quote, Attribute, Expr, LitInt, Result}; /// /// let attr_x: Attribute = parse_quote!(#[attr(X)]); /// let attr_y: Attribute = parse_quote!(#[attr(Y)]); /// /// #[derive(StructMeta)] /// struct NoFilter { /// #[struct_meta(unnamed)] /// unnamed: Option, /// #[struct_meta(name = "X")] /// x: bool, /// } /// let result: NoFilter = attr_x.parse_args().unwrap(); /// assert_eq!(result.unnamed, None); /// assert_eq!(result.x, true); // `X` is parsed as a named parameter. /// /// let result: Result = attr_y.parse_args(); /// assert!(result.is_err()); // `Y` is parsed as a wrong named parameter. /// /// /// #[derive(StructMeta)] /// #[struct_meta(name_filter = "snake_case")] /// struct WithFilter { /// #[struct_meta(unnamed)] /// unnamed: Option, /// #[struct_meta(name = "X")] /// x: bool, /// } /// let result: WithFilter = attr_x.parse_args().unwrap(); /// assert_eq!(result.unnamed, Some(parse_quote!(X))); // `X` is parsed as a unnamed parameter. /// assert_eq!(result.x, false); /// ``` // #[include_doc("../../doc/struct_meta.md", end)] pub use structmeta_derive::StructMeta;