despatma_lib/
annotated_type.rs

1use crate::options_attribute::OptionsAttribute;
2use syn::parse::{Parse, ParseStream, Result};
3use syn::{Token, Type};
4
5/// Holds a type that is optionally annotated with key-value options.
6///
7/// An acceptable stream will have the following form:
8/// ```text
9/// #[option1 = value1, option2 = value2]
10/// SomeType
11/// ```
12///
13/// The outer attribute (hash part) is optional.
14/// `SomeType` will be parsed to `T`.
15#[cfg_attr(any(test, feature = "extra-traits"), derive(Eq, PartialEq, Debug))]
16pub struct AnnotatedType<T = Type> {
17    pub attrs: OptionsAttribute,
18    pub inner_type: T,
19}
20
21/// Make AnnotatedType parsable from token stream
22impl<T: Parse> Parse for AnnotatedType<T> {
23    fn parse(input: ParseStream) -> Result<Self> {
24        // Parse attribute options if the next token is a hash
25        if input.peek(Token![#]) {
26            return Ok(AnnotatedType {
27                attrs: input.parse()?,
28                inner_type: input.parse()?,
29            });
30        };
31
32        // Parse without attribute options
33        Ok(AnnotatedType {
34            attrs: Default::default(),
35            inner_type: input.parse()?,
36        })
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use pretty_assertions::assert_eq;
44    use syn::{parse_quote, parse_str, TypeTraitObject};
45
46    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
47
48    #[test]
49    fn parse() -> Result {
50        let actual: AnnotatedType = parse_quote! {
51            #[no_default]
52            i32
53        };
54        let expected = AnnotatedType {
55            attrs: parse_str("#[no_default]")?,
56            inner_type: parse_str("i32")?,
57        };
58
59        assert_eq!(actual, expected);
60        Ok(())
61    }
62
63    #[test]
64    fn parse_simple_type() -> Result {
65        let actual: AnnotatedType = parse_quote! {
66            Button
67        };
68        let expected = AnnotatedType {
69            attrs: Default::default(),
70            inner_type: parse_str("Button")?,
71        };
72
73        assert_eq!(actual, expected);
74        Ok(())
75    }
76
77    #[test]
78    fn parse_trait_bounds() -> Result {
79        let actual: AnnotatedType<TypeTraitObject> = parse_quote! {
80            #[no_default]
81            dyn Button
82        };
83        let expected = AnnotatedType::<TypeTraitObject> {
84            attrs: parse_str("#[no_default]")?,
85            inner_type: parse_str("dyn Button")?,
86        };
87
88        assert_eq!(actual, expected);
89        Ok(())
90    }
91
92    #[test]
93    #[should_panic(expected = "unexpected end of input")]
94    fn missing_type() {
95        parse_str::<AnnotatedType>("#[no_default]").unwrap();
96    }
97}