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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
use std::collections::HashMap;

use fnv::FnvBuildHasher;

// =============================================================================
// Value
// =============================================================================

// Types

/// The [Values] type is used as the source of content during template
/// expansion, and is a logical map of keys to typed [Value] (which may or may
/// not be present during expansion).
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Values {
    values: HashMap<String, Value, FnvBuildHasher>,
}

impl Values {
    /// Adds a new [Value] to the [Values] collection and returns the modified
    /// collection to allow for chaining of calls during construction. Values
    /// may be any type which implements `Into<Value>` - this will generally be
    /// a concrete [Value] but may be your own type for which this trait has
    /// been implemented.
    ///
    /// For clarity, it may be better to implement a suitable iterator trait for
    /// your custom type and pass it to the relevant [Value] construction
    /// function, as this will make the shape of data produced more obvious for
    /// anyone reading the code.
    #[must_use]
    pub fn add(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
        self.values.insert(key.into(), value.into());
        self
    }

    /// Gets the [Value] at the given key from the [Values] collection if it
    /// exists.
    #[must_use]
    pub fn get(&self, key: &str) -> Option<&Value> {
        self.values.get(key)
    }
}

impl FromIterator<(String, Value)> for Values {
    fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
        Self {
            values: HashMap::from_iter(iter),
        }
    }
}

/// The [Value] type is used as the source of content during template expansion,
/// as part of a [Values] collection. It maps to the three valid shapes of data
/// defined by the [RFC](https://datatracker.ietf.org/doc/html/rfc6570) (a single item,
/// a list of items, or a list of key/value pairs).
///
/// All values are of type [String] for simplicity of ownership, etc.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Value {
    /// The [`Value::AssociativeArray`] variant allows for input data to be
    /// treated as a logical map (with implicit ordering), complying with the
    /// requirements for Level 4 templates defined in
    /// [RFC6570 2.3](https://datatracker.ietf.org/doc/html/rfc6570#section-2.3).
    AssociativeArray(Vec<(String, String)>),
    /// The [`Value::Item`] variant allows for simple input data, complying with
    /// the requirements for sub-Level 4 templates defined in
    /// [RFC6570 2.3](https://datatracker.ietf.org/doc/html/rfc6570#section-2.3).
    Item(String),
    /// The [`Value::List`] variant allows for input data to be
    /// treated as a logical list (with implicit ordering), complying with the
    /// requirements for Level 4 templates defined in
    /// [RFC6570 2.3](https://datatracker.ietf.org/doc/html/rfc6570#section-2.3).
    List(Vec<String>),
}

impl Value {
    /// Constructs a new [Value] from any iterator which produces pairs (tuples)
    /// where both items implement `Into<String>`. This may be a simple array or
    /// vec, or a more complex type such as an `IndexMap`.
    ///
    /// ```
    /// # use uri_template_system_core::Value;
    /// #
    /// let expected = Value::AssociativeArray(Vec::from_iter([
    ///     (String::from("a"), String::from("1")),
    ///     (String::from("b"), String::from("2")),
    /// ]));
    ///
    /// let array = [("a", "1"), ("b", "2")];
    /// assert_eq!(expected, Value::associative_array(array));
    ///
    /// let vec = Vec::from_iter(array);
    /// assert_eq!(expected, Value::associative_array(vec));
    /// ```
    pub fn associative_array<T, U, V>(value: T) -> Self
    where
        T: IntoIterator<Item = (U, V)>,
        U: Into<String>,
        V: Into<String>,
    {
        Self::AssociativeArray(
            value
                .into_iter()
                .map(|(u, v)| (u.into(), v.into()))
                .collect(),
        )
    }

    /// Constructs a new [Value] from any type which implements `Into<String>`.
    ///
    /// ```
    /// # use uri_template_system_core::Value;
    /// #
    /// let expected = Value::Item(String::from("a"));
    ///
    /// let str = "a";
    /// assert_eq!(expected, Value::item(str));
    ///
    /// let string = String::from(str);
    /// assert_eq!(expected, Value::item(string));
    /// ```
    pub fn item<T>(value: T) -> Self
    where
        T: Into<String>,
    {
        Self::Item(value.into())
    }

    /// Constructs a new [Value] from any iterator which produces items which
    /// implement `Into<String>`, such as arrays, vecs, etc.
    ///
    /// ```
    /// # use uri_template_system_core::Value;
    /// #
    /// let expected = Value::List(Vec::from_iter([String::from("a"), String::from("b")]));
    ///
    /// let array = ["a", "b"];
    /// assert_eq!(expected, Value::list(array));
    ///
    /// let vec = Vec::from_iter(array);
    /// assert_eq!(expected, Value::list(vec));
    /// ```
    pub fn list<T, U>(value: T) -> Self
    where
        T: IntoIterator<Item = U>,
        U: Into<String>,
    {
        Self::List(value.into_iter().map(Into::into).collect())
    }
}

impl Value {
    #[must_use]
    pub(crate) fn defined(&self) -> bool {
        match self {
            Self::AssociativeArray(value) if value.is_empty() => false,
            Self::List(value) if value.is_empty() => false,
            _ => true,
        }
    }
}

// -----------------------------------------------------------------------------

// Tests

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn associative_array_value_construction() {
        let expected = Value::AssociativeArray(Vec::from_iter([
            (String::from("a"), String::from("1")),
            (String::from("b"), String::from("2")),
        ]));

        let array = [("a", "1"), ("b", "2")];
        assert_eq!(expected, Value::associative_array(array));

        let vec = Vec::from_iter(array);
        assert_eq!(expected, Value::associative_array(vec));
    }

    #[test]
    fn item_value_construction() {
        let expected = Value::Item(String::from("a"));

        let str = "a";
        assert_eq!(expected, Value::item(str));

        let string = String::from(str);
        assert_eq!(expected, Value::item(string));
    }

    #[test]
    fn list_value_construction() {
        let expected = Value::List(Vec::from_iter([String::from("a"), String::from("b")]));

        let array = ["a", "b"];
        assert_eq!(expected, Value::list(array));

        let vec = Vec::from_iter(array);
        assert_eq!(expected, Value::list(vec));
    }
}