uri_template_system_core/
value.rs

1use std::collections::HashMap;
2
3use fnv::FnvBuildHasher;
4
5// =============================================================================
6// Value
7// =============================================================================
8
9// Types
10
11/// The [`Values`] type is used as the source of content during template
12/// expansion, and is a logical map of keys to typed [`Value`] (which may or may
13/// not be present during expansion).
14#[derive(Clone, Debug, Default, Eq, PartialEq)]
15pub struct Values {
16    values: HashMap<String, Value, FnvBuildHasher>,
17}
18
19impl Values {
20    /// Adds a new [`Value`] to the [`Values`] collection and returns the
21    /// modified collection to allow for chaining of calls during
22    /// construction. Values may be any type which implements `Into<Value>`
23    /// - this will generally be a concrete [`Value`] but may be your own
24    /// type for which this trait has been implemented.
25    ///
26    /// For clarity, it may be better to implement a suitable iterator trait for
27    /// your custom type and pass it to the relevant [`Value`] construction
28    /// function, as this will make the shape of data produced more obvious for
29    /// anyone reading the code.
30    #[must_use]
31    pub fn add(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
32        self.values.insert(key.into(), value.into());
33        self
34    }
35
36    /// Gets the [`Value`] at the given key from the [`Values`] collection if it
37    /// exists.
38    #[must_use]
39    pub fn get(&self, key: &str) -> Option<&Value> {
40        self.values.get(key)
41    }
42}
43
44impl FromIterator<(String, Value)> for Values {
45    fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
46        Self {
47            values: HashMap::from_iter(iter),
48        }
49    }
50}
51
52/// The [`Value`] type is used as the source of content during template
53/// expansion, as part of a [`Values`] collection. It maps to the three valid
54/// shapes of data defined by the [RFC](https://datatracker.ietf.org/doc/html/rfc6570) (a single item,
55/// a list of items, or a list of key/value pairs).
56///
57/// All values are of type [String] for simplicity of ownership, etc.
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub enum Value {
60    /// The [`Value::AssociativeArray`] variant allows for input data to be
61    /// treated as a logical map (with implicit ordering), complying with the
62    /// requirements for Level 4 templates defined in
63    /// [RFC6570 2.3](https://datatracker.ietf.org/doc/html/rfc6570#section-2.3).
64    AssociativeArray(Vec<(String, String)>),
65    /// The [`Value::Item`] variant allows for simple input data, complying with
66    /// the requirements for sub-Level 4 templates defined in
67    /// [RFC6570 2.3](https://datatracker.ietf.org/doc/html/rfc6570#section-2.3).
68    Item(String),
69    /// The [`Value::List`] variant allows for input data to be
70    /// treated as a logical list (with implicit ordering), complying with the
71    /// requirements for Level 4 templates defined in
72    /// [RFC6570 2.3](https://datatracker.ietf.org/doc/html/rfc6570#section-2.3).
73    List(Vec<String>),
74    /// The [`Value::Undefined`] variant allows for input data to be explicitly
75    /// given as undefined. This is sometimes useful rather than simply omitting
76    /// the data.
77    Undefined,
78}
79
80impl Value {
81    /// Constructs a new [`Value`] from any iterator which produces pairs
82    /// (tuples) where both items implement `Into<String>`. This may be a
83    /// simple array or vec, or a more complex type such as an `IndexMap`.
84    ///
85    /// ```
86    /// # use uri_template_system_core::Value;
87    /// #
88    /// let expected = Value::AssociativeArray(Vec::from_iter([
89    ///     (String::from("a"), String::from("1")),
90    ///     (String::from("b"), String::from("2")),
91    /// ]));
92    ///
93    /// let array = [("a", "1"), ("b", "2")];
94    /// assert_eq!(expected, Value::associative_array(array));
95    ///
96    /// let vec = Vec::from_iter(array);
97    /// assert_eq!(expected, Value::associative_array(vec));
98    /// ```
99    pub fn associative_array<T, U, V>(value: T) -> Self
100    where
101        T: IntoIterator<Item = (U, V)>,
102        U: Into<String>,
103        V: Into<String>,
104    {
105        Self::AssociativeArray(
106            value
107                .into_iter()
108                .map(|(u, v)| (u.into(), v.into()))
109                .collect(),
110        )
111    }
112
113    /// Constructs a new [`Value`] from any type which implements
114    /// `Into<String>`.
115    ///
116    /// ```
117    /// # use uri_template_system_core::Value;
118    /// #
119    /// let expected = Value::Item(String::from("a"));
120    ///
121    /// let str = "a";
122    /// assert_eq!(expected, Value::item(str));
123    ///
124    /// let string = String::from(str);
125    /// assert_eq!(expected, Value::item(string));
126    /// ```
127    pub fn item<T>(value: T) -> Self
128    where
129        T: Into<String>,
130    {
131        Self::Item(value.into())
132    }
133
134    /// Constructs a new [`Value`] from any iterator which produces items which
135    /// implement `Into<String>`, such as arrays, vecs, etc.
136    ///
137    /// ```
138    /// # use uri_template_system_core::Value;
139    /// #
140    /// let expected = Value::List(Vec::from_iter([String::from("a"), String::from("b")]));
141    ///
142    /// let array = ["a", "b"];
143    /// assert_eq!(expected, Value::list(array));
144    ///
145    /// let vec = Vec::from_iter(array);
146    /// assert_eq!(expected, Value::list(vec));
147    /// ```
148    pub fn list<T, U>(value: T) -> Self
149    where
150        T: IntoIterator<Item = U>,
151        U: Into<String>,
152    {
153        Self::List(value.into_iter().map(Into::into).collect())
154    }
155}
156
157impl Value {
158    #[must_use]
159    pub(crate) fn defined(&self) -> bool {
160        match self {
161            Self::AssociativeArray(value) if value.is_empty() => false,
162            Self::List(value) if value.is_empty() => false,
163            Self::Undefined => false,
164            _ => true,
165        }
166    }
167}
168
169// -----------------------------------------------------------------------------
170
171// Tests
172
173#[cfg(test)]
174mod tests {
175
176    use super::*;
177
178    #[test]
179    fn associative_array_value_construction() {
180        let expected = Value::AssociativeArray(Vec::from_iter([
181            (String::from("a"), String::from("1")),
182            (String::from("b"), String::from("2")),
183        ]));
184
185        let array = [("a", "1"), ("b", "2")];
186        assert_eq!(expected, Value::associative_array(array));
187
188        let vec = Vec::from_iter(array);
189        assert_eq!(expected, Value::associative_array(vec));
190    }
191
192    #[test]
193    fn item_value_construction() {
194        let expected = Value::Item(String::from("a"));
195
196        let str = "a";
197        assert_eq!(expected, Value::item(str));
198
199        let string = String::from(str);
200        assert_eq!(expected, Value::item(string));
201    }
202
203    #[test]
204    fn list_value_construction() {
205        let expected = Value::List(Vec::from_iter([String::from("a"), String::from("b")]));
206
207        let array = ["a", "b"];
208        assert_eq!(expected, Value::list(array));
209
210        let vec = Vec::from_iter(array);
211        assert_eq!(expected, Value::list(vec));
212    }
213}