despatma_lib/
key_value.rs

1use proc_macro2::TokenTree;
2use syn::parse::{Parse, ParseStream, Result};
3use syn::{parse_str, Ident, Token};
4
5/// Holds a single key value attribute, with the value being optional.
6///
7/// Streams in the following form will be parsed:
8/// ```text
9/// key = value
10/// ```
11///
12/// The `value` is optional.
13/// Thus, the following is also valid.
14/// ```text
15/// key
16/// ```
17#[cfg_attr(any(test, feature = "extra-traits"), derive(Debug))]
18pub struct KeyValue {
19    pub key: Ident,
20    pub equal_token: Token![=],
21    pub value: TokenTree,
22}
23
24/// Make KeyValue parsable from a token stream
25impl Parse for KeyValue {
26    fn parse(input: ParseStream) -> Result<Self> {
27        let key = input.parse()?;
28
29        // Stop if optional value is not given
30        if input.is_empty() || input.peek(Token![,]) {
31            return Ok(KeyValue {
32                key,
33                equal_token: Default::default(),
34                value: parse_str("default")?,
35            });
36        }
37
38        // Parse with value
39        Ok(KeyValue {
40            key,
41            equal_token: input.parse()?,
42            value: input.parse()?,
43        })
44    }
45}
46
47// Just for testing
48#[cfg(any(test, feature = "extra-traits"))]
49impl PartialEq for KeyValue {
50    fn eq(&self, other: &Self) -> bool {
51        self.key == other.key && format!("{}", self.value) == format!("{}", other.value)
52    }
53}
54#[cfg(any(test, feature = "extra-traits"))]
55impl Eq for KeyValue {}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use pretty_assertions::assert_eq;
61    use syn::parse_str;
62
63    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
64
65    #[test]
66    fn parse() -> Result {
67        let actual: KeyValue = parse_str("some_key = \"value\"")?;
68        let expected = KeyValue {
69            key: parse_str("some_key")?,
70            equal_token: Default::default(),
71            value: parse_str("\"value\"")?,
72        };
73
74        assert_eq!(actual, expected);
75        Ok(())
76    }
77
78    #[test]
79    fn parse_missing_value() -> Result {
80        let actual: KeyValue = parse_str("bool_key")?;
81        let expected = KeyValue {
82            key: parse_str("bool_key")?,
83            equal_token: Default::default(),
84            value: parse_str("default")?,
85        };
86
87        assert_eq!(actual, expected);
88        Ok(())
89    }
90
91    #[test]
92    fn parse_attribute_item_complex_stream() -> Result {
93        let actual: KeyValue = parse_str("tmpl = {trait To {};}")?;
94        let expected = KeyValue {
95            key: parse_str("tmpl")?,
96            equal_token: Default::default(),
97            value: parse_str("{trait To {};}")?,
98        };
99
100        assert_eq!(actual, expected);
101        Ok(())
102    }
103
104    // Test extra input after a value stream is ignored
105    #[test]
106    #[should_panic(expected = "expected token")]
107    fn parse_attribute_item_complex_stream_extra() {
108        parse_str::<KeyValue>("tmpl = {trait To {};}, key").unwrap();
109    }
110
111    #[test]
112    #[should_panic(expected = "expected identifier")]
113    fn missing_key() {
114        parse_str::<KeyValue>("= true").unwrap();
115    }
116
117    #[test]
118    #[should_panic(expected = "expected `=`")]
119    fn missing_equal_sign() {
120        parse_str::<KeyValue>("key  value").unwrap();
121    }
122
123    #[test]
124    #[should_panic(expected = "expected token tree")]
125    fn missing_value() {
126        parse_str::<KeyValue>("key = ").unwrap();
127    }
128}