json_node/models/json_property_map.rs
1use std::ops::{Index, IndexMut};
2
3use crate::{models::JsonNode, errors::JsonNodeError};
4
5#[derive(Debug, Clone, PartialEq)]
6pub struct JsonPropertyMap(Vec<(String, JsonNode)>);
7
8impl JsonPropertyMap {
9 /// Create a new property map with no mappings.
10 pub fn new() -> Self {
11 Self(Vec::new())
12 }
13
14 /// Get the `JsonNode` associated with a name.
15 ///
16 /// # Arguments
17 ///
18 /// * `property_name` - The name of the property you want.
19 ///
20 /// # Examples
21 ///
22 /// ```
23 /// use json_node::{JsonNode, JsonPropertyMap};
24 ///
25 /// // Create node with mappings.
26 /// let object_node = JsonNode::Object(JsonPropertyMap::from([
27 /// ("name".to_owned(), JsonNode::String("John Doe".to_owned())),
28 /// ("age".to_owned(), JsonNode::Integer(42)),
29 /// ]));
30 ///
31 /// let map = object_node.as_object().unwrap(); // &JsonPropertyMap.
32 /// let property = map.get("name").unwrap(); // &JsonNode.
33 /// let name = property.as_string().unwrap(); // &str.
34 ///
35 /// assert_eq!(name, "John Doe");
36 /// ```
37 pub fn get(&self, property_name: &str) -> Option<&JsonNode> {
38 self.0.iter()
39 .find(|(k, _)| k == property_name)
40 .map(|(_, v)| v)
41 }
42
43 /// Get the `JsonNode` associated with a name as a mutable value.
44 ///
45 /// # Arguments
46 ///
47 /// * `property_name` - The name of the property you want.
48 ///
49 /// # Examples
50 ///
51 /// ```
52 /// use json_node::{JsonNode, JsonPropertyMap};
53 ///
54 /// // Create node with mappings.
55 /// let mut object_node = JsonNode::Object(JsonPropertyMap::from([
56 /// ("name".to_owned(), JsonNode::String("John Doe".to_owned())),
57 /// ("age".to_owned(), JsonNode::Integer(42)),
58 /// ]));
59 ///
60 /// let mut_map = object_node.as_object_mut().unwrap(); // &mut JsonPropertyMap.
61 /// let mut_property = mut_map.get_mut("name").unwrap(); // &mut JsonNode.
62 /// let mut_name = mut_property.as_string_mut().unwrap(); // &mut str.
63 ///
64 /// mut_name.make_ascii_uppercase(); // Mutates the string slice.
65 ///
66 /// let map = object_node.as_object().unwrap(); // &JsonPropertyMap.
67 /// let property = map.get("name").unwrap(); // &JsonNode.
68 /// let name = property.as_string().unwrap(); // &String.
69 ///
70 /// assert_eq!(name, "JOHN DOE");
71 /// ```
72 pub fn get_mut(&mut self, property_name: &str) -> Option<&mut JsonNode> {
73 self.0.iter_mut()
74 .find(|(k, _)| k == property_name)
75 .map(|(_, v)| v)
76 }
77
78 /// Adds a new mapping to the object.
79 ///
80 /// # Arguments
81 ///
82 /// * `property_name` - Name of the new property.
83 /// * `json_node` - The `JsonNode` to be associated with the `property_name`.
84 ///
85 /// # Examples
86 ///
87 /// ```
88 /// use json_node::{JsonNode, JsonPropertyMap};
89 ///
90 /// let mut map = JsonPropertyMap::new();
91 ///
92 /// map.add("number", JsonNode::Integer(42));
93 ///
94 /// let expected = JsonPropertyMap::from([
95 /// ("number".to_owned(), JsonNode::Integer(42))
96 /// ]);
97 ///
98 /// assert_eq!(map, expected);
99 /// ```
100 pub fn add(&mut self, property_name: &str, json_node: JsonNode) {
101 if self.contains_property(&property_name) {
102 return;
103 }
104
105 self.0.push((property_name.to_owned(), json_node));
106 }
107
108 /// Removes a mapping from the object if it exists.
109 ///
110 /// # Arguments
111 ///
112 /// * `property_name` - Name of the property to be removed.
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// use json_node::{JsonNode, JsonPropertyMap};
118 ///
119 /// let mut map = JsonPropertyMap::from([
120 /// ("number".to_owned(), JsonNode::Integer(42))
121 /// ]);
122 ///
123 /// map.remove("number");
124 ///
125 /// let expected = JsonPropertyMap::new();
126 /// assert_eq!(map, expected);
127 /// ```
128 pub fn remove(&mut self, property_name: &str) -> crate::Result<JsonNode> {
129 if self.0.iter().filter(|(k, _)| k == property_name).count() > 1 {
130 return Err(JsonNodeError::MultiplePropertiesWithSameKey(property_name.to_string()))
131 }
132
133 self.0.iter()
134 .position(|(k, _)| k == property_name)
135 .map(|i| self.0.remove(i).1)
136 .ok_or(JsonNodeError::KeyNotFound(property_name.to_string()))
137 }
138
139 /// Checks if a property with the name `property_name` exists.
140 ///
141 /// # Arguments
142 ///
143 /// * `property_name` - The name to check for.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use json_node::{JsonNode, JsonPropertyMap};
149 ///
150 /// let mut map = JsonPropertyMap::from([
151 /// ("number".to_owned(), JsonNode::Integer(42))
152 /// ]);
153 ///
154 /// assert!(map.contains_property("number"));
155 /// assert!(!map.contains_property("name"));
156 /// ```
157 pub fn contains_property(&self, property_name: &str) -> bool {
158 self.0.iter().any(|(k, _)| k == property_name)
159 }
160
161 /// Gets all property names in the object.
162 pub fn property_names(&self) -> Vec<&String> {
163 self.0.iter().map(|(k, _)| k).collect()
164 }
165
166 pub fn property_names_mut(&mut self) -> Vec<&mut String> {
167 self.0.iter_mut().map(|(k, _)| k).collect()
168 }
169
170 /// Gets all child nodes in the object.
171 pub fn nodes(&self) -> Vec<&JsonNode> {
172 self.0.iter().map(|(_, v)| v).collect()
173 }
174
175 /// Gets all child nodes in the object as mutable references.
176 pub fn nodes_mut(&mut self) -> Vec<&mut JsonNode> {
177 self.0.iter_mut().map(|(_, v)| v).collect()
178 }
179
180 /// Clears the map of all mappings.
181 pub fn clear(&mut self) {
182 self.0.clear();
183 }
184
185 /// Returns an iterator over the mappings represented as tuples.
186 pub fn iter(&self) -> std::slice::Iter<(String, JsonNode)> {
187 self.0.iter()
188 }
189
190 /// Returns an iterator over the mappings represented as tuples that allows modifying each element and its name.
191 pub fn iter_mut(&mut self) -> std::slice::IterMut<(String, JsonNode)> {
192 self.0.iter_mut()
193 }
194
195 /// Returns the number of mappings in the object.
196 pub fn len(&self) -> usize {
197 self.0.len()
198 }
199
200 /// Returns true if the object has zero mappings.
201 pub fn is_empty(&self) -> bool {
202 self.len() == 0
203 }
204
205 /// Serialized the object as a JSON object string.
206 ///
207 /// # Remarks
208 ///
209 /// This function does zero formatting meaning the JSON string will have no spaces or new-lines.
210 pub fn to_json_string(&self) -> String {
211 let mut result = "{".to_string();
212
213 for (key, value) in &self.0 {
214 result.push_str(&format!("\"{}\":{},", key, value.to_json_string()));
215 }
216
217 result.pop(); // Pops the trailing comma
218 result.push('}');
219
220 result
221 }
222}
223
224impl Index<usize> for JsonPropertyMap {
225 type Output = (String, JsonNode);
226
227 fn index(&self, index: usize) -> &Self::Output {
228 &self.0[index]
229 }
230}
231
232impl IndexMut<usize> for JsonPropertyMap {
233 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
234 &mut self.0[index]
235 }
236}
237
238impl FromIterator<(String, JsonNode)> for JsonPropertyMap {
239 fn from_iter<T: IntoIterator<Item = (String, JsonNode)>>(iter: T) -> Self {
240 Self(iter.into_iter().collect())
241 }
242}
243
244impl From<Vec<(String, JsonNode)>> for JsonPropertyMap {
245 fn from(value: Vec<(String, JsonNode)>) -> Self {
246 Self(value)
247 }
248}
249
250impl<const COUNT: usize> From<[(String, JsonNode); COUNT]> for JsonPropertyMap {
251 fn from(value: [(String, JsonNode); COUNT]) -> Self {
252 Self(value.to_vec())
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 #[test]
259 fn get_mut() {
260 use crate::{JsonNode, JsonPropertyMap};
261
262 // Create node with mappings.
263 let mut object_node = JsonNode::Object(JsonPropertyMap::from([
264 ("name".to_owned(), JsonNode::String("John Doe".to_owned())),
265 ("age".to_owned(), JsonNode::Integer(42)),
266 ]));
267
268 let mut_map = object_node.as_object_mut().unwrap(); // &mut JsonPropertyMap.
269 let mut_property = mut_map.get_mut("name").unwrap(); // &mut JsonNode.
270 let mut_name = mut_property.as_string_mut().unwrap(); // &mut JsonValue.
271
272 mut_name.make_ascii_uppercase(); // Mutates the string slice.
273
274 let map = object_node.as_object().unwrap(); // &JsonPropertyMap.
275 let property = map.get("name").unwrap(); // &JsonNode.
276 let name = property.as_string().unwrap(); // &JsonValue.
277
278 assert_eq!(name, "JOHN DOE");
279 }
280}