attribute_derive/
std_impls.rs1use 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());
39ParseAs!(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 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}