attr_parser_fn/meta/
mod.rs

1use std::fmt::Write;
2
3use impl_variadics::impl_variadics;
4use proc_macro2::{Span, TokenStream};
5use syn::{
6    meta::ParseNestedMeta,
7    parenthesized,
8    parse::{Parse, ParseStream, Parser},
9    token::Paren,
10    Error, LitStr, Result, Token,
11};
12
13use crate::ParseAttrTrait;
14
15pub use self::{
16    conflicts::{conflicts, ConflictGroup},
17    utils::{meta_list, Map, MetaList, Optional, ParseMetaExt},
18};
19
20mod conflicts;
21mod utils;
22
23pub trait ParseMeta {
24    type Output;
25
26    fn conflict_alternative_arm(&self, f: &mut dyn Write) -> std::fmt::Result;
27    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool>;
28    fn finish(self) -> Result<Self::Output>;
29    fn ok_to_finish(&self) -> bool;
30}
31
32pub trait ParseMetaUnnamed {
33    type Output;
34
35    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool>;
36    fn finish(self) -> Option<Self::Output>;
37    fn ok_to_finish(&self) -> bool;
38}
39
40impl<T> ParseMeta for (&str, T)
41where
42    T: ParseMetaUnnamed,
43{
44    type Output = T::Output;
45
46    fn conflict_alternative_arm(&self, f: &mut dyn Write) -> std::fmt::Result {
47        write!(f, "`{}`", self.0)
48    }
49
50    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool> {
51        if nested.path.is_ident(self.0) {
52            self.1.parse(nested)
53        } else {
54            Ok(false)
55        }
56    }
57
58    fn finish(self) -> Result<Self::Output> {
59        self.1.finish().ok_or_else(|| {
60            Error::new(
61                Span::call_site(),
62                format!("attribute `{}` must be specified", self.0),
63            )
64        })
65    }
66
67    fn ok_to_finish(&self) -> bool {
68        self.1.ok_to_finish()
69    }
70}
71
72pub fn path_only() -> PathOnly {
73    PathOnly { assigned: false }
74}
75
76pub struct PathOnly {
77    assigned: bool,
78}
79
80impl ParseMetaUnnamed for PathOnly {
81    type Output = bool;
82
83    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool> {
84        if nested.input.peek(Token![,]) || nested.input.is_empty() {
85            self.assigned = true;
86            Ok(true)
87        } else {
88            Ok(false)
89        }
90    }
91
92    fn finish(self) -> Option<Self::Output> {
93        Some(self.assigned)
94    }
95
96    fn ok_to_finish(&self) -> bool {
97        true
98    }
99}
100
101pub fn key_value<T>() -> KeyValue<T>
102where
103    T: Parse,
104{
105    KeyValue { value: None }
106}
107
108pub struct KeyValue<T> {
109    value: Option<T>,
110}
111
112impl<T> ParseMetaUnnamed for KeyValue<T>
113where
114    T: Parse,
115{
116    type Output = T;
117
118    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool> {
119        if nested.input.peek(Token![=]) {
120            self.value = Some(nested.value()?.parse::<T>()?);
121            Ok(true)
122        } else {
123            Ok(false)
124        }
125    }
126
127    fn finish(self) -> Option<Self::Output> {
128        self.value
129    }
130
131    fn ok_to_finish(&self) -> bool {
132        self.value.is_some()
133    }
134}
135
136pub fn key_str<T>() -> KeyStr<T>
137where
138    T: Parse,
139{
140    KeyStr { value: None }
141}
142
143pub struct KeyStr<T> {
144    value: Option<T>,
145}
146
147impl<T> ParseMetaUnnamed for KeyStr<T>
148where
149    T: Parse,
150{
151    type Output = T;
152
153    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool> {
154        if nested.input.peek(Token![=]) {
155            let litstr: LitStr = nested.value()?.parse()?;
156            self.value = Some(litstr.parse()?);
157            Ok(true)
158        } else {
159            Ok(false)
160        }
161    }
162
163    fn finish(self) -> Option<Self::Output> {
164        self.value
165    }
166
167    fn ok_to_finish(&self) -> bool {
168        self.value.is_some()
169    }
170}
171
172pub fn list<P>(parser: P) -> List<P>
173where
174    P: ParseAttrTrait,
175{
176    List(ListInner::Unassigned(parser))
177}
178
179enum ListInner<P>
180where
181    P: ParseAttrTrait,
182{
183    Unassigned(P),
184    Assigned(P::Output),
185    Intermediate,
186}
187
188pub struct List<P>(ListInner<P>)
189where
190    P: ParseAttrTrait;
191
192impl<P> ParseMetaUnnamed for List<P>
193where
194    P: ParseAttrTrait,
195{
196    type Output = P::Output;
197
198    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool> {
199        if !nested.input.peek(Paren) {
200            return Ok(false);
201        }
202
203        let inner = &mut self.0;
204
205        let content;
206        parenthesized!(content in nested.input);
207
208        let ListInner::Unassigned(parser) = std::mem::replace(inner, ListInner::Intermediate)
209        else {
210            unreachable!("cannot assign a list twice");
211        };
212
213        *inner = ListInner::Assigned(parser.parse(&content)?);
214        Ok(true)
215    }
216
217    fn finish(self) -> Option<Self::Output> {
218        match self.0 {
219            ListInner::Assigned(output) => Some(output),
220            ListInner::Intermediate => unreachable!("this list is not correctly assigned"),
221            ListInner::Unassigned(parser) => {
222                let new_parser = |input: ParseStream| parser.parse(input);
223                new_parser.parse2(TokenStream::new()).ok()
224            }
225        }
226    }
227
228    fn ok_to_finish(&self) -> bool {
229        matches!(self.0, ListInner::Assigned(_))
230    }
231}
232
233impl_variadics! {
234    ..21 "T*" => {
235        impl<#(#T0,)*> ParseMeta for (#(#T0,)*)
236        where
237            #(#T0: ParseMeta,)*
238        {
239            type Output = (#(#T0::Output,)*);
240
241            fn conflict_alternative_arm(&self, _f: &mut dyn Write) -> std::fmt::Result {
242                let mut _comma = "";
243
244                #(
245                    write!(_f, "{_comma}")?;
246                    self.#index.conflict_alternative_arm(_f)?;
247                    _comma = ", ";
248                )*
249
250                Ok(())
251            }
252
253            fn parse(&mut self, _nested: &ParseNestedMeta) -> Result<bool> {
254                Ok(false #(|| self.#index.parse(_nested)?)*)
255            }
256
257            fn finish(self) -> Result<Self::Output> {
258                Ok((
259                    #(self.#index.finish()?,)*
260                ))
261            }
262
263            fn ok_to_finish(&self) -> bool {
264                true #(&& self.#index.ok_to_finish())*
265            }
266        }
267    }
268}