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}