1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use proc_macro2::TokenTree;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_str, Ident, Token};

/// Holds a single key value attribute, with the value being optional.
///
/// Streams in the following form will be parsed:
/// ```text
/// key = value
/// ```
///
/// The `value` is optional.
/// Thus, the following is also valid.
/// ```text
/// key
/// ```
#[cfg_attr(any(test, feature = "extra-traits"), derive(Debug))]
pub struct KeyValue {
    pub key: Ident,
    pub equal_token: Token![=],
    pub value: TokenTree,
}

/// Make KeyValue parsable from a token stream
impl Parse for KeyValue {
    fn parse(input: ParseStream) -> Result<Self> {
        let key = input.parse()?;

        // Stop if optional value is not given
        if input.is_empty() || input.peek(Token![,]) {
            return Ok(KeyValue {
                key,
                equal_token: Default::default(),
                value: parse_str("default")?,
            });
        }

        // Parse with value
        Ok(KeyValue {
            key,
            equal_token: input.parse()?,
            value: input.parse()?,
        })
    }
}

// Just for testing
#[cfg(any(test, feature = "extra-traits"))]
impl PartialEq for KeyValue {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key && format!("{}", self.value) == format!("{}", other.value)
    }
}
#[cfg(any(test, feature = "extra-traits"))]
impl Eq for KeyValue {}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq;
    use syn::parse_str;

    type Result = std::result::Result<(), Box<dyn std::error::Error>>;

    #[test]
    fn parse() -> Result {
        let actual: KeyValue = parse_str("some_key = \"value\"")?;
        let expected = KeyValue {
            key: parse_str("some_key")?,
            equal_token: Default::default(),
            value: parse_str("\"value\"")?,
        };

        assert_eq!(actual, expected);
        Ok(())
    }

    #[test]
    fn parse_missing_value() -> Result {
        let actual: KeyValue = parse_str("bool_key")?;
        let expected = KeyValue {
            key: parse_str("bool_key")?,
            equal_token: Default::default(),
            value: parse_str("default")?,
        };

        assert_eq!(actual, expected);
        Ok(())
    }

    #[test]
    fn parse_attribute_item_complex_stream() -> Result {
        let actual: KeyValue = parse_str("tmpl = {trait To {};}")?;
        let expected = KeyValue {
            key: parse_str("tmpl")?,
            equal_token: Default::default(),
            value: parse_str("{trait To {};}")?,
        };

        assert_eq!(actual, expected);
        Ok(())
    }

    // Test extra input after a value stream is ignored
    #[test]
    #[should_panic(expected = "expected token")]
    fn parse_attribute_item_complex_stream_extra() {
        parse_str::<KeyValue>("tmpl = {trait To {};}, key").unwrap();
    }

    #[test]
    #[should_panic(expected = "expected identifier")]
    fn missing_key() {
        parse_str::<KeyValue>("= true").unwrap();
    }

    #[test]
    #[should_panic(expected = "expected `=`")]
    fn missing_equal_sign() {
        parse_str::<KeyValue>("key  value").unwrap();
    }

    #[test]
    #[should_panic(expected = "expected token tree")]
    fn missing_value() {
        parse_str::<KeyValue>("key = ").unwrap();
    }
}