attribute_derive/
std_impls.rs

1use manyhow::{span_range, SpanRanged};
2use syn::parse::discouraged::{AnyDelimiter, Speculative};
3use syn::parse::{Parse, ParseStream};
4use syn::token::{Bracket, Paren};
5use syn::{bracketed, parenthesized, LitBool, LitChar, LitFloat, LitInt, LitStr, Result, Token};
6
7use super::FromAttr;
8use crate::from_partial::{Defaulting, FromPartial, Partial};
9use crate::parsing::{
10    parse_name, AttributeBase, AttributeNamed, AttributePeekable, AttributePositional,
11    AttributeValue, Named, PositionalValue, SpannedValue,
12};
13use crate::AttributeIdent;
14
15macro_rules! ParseAs {
16    ($parse:ty => $($type:ty),+; $value:ident $($tt:tt)*) => {
17        ParseAs! {$parse => $($type),+; |$value| $value $($tt)*}
18    };
19    ($parse:ty => $($type:ty),+; |$value:ident| $conv:expr) => {
20        $(impl AttributeBase for $type {
21            type Partial = Self;
22        }
23
24        impl AttributeValue for $type {
25            fn parse_value(input: ParseStream) -> Result<SpannedValue<Self>> {
26                let $value: $parse = input.parse()?;
27                let span = span_range!($value);
28                Ok(SpannedValue::new($conv, span))
29            }
30        }
31
32        impl PositionalValue for $type {}
33    )+
34    };
35}
36
37ParseAs!(LitStr => String; v.value());
38ParseAs!(LitChar => char; v.value());
39// TODO: This could be achieved with auto-deref or real specilization
40//       ParseAs!(LitByteStr => Vec<u8>; v.value());
41ParseAs!(LitInt => u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize; v.base10_parse()?);
42ParseAs!(LitFloat => f32, f64; v.base10_parse()?);
43
44impl<T: FromPartial<P>, P> FromPartial<Partial<Option<P>>> for Option<T> {
45    fn from(partial: Partial<Option<P>>) -> Result<Self> {
46        partial.0.map(T::from).transpose()
47    }
48}
49
50impl<T: AttributeBase> AttributeBase for Option<T> {
51    type Partial = Defaulting<Partial<Option<T::Partial>>>;
52}
53
54impl<T: AttributePositional + AttributePeekable> AttributePositional for Option<T> {
55    fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>> {
56        Ok(if T::peek(input) {
57            T::parse_positional(input)?.map(|SpannedValue { value, span }| SpannedValue {
58                value: Defaulting(Partial(Some(value))),
59                span,
60            })
61        } else {
62            None
63        })
64    }
65}
66
67impl<T: AttributeNamed> AttributeNamed for Option<T> {
68    fn parse_named(
69        name: &'static str,
70        input: ParseStream,
71    ) -> Result<Option<Named<SpannedValue<Self::Partial>>>> {
72        T::parse_named(name, input).map(|value| {
73            value.map(|value| Named {
74                value: SpannedValue {
75                    value: Defaulting(Partial(Some(value.value.value))),
76                    span: value.value.span,
77                },
78                name: value.name,
79            })
80        })
81    }
82}
83
84impl<T: FromAttr> FromAttr for Option<T> {
85    fn parse_partial(input: ParseStream) -> Result<Self::Partial> {
86        T::parse_partial(input)
87            .map(Some)
88            .map(Partial)
89            .map(Defaulting)
90    }
91}
92
93impl<T: AttributeIdent> AttributeIdent for Option<T> {
94    const IDENTS: &'static [&'static str] = T::IDENTS;
95}
96
97impl<T: FromPartial<P>, P> FromPartial<Partial<Vec<P>>> for Vec<T> {
98    fn from(partial: Partial<Vec<P>>) -> Result<Self> {
99        partial.0.into_iter().map(T::from).collect()
100    }
101
102    fn join(
103        first: Option<SpannedValue<Partial<Vec<P>>>>,
104        second: SpannedValue<Partial<Vec<P>>>,
105        _: &str,
106    ) -> Result<Option<SpannedValue<Partial<Vec<P>>>>> {
107        if let Some(mut first) = first {
108            first.value.0.extend(second.value.0);
109            Ok(Some(first))
110        } else {
111            Ok(Some(second))
112        }
113    }
114}
115
116impl<T: AttributeBase> AttributeBase for Vec<T> {
117    type Partial = Partial<Vec<T::Partial>>;
118}
119
120impl<T> AttributePeekable for Vec<T> {
121    fn peek(input: ParseStream) -> bool {
122        input.peek(Paren) || input.peek(Bracket)
123    }
124}
125
126impl<T> PositionalValue for Vec<T> {}
127
128fn parse_vec_inner<T: AttributePositional>(
129    input: ParseStream,
130) -> Result<<Vec<T> as AttributeBase>::Partial> {
131    // TODO parse_positional that return Ok(None) will behave wierd
132    let i = input.parse_terminated(T::parse_positional, Token!(,))?;
133    Ok(Partial(i.into_iter().flatten().map(|v| v.value).collect()))
134}
135
136impl<T: AttributePositional> AttributeValue for Vec<T> {
137    fn parse_value_meta(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
138        let content;
139        let paren = parenthesized!(content in input);
140        let fork = &content.fork();
141        match parse_vec_inner::<T>(fork) {
142            Ok(value) => {
143                content.advance_to(fork);
144                Ok(SpannedValue {
145                    value,
146                    span: paren.span.join().span_range(),
147                })
148            }
149            Err(err) => {
150                if let Ok((.., span, content)) = content.parse_any_delimiter() {
151                    if let Ok(value) = parse_vec_inner::<T>(&content) {
152                        return Ok(SpannedValue {
153                            value,
154                            span: span.join().span_range(),
155                        });
156                    }
157                }
158                Err(err)
159            }
160        }
161    }
162
163    fn parse_value(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
164        if input.peek(Paren) {
165            Self::parse_value_meta(input)
166        } else if input.peek(Bracket) {
167            let content;
168            let b = bracketed!(content in input);
169            let value = parse_vec_inner::<T>(&content)?;
170            Ok(SpannedValue {
171                value,
172                span: b.span.join().span_range(),
173            })
174        } else {
175            Err(input.error("expected `[` or `(`"))
176        }
177    }
178}
179
180impl AttributeBase for bool {
181    type Partial = Defaulting<bool>;
182}
183
184impl AttributePositional for bool {
185    fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>> {
186        let lit = LitBool::parse_value(input)?;
187        Ok(Some(SpannedValue {
188            value: Defaulting(lit.value.value),
189            span: lit.span.span_range(),
190        }))
191    }
192}
193
194impl AttributePeekable for bool {
195    fn peek(input: ParseStream) -> bool {
196        input.peek(LitBool)
197    }
198}
199
200impl AttributeNamed for bool {
201    fn parse_named(
202        name: &'static str,
203        input: ParseStream,
204    ) -> Result<Option<Named<SpannedValue<Self::Partial>>>> {
205        let Some(ident) = parse_name(input, name) else {
206            return Ok(None);
207        };
208        let value = if input.parse::<Token![=]>().is_ok() {
209            Self::parse_positional(input)?.unwrap()
210        } else if input.peek(Paren) {
211            let content;
212            parenthesized!(content in input);
213            Self::parse_positional(&content)?.unwrap()
214        } else {
215            SpannedValue {
216                value: Defaulting(true),
217                span: ident.span().span_range(),
218            }
219        };
220        Ok(Some(Named { value, name: ident }))
221    }
222}
223
224impl crate::FromAttr for bool {
225    fn parse_partial(input: ParseStream) -> Result<Self::Partial> {
226        if input.is_empty() {
227            Ok(Defaulting(true))
228        } else {
229            Ok(Defaulting(LitBool::parse(input)?.value()))
230        }
231    }
232}