derive_attribute_utils/
shared.rs

1use std::{str::FromStr, fmt::Display};
2
3use proc_macro2::Span;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum ErrorMsg {
8    #[error("Failed to Parse Attribute: expected list of key/value pairs EX) ATTR_NAME(x = 1) and/or booleans EX) ATTR_NAME(is_x)")]
9    FailedToParseAttr,
10
11    #[error("Invalid Item: expected struct")]
12    InvalidItem,
13
14    #[error("Missing Attribute: attribute '{0}' is required")]
15    MissingAttribute(&'static str),
16
17    #[error("Missing Argument: '{0}' is required")]
18    MissingArg(&'static str),
19    #[error("Invalid Type: expected {expected}")]
20    InvalidType{expected: &'static str},
21    #[error("Duplicate Argument")]
22    DuplicateArg,
23    #[error("Invalid Argument")]
24    InvalidArg,
25}
26use ErrorMsg::*;
27
28
29#[derive(Debug)]
30pub struct Error {
31    pub msg: ErrorMsg,
32    pub location: Span,
33}
34impl Error {
35    pub fn new(location: Span, msg: ErrorMsg) -> Self {
36        Self {
37            location,
38            msg
39        }
40    }
41}
42
43/// Combines an argument with a previously stored instance.
44pub trait Concat: Sized {
45    /// Determines whether a duplicate is allowed or if an error should be thrown. \
46    /// This logic is handled in the implementation for `ArgResult`. \
47    /// Typically only true for primitive types.
48    const NO_DUPLICATES: bool = true;
49
50    /// Combines an argument.
51    fn concat(&mut self, other: Self) { *self = other }
52}
53impl<T: Concat> Concat for ArgResult<T> {
54    fn concat(&mut self, other: Self) {
55        let mut other = other;
56        if other.is_found() {
57            self.location = other.location;
58            if self.is_found() && T::NO_DUPLICATES {
59                self.add_error(DuplicateArg);
60            }
61        }
62        if other.found_with_errors() {
63            self.errors.append(&mut other.errors)
64        }
65        if other.found_with_value() {    
66            match self.value {
67                Some(ref mut value) => {
68                    match other.value {
69                        Some(other_value) => {
70                            value.concat(other_value);
71                        },
72                        None => {}
73                    }
74    
75                }
76                None => self.value = other.value
77            }
78        }
79    }
80}
81
82
83
84
85/// A result type that can contain multiple errors and a value at the same time.
86#[derive(Debug)]
87pub struct ArgResult<T> {
88    pub value: Option<T>,
89    pub errors: Vec<Error>,
90    pub location: Span,
91}
92impl<T> ArgResult<T> {
93    pub fn new(location: Span) -> Self {
94        Self {
95            value: None,
96            errors: vec![],
97            location,
98        }
99    }
100    /// Adds an error using the stored Span.
101    pub fn add_error(&mut self, msg: ErrorMsg) {
102        self.errors.push(Error::new(self.location, msg));
103    }
104
105    /// Adds a value.
106    pub fn add_value(&mut self, value: T) {
107        self.value = Some(value);
108    }
109
110    /// Adds a value or an error that uses the stored Span depending on the result variant.
111    pub fn add_result(&mut self, value: Result<T, ErrorMsg>) {
112        match value {
113            Ok(value) => self.add_value(value),
114            Err(error) => self.add_error(error)
115        }
116    }
117    
118    pub fn is_found(&self) -> bool { self.errors.len() > 0 || self.value.is_some() }
119    pub fn found_with_errors(&self) -> bool { self.errors.len() > 0 }
120    pub fn found_with_value(&self) -> bool { self.value.is_some() }
121}
122
123
124/// Represents a Syn version and how it can parse attribute data into values
125pub trait SynVersion: Sized {
126    /// A type that represents an attribute.
127    type Attribute: GetSpan;
128
129    /// Parses an attribute list into a vector of its elements as metadata.
130    fn deserialize_attr_args(attr: &Self::Attribute) -> Option<Vec<Self::ArgMeta>>;
131    /// Parses a nested list into a vector of its elements as metadata.
132    fn deserialize_list_args(meta: &Self::ArgMeta) -> Option<Vec<Self::ArgMeta>>;
133
134    /// Metadata that can be used to deserialize a value.
135    type ArgMeta: GetSpan;
136
137    /// Gets the key from a key value pair as a string.
138    fn deserialize_key(meta: &Self::ArgMeta) -> Option<String>;
139    
140    /// Gets the name of an attribute list.
141    fn deserialize_attr_key(meta: &Self::Attribute) -> Option<String>;
142
143    /// Attempts to get an integer from an argument. Returns None if the argument is a different type.
144    fn deserialize_integer<T>(meta: &Self::ArgMeta) -> Option<T> where T: FromStr, T::Err: Display;
145
146    /// Attempts to get a float from an argument. Returns None if the argument is a different type.
147    fn deserialize_float<T>(meta: &Self::ArgMeta) ->  Option<T> where T: FromStr, T::Err: Display;
148
149    /// Attempts to get a string from an argument. Returns None if the argument is a different type.
150    fn deserialize_string(meta: &Self::ArgMeta) -> Option<String>;
151
152    /// Attempts to get a boolean from an argument. Returns None if the argument is a different type.
153    fn deserialize_bool(meta: &Self::ArgMeta) -> Option<bool>;
154
155    /// Attempts to get an array from an argument and returns a vector of its elements as metadata.
156    fn deserialize_array(meta: &Self::ArgMeta) -> Option<Vec<Self::ArgMeta>>;
157
158    /// A Syn Error.
159    type Error;
160
161    /// Converts this crates error into a Syn error.
162    fn convert_error(error: Error) -> Self::Error;
163}
164
165/// Gets the Span of Syn metadata
166pub trait GetSpan {
167    fn get_span(&self) -> Span;
168}
169
170
171/// A trait for deserializing Syn metadata. \
172/// Its recommended that you use the `CustomArgFromMeta` trait for deserializing simple arguments.
173pub trait TryFromMeta<V: SynVersion>: Sized {
174    /// This is the initial type and will be converted to `Self` in the `validate` function. \
175    /// For most types this is typically Self but for lists this is a builder.
176    type InitialType: Concat;
177
178    /// The metadata of an argument or an attribute.
179    type Metadata;
180    /// Looks for values & errors in the metadata. \
181    /// The returned result is added to the argument's state using the 
182    /// `concat` method on `ArgResult` which in turn will call the initial type's `Concat` implementation if found.
183    fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self::InitialType>;
184    /// Converts the initial type to Self or returns found errors. \
185    /// If the argument is required it should return a missing error. \
186    /// Wrapper types such as Option can overwrite this.
187    fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>>;
188}
189
190/// Validates a simple required type.
191pub fn required_validation<A, B: From<A>>(state: ArgResult<A>, arg_name: &'static str) -> Result<B, Vec<Error>> {
192    let mut state = state;
193    match state.found_with_errors() {
194        true => Err(state.errors),
195        false if state.value.is_none() => {
196            state.add_error(MissingArg(arg_name));
197            Err(state.errors)
198        }
199        false => Ok(state.value.unwrap().into())
200    }
201}
202
203impl Concat for String {}
204impl<V: SynVersion> TryFromMeta<V> for String {
205    type InitialType = Self;
206
207    type Metadata = V::ArgMeta;
208    fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self> {
209        let mut result = ArgResult::new(meta.get_span());
210
211        let maybe_string = V::deserialize_string(&meta);
212
213        match maybe_string {
214            Some(string) => result.add_value(string),
215            None => result.add_error(InvalidType { expected: "string" })
216        }
217
218        result
219    }
220
221    fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
222        required_validation(state, arg_name)
223    }
224}
225
226
227impl Concat for bool {}
228impl<V: SynVersion> TryFromMeta<V> for bool {
229    type InitialType = Self;
230
231    type Metadata = V::ArgMeta;
232    fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self::InitialType> {
233        let mut result = ArgResult::new(meta.get_span());
234
235        let maybe_bool = V::deserialize_bool(&meta);
236
237        match maybe_bool {
238            Some(value) => result.add_value(value),
239            None => result.add_error(InvalidType { expected: "boolean" })
240        }
241
242        result
243    }
244
245    fn validate(state: ArgResult<Self::InitialType>, _arg_name: &'static str) -> Result<Self, Vec<Error>> {
246        match state.found_with_errors() {
247            true => Err(state.errors),
248            false if state.value.is_none() => Ok(false),
249            false => Ok(state.value.unwrap())
250        }
251    }
252}
253
254impl<T: Concat> Concat for Vec<T> {
255    const NO_DUPLICATES: bool = false;
256    fn concat(&mut self, other: Self) {
257        let mut other = other;
258        self.append(&mut other);
259    }
260}
261impl<V: SynVersion, T: TryFromMeta<V, Metadata = V::ArgMeta>> TryFromMeta<V> for Vec<T> {
262    type InitialType = Vec<ArgResult<T::InitialType>>;
263    type Metadata = V::ArgMeta;
264
265    fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self::InitialType> {
266        let mut result = ArgResult::new(meta.get_span());
267        let array = 
268            match V::deserialize_array(&meta) {
269                Some(array) => array,
270                None => {
271                    result.add_error(InvalidType { expected: "array" });
272                    return result;
273                }
274            };
275
276        let mut values = vec![];
277        for meta in array {
278            let element = T::try_from_meta(meta);
279            values.push(element);
280        }
281
282        result.add_value(values);
283
284        result
285    }
286
287    fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
288        let mut state = state;
289
290        let values = 
291            match state.found_with_errors() {
292                true => return Err(state.errors),
293                false if state.value.is_none() => {
294                    state.add_error(MissingArg(arg_name));
295                    return Err(state.errors);
296                },
297                false => state.value.unwrap()
298            };
299
300        let mut y = vec![];
301        for element in values {
302            let x = 
303                match T::validate(element, arg_name) {
304                    Ok(val) => val,
305                    Err(ref mut errors) => {
306                        state.errors.append(errors);
307                        continue;
308                    }
309                };
310
311            y.push(x);
312        }
313
314        match state.errors.len() {
315            0 => Ok(y),
316            _ => Err(state.errors)
317        }
318    }
319}
320
321
322impl<V: SynVersion, T: TryFromMeta<V>> TryFromMeta<V> for Option<T> {
323    type InitialType = T::InitialType;
324
325    type Metadata = T::Metadata;
326    fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self::InitialType> {
327        T::try_from_meta(meta)
328    }
329
330    fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
331        match state.value {
332            Some(_) => Ok(Some(T::validate(state, arg_name)?)),
333            None if state.found_with_errors() => Err(state.errors),
334            None => Ok(None)
335        }
336    }
337}
338
339
340pub trait AttributeName {
341    const NAME: &'static str;
342}
343
344impl<T: AttributeName> AttributeName for Option<T> {
345    const NAME: &'static str = T::NAME;
346}
347
348impl<V: SynVersion, T: Attribute<V>> Attribute<V> for Option<T>
349where 
350    Self: TryFromMeta<V, Metadata = V::Attribute>
351{}
352
353
354
355/// Represents a struct that can be deserialized from Syn attributes.
356pub trait Attribute<V: SynVersion>: AttributeName + TryFromMeta<V, Metadata = V::Attribute> {
357    /// Creates a deserialized attribute from a list of Syn attributes.
358    fn from_attrs(location: Span, attrs: Vec<V::Attribute>) -> Result<Self, Vec<V::Error>> {
359        let mut result = ArgResult::new(location);
360
361        for attr in attrs {
362            let maybe_key = V::deserialize_attr_key(&attr);
363            let found_attribute = matches!(maybe_key, Some(key) if key == Self::NAME);
364            if found_attribute == false { continue; }
365            
366
367            let attr = Self::try_from_meta(attr);
368            result.concat(attr);
369        }
370
371        let maybe_attr = <Self as TryFromMeta<V>>::validate(result, Self::NAME);
372
373        maybe_attr.map_err(|e| e.into_iter().map(|e| V::convert_error(e)).collect())
374    }
375}
376
377
378/// A simplified version of the `TryFromMeta` trait. Types that implement this must be wrapped in the `CustomArg` struct. 
379pub trait CustomArgFromMeta<V: SynVersion>: Sized {
380    fn try_from_meta(meta: V::ArgMeta) -> Result<Self, ErrorMsg>;
381}
382
383/// Allows a type to implement `CustomArgFromMeta`, a simplified version of `TryFromMeta`.
384#[derive(Debug)]
385pub struct CustomArg<T>(pub T);
386impl<V: SynVersion, T: CustomArgFromMeta<V>> TryFromMeta<V> for CustomArg<T> {
387    type InitialType = Self;
388    type Metadata = V::ArgMeta;
389    
390    fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self::InitialType> {
391        let mut result = ArgResult::new(meta.get_span());
392        let x = <T as CustomArgFromMeta<V>>::try_from_meta(meta);
393
394        result.add_result(x);
395        
396        let v = result.value.map(|v| Self(v));
397        ArgResult { value: v, errors: result.errors, location: result.location }
398
399
400    }
401
402    fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
403        required_validation(state, arg_name)
404    }
405}
406impl<T> Concat for CustomArg<T> {}
407impl<T: Default> Default for CustomArg<T> {
408    fn default() -> Self { Self(T::default()) }
409}
410
411
412
413
414macro_rules! impl_integer {
415    ($($type_name: ident), *) => {
416        $(
417            impl Concat for $type_name {}
418            impl<V: SynVersion> TryFromMeta<V> for $type_name {
419                type InitialType = Self;
420                type Metadata = V::ArgMeta;
421                fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self::InitialType> {
422                    let mut result = ArgResult::new(meta.get_span());
423
424                    let maybe_int = V::deserialize_integer(&meta);
425
426                    match maybe_int {
427                        Some(value) => result.add_value(value),
428                        None => result.add_error(InvalidType { expected: stringify!($type_name) })
429                    }
430
431                    result
432                }
433
434                fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
435                    required_validation(state, arg_name)
436                }
437            }
438
439        )*
440    };
441}
442macro_rules! impl_float {
443    ($($type_name: ident), *) => {
444        $(
445            impl Concat for $type_name {}
446            impl<V: SynVersion> TryFromMeta<V> for $type_name {
447                type InitialType = Self;
448                type Metadata = V::ArgMeta;
449                fn try_from_meta(meta: Self::Metadata) -> ArgResult<Self::InitialType> {
450                    let mut result = ArgResult::new(meta.get_span());
451
452                    let maybe_int = V::deserialize_integer(&meta);
453
454                    match maybe_int {
455                        Some(value) => result.add_value(value),
456                        None => result.add_error(InvalidType { expected: stringify!($type_name) })
457                    }
458
459                    result
460                }
461
462                fn validate(state: ArgResult<Self::InitialType>, arg_name: &'static str) -> Result<Self, Vec<Error>> {
463                    required_validation(state, arg_name)
464                }
465            }
466        )*
467    };
468}
469
470impl_integer!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
471impl_float!(f32, f64);