proc_macro_kwargs/
args.rs

1//! The main interface to parsing kwargs
2//!
3//! Generally speaking,
4//! this should be considered an implementation detail of the macro.
5use 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
16/// The runtime value of a parsed argument
17pub trait ParsedArgValue<Id: KeywordArgId>: Sized {
18    /// Return the corresponding id
19    fn id(&self) -> Id;
20    /// Parse the argument with the specified id
21    fn parse_with_id(id: Id, id_span: Span, stream: ParseStream) -> syn::Result<Self>;
22}
23/// The unique id for a single keyword argument
24pub trait KeywordArgId: Copy + Eq + Hash + Debug {
25    /// The name of this argument, as a string
26    fn as_str(&self) -> &'_ str;
27    /// Get the argument with the specified name,
28    /// returning `None` if it's unknown
29    fn from_name(name: &str) -> Option<Self>;
30}
31
32/// A parsed argument,
33/// along with its original name and id.
34pub struct KeywordArg<K: MacroKeywordArgs> {
35    /// The name of this argument,
36    /// as a `Ident`
37    ///
38    /// This is needed in addition to `id`,
39    /// because it contains a `Span`
40    pub name: Ident,
41    /// The id of this argument
42    pub id: K::ArgId,
43    /// The parsed value of this argument
44    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}
61/// The list of parsed keyword arguments
62pub struct ParsedKeywordArguments<K: MacroKeywordArgs> {
63    /// A map of arguments, in the order of the declaration.
64    by_name: IndexMap<K::ArgId, KeywordArg<K>>,
65}
66impl<K: MacroKeywordArgs> ParsedKeywordArguments<K> {
67    /// Lookup a argument by its id,
68    /// returning `None` if it's missing
69    pub fn get(&self, id: K::ArgId) -> Option<&'_ KeywordArg<K>> {
70        self.by_name.get(&id)
71    }
72    /// Consume the argument with the specified id,
73    /// returning `None` if it's missing
74    ///
75    /// Implicitly shifts the ordering of the map.
76    pub fn take(&mut self, id: K::ArgId) -> Option<KeywordArg<K>> {
77        self.by_name.swap_remove(&id)
78    }
79    /// Require that the argument with the specified id exists,
80    /// returning an error if it is missing
81    ///
82    /// Consumes the argument, as if calling `take`
83    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    /// Iterate over the original list of arguments,
92    /// in the order of their declaration
93    #[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
131/// The whole point.
132///
133/// Defines the interface for parsing keyword args.
134///
135/// A set of argument can itself be nested as a `MacroArg`,
136/// provided it is wrapped in braces `{ }`
137pub trait MacroKeywordArgs: MacroArg + Parse {
138    /// The id of an argument.
139    type ArgId: KeywordArgId;
140    /// The runtime value of a parsed argument
141    type ParsedArg: ParsedArgValue<Self::ArgId>;
142    /// Create the parsed arguments struct from
143    /// its list of arguments
144    fn from_keyword_args(kwargs: ParsedKeywordArguments<Self>) -> Result<Self, syn::Error>;
145}