proc_macro_kwargs/
args.rs1use std::fmt::Debug;
6use std::hash::Hash;
7
8use indexmap::{map::Entry, IndexMap};
9
10use proc_macro2::{Ident, Span};
11use syn::parse::{Parse, ParseStream};
12use syn::{braced, ext::IdentExt, punctuated::Punctuated, Token};
13
14use crate::MacroArg;
15
16pub trait ParsedArgValue<Id: KeywordArgId>: Sized {
18 fn id(&self) -> Id;
20 fn parse_with_id(id: Id, id_span: Span, stream: ParseStream) -> syn::Result<Self>;
22}
23pub trait KeywordArgId: Copy + Eq + Hash + Debug {
25 fn as_str(&self) -> &'_ str;
27 fn from_name(name: &str) -> Option<Self>;
30}
31
32pub struct KeywordArg<K: MacroKeywordArgs> {
35 pub name: Ident,
41 pub id: K::ArgId,
43 pub value: K::ParsedArg,
45}
46impl<K: MacroKeywordArgs> Parse for KeywordArg<K> {
47 fn parse(stream: ParseStream) -> syn::Result<Self> {
48 let name = stream.call(Ident::parse_any)?;
49 let name_text = name.to_string();
50 let id = K::ArgId::from_name(&name_text).ok_or_else(|| {
51 syn::Error::new(
52 name.span(),
53 format!("Unknown argument name: {}", &name_text),
54 )
55 })?;
56 stream.parse::<Token![=>]>()?;
57 let value = K::ParsedArg::parse_with_id(id, name.span(), stream)?;
58 Ok(KeywordArg { id, name, value })
59 }
60}
61pub struct ParsedKeywordArguments<K: MacroKeywordArgs> {
63 by_name: IndexMap<K::ArgId, KeywordArg<K>>,
65}
66impl<K: MacroKeywordArgs> ParsedKeywordArguments<K> {
67 pub fn get(&self, id: K::ArgId) -> Option<&'_ KeywordArg<K>> {
70 self.by_name.get(&id)
71 }
72 pub fn take(&mut self, id: K::ArgId) -> Option<KeywordArg<K>> {
77 self.by_name.swap_remove(&id)
78 }
79 pub fn require(&mut self, id: K::ArgId) -> syn::Result<KeywordArg<K>> {
84 self.take(id).ok_or_else(|| {
85 syn::Error::new(
86 Span::call_site(),
87 format!("Missing required argument `{}`", id.as_str()),
88 )
89 })
90 }
91 #[inline]
94 pub fn iter(&self) -> impl Iterator<Item = &'_ KeywordArg<K>> + '_ {
95 self.by_name.values()
96 }
97}
98impl<K: MacroKeywordArgs> Parse for ParsedKeywordArguments<K> {
99 fn parse(stream: ParseStream) -> syn::Result<Self> {
100 let punct: Punctuated<KeywordArg<K>, Token![,]> =
101 stream.call(Punctuated::parse_terminated)?;
102 let mut by_name = IndexMap::with_capacity(punct.len());
103 let mut errors = Vec::new();
104 for arg in punct.into_iter() {
105 match by_name.entry(arg.id) {
106 Entry::Occupied(_entry) => {
107 errors.push(syn::Error::new(
108 arg.name.span(),
109 format!("Duplicate values for argument: {}", arg.name),
110 ));
111 }
112 Entry::Vacant(entry) => {
113 entry.insert(arg);
114 }
115 }
116 }
117 if !errors.is_empty() {
118 return Err(crate::combine_errors(errors));
119 }
120 Ok(ParsedKeywordArguments { by_name })
121 }
122}
123impl<K: MacroKeywordArgs> MacroArg for ParsedKeywordArguments<K> {
124 fn parse_macro_arg(stream: ParseStream) -> syn::Result<Self> {
125 let content;
126 braced!(content in stream);
127 content.parse::<Self>()
128 }
129}
130
131pub trait MacroKeywordArgs: MacroArg + Parse {
138 type ArgId: KeywordArgId;
140 type ParsedArg: ParsedArgValue<Self::ArgId>;
142 fn from_keyword_args(kwargs: ParsedKeywordArguments<Self>) -> Result<Self, syn::Error>;
145}