1use alloc::{string::ToString, vec::Vec};
4
5use quote::ToTokens;
6
7use syn::{
8 Expr, Ident, LitInt, LitStr, Path, Token,
9 parse::{Parse, ParseStream},
10 punctuated::Punctuated,
11 token::Paren,
12};
13
14use super::ErrorList;
15
16#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24pub struct Param {
25 pub name: Option<Ident>,
30
31 pub kind: ParamKind,
33}
34
35impl Param {
36 #[inline]
38 pub const fn lone(kind: ParamKind) -> Self {
39 Self { name: None, kind }
40 }
41
42 #[inline]
44 pub const fn identified(name: Ident, kind: ParamKind) -> Self {
45 Self { name: Some(name), kind }
46 }
47}
48
49impl Param {
50 pub fn classify<'a>(iter: impl IntoIterator<Item = &'a syn::Attribute>) -> syn::Result<(Vec<Self>, Vec<&'a syn::Attribute>)> {
53 let attr_iter = iter.into_iter();
54
55 let (attr_len, ..) = attr_iter.size_hint();
56
57 let mut param_list = Vec::with_capacity(attr_len);
58
59 let mut attr_list = Vec::new();
60
61 let mut error_list = ErrorList::empty();
62
63 for attr in attr_iter {
64 if let Some(ident) = attr.path().get_ident() {
65 if ident == "error" {
66 match attr.parse_args_with(Param::parse) {
67 Ok(param) => param_list.push(param),
68 Err(error) => error_list.append(error),
69 }
70 } else {
71 attr_list.push(attr);
72 }
73 } else {
74 attr_list.push(attr);
75 }
76 }
77
78 error_list.compose((param_list, attr_list))
79 }
80}
81
82impl Parse for Param {
83 fn parse(input: ParseStream) -> syn::Result<Self> {
84 let lookahead = input.lookahead1();
85
86 if lookahead.peek(Ident) {
87 let ident: Ident = input.parse()?;
88
89 let param = if ident == "inline" {
90 let inline_opts: InlineOptions = if input.peek(Paren) {
91 let inline_content;
92
93 syn::parenthesized!(inline_content in input);
94
95 inline_content.parse()?
96 } else {
97 InlineOptions::default()
98 };
99
100 Param::identified(ident, ParamKind::Inline(inline_opts))
101 } else if ident == "import" {
102 let import_content;
103
104 syn::parenthesized!(import_content in input);
105
106 let import_root: ImportRoot = import_content.parse()?;
107
108 Param::identified(ident, ParamKind::Import(import_root))
109 } else if ident == "source" {
110 let source_content;
111
112 syn::parenthesized!(source_content in input);
113
114 let source_field: FieldRef = source_content.parse()?;
115
116 Param::identified(ident, ParamKind::Source(source_field))
117 } else if ident == "transparent" {
118 let trans_content;
119
120 syn::parenthesized!(trans_content in input);
121
122 let source_field: Transparent = trans_content.parse()?;
123
124 Param::identified(ident, ParamKind::Transparent(source_field))
125 } else if ident == "from" {
126 Param::identified(ident, ParamKind::From)
127 } else {
128 return Err(syn::Error::new_spanned(
129 ident,
130 "expected `inline`, `import`, `source`, `transparent`, or `from`",
131 ));
132 };
133
134 Ok(param)
135 } else if lookahead.peek(LitStr) {
136 Ok(Param::lone(ParamKind::Format(Format::parse(input)?)))
137 } else {
138 Err(lookahead.error())
139 }
140 }
141}
142
143impl Param {
144 #[inline]
149 pub fn attribute(target_meta: &syn::Meta) -> syn::Result<Self> {
150 syn::parse2::<Self>(target_meta.to_token_stream()).map_err(|error| syn::Error::new_spanned(target_meta, error))
151 }
152}
153
154#[derive(Clone, Debug, PartialEq, Eq, Hash)]
156pub enum ParamKind {
157 Inline(InlineOptions),
159
160 Import(ImportRoot),
162
163 Format(Format),
165
166 Source(FieldRef),
168
169 Transparent(Transparent),
174
175 From,
180}
181
182#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Copy)]
184pub enum InlineOptions {
185 #[default]
192 Neutral,
193
194 Never,
196
197 Always,
199}
200
201impl Parse for InlineOptions {
202 #[inline]
203 fn parse(input: ParseStream) -> syn::Result<Self> {
204 let ident: Ident = input.parse()?;
205
206 match ident.to_string().as_str() {
207 "neutral" => Ok(Self::Neutral),
208 "never" => Ok(Self::Never),
209 "always" => Ok(Self::Always),
210 _ => Err(syn::Error::new_spanned(ident, "expected `neutral`, `never` or `always`")),
211 }
212 }
213}
214
215#[derive(Clone, Debug, PartialEq, Eq, Hash)]
217pub struct ImportRoot(pub Path);
218
219impl Parse for ImportRoot {
220 #[inline]
221 fn parse(input: ParseStream) -> syn::Result<Self> {
222 let path: Path = input.parse()?;
223
224 Ok(Self(path))
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Hash)]
230pub struct Format {
231 pub format: LitStr,
233
234 pub format_args: Punctuated<Expr, Token![,]>,
236}
237
238impl Parse for Format {
239 #[inline]
240 fn parse(input: ParseStream) -> syn::Result<Self> {
241 let format: LitStr = input.parse()?;
242
243 let format_args = if input.peek(Token![,]) {
244 let _ = input.parse::<Token![,]>()?;
245
246 let format_args = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
247
248 format_args
249 } else {
250 Punctuated::new()
251 };
252
253 Ok(Self { format, format_args })
254 }
255}
256
257#[derive(Debug, Clone, PartialEq, Eq, Hash)]
259pub enum FieldRef {
260 Named(Ident),
262
263 Indexed(usize),
265}
266
267impl Parse for FieldRef {
268 #[inline]
269 fn parse(input: ParseStream) -> syn::Result<Self> {
270 let lookahead = input.lookahead1();
271
272 if lookahead.peek(Ident) {
273 let ident: Ident = input.parse()?;
274
275 Ok(Self::Named(ident))
276 } else if lookahead.peek(LitInt) {
277 let lit_int: LitInt = input.parse()?;
278
279 let index = lit_int
280 .base10_parse::<usize>()
281 .map_err(|_| syn::Error::new_spanned(lit_int, "expected valid field index"))?;
282
283 Ok(Self::Indexed(index))
284 } else {
285 Err(lookahead.error())
286 }
287 }
288}
289
290#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
293pub enum ForwardSource {
294 #[default]
298 No,
299
300 Yes,
302}
303
304impl Parse for ForwardSource {
305 fn parse(input: ParseStream) -> syn::Result<Self> {
306 let ident: Ident = input.parse()?;
307
308 if ident == "forward" {
309 Ok(Self::Yes)
310 } else {
311 Err(syn::Error::new_spanned(
312 ident,
313 "expected just `source`, no internal further parameters",
314 ))
315 }
316 }
317}
318
319#[derive(Clone, Debug, PartialEq, Eq, Hash)]
321pub struct Transparent(pub FieldRef);
322
323impl Parse for Transparent {
324 #[inline]
325 fn parse(input: ParseStream) -> syn::Result<Self> {
326 let field_ref: FieldRef = input.parse()?;
327
328 Ok(Self(field_ref))
329 }
330}