luminos_support/
fluent.rs1use std::collections::HashMap;
2use std::ops::{Index, IndexMut};
3use serde_json::Value;
4use crate::contracts::{Jsonable, JsonSerializable, Vectorable, Arrayable};
5
6#[derive(Debug, Clone, PartialEq, Eq, Default)]
7pub struct Fluent {
8 attributes: HashMap<String, Value>,
9}
10
11impl Fluent {
12
13 pub fn new() -> Self {
17 Self {
18 attributes: HashMap::new(),
19 }
20 }
21
22 pub fn make() -> Self {
28 Self::new()
29 }
30
31 pub fn from(attributes: HashMap<String, Value>) -> Self {
35 Self { attributes }
36 }
37
38 pub fn set(&mut self, key: &str, value: impl Into<Value>) {
42 self.attributes.insert(key.to_string(), value.into());
43 }
44
45 pub fn get(&self, key: &str) -> Option<&Value> {
49 self.attributes.get(key)
50 }
51
52 pub fn get_as_str(&self, key: &str) -> Option<&str> {
56 self.attributes
57 .get(key)
58 .and_then(|value| value.as_str())
59 }
60
61 pub fn fill(&mut self, attributes: HashMap<String, Value>) -> &mut Self {
65 self.attributes.extend(attributes);
66 self
67 }
68
69 pub fn has(&self, key: &str) -> bool {
73 self.attributes.contains_key(key)
74 }
75
76 pub fn all(&self) -> &HashMap<String, Value> {
80 &self.attributes
81 }
82
83 pub fn value(&self, key: &str, default: Value) -> Value {
87 self.get(key).cloned().unwrap_or(default)
88 }
89
90 pub fn scope(&self, key: &str, default: Value) -> Self {
94 let mut map = HashMap::new();
95 map.insert(key.to_string(), self.value(key, default));
96
97 Self {
98 attributes: map
99 }
100 }
101
102 pub fn get_attributes(&self) -> &HashMap<String, Value> {
106 &self.attributes
107 }
108
109 pub fn set_from_option(&mut self, key: &str, value: Option<impl Into<Value>>) {
115 match value {
116 Some(v) => {
117 self.attributes.insert(key.to_string(), v.into());
118 },
119 None => {
120 self.attributes.insert(key.to_string(), Value::Null);
121 },
122 }
123 }
124}
125
126impl Index<&str> for Fluent {
127 type Output = Value;
128
129 fn index(&self, key: &str) -> &Self::Output {
130 self.attributes.get(key).expect("Key does not exist")
131 }
132}
133
134impl IndexMut<&str> for Fluent {
135 fn index_mut(&mut self, key: &str) -> &mut Self::Output {
136 self.attributes.get_mut(key).expect("Key does not exist")
137 }
138}
139
140impl JsonSerializable for Fluent {
141 type Value = serde_json::Value;
142
143 fn json_serialize(&self) -> Value {
144 serde_json::Value::Object(self.attributes.clone().into_iter().collect())
145 }
146}
147
148impl Jsonable for Fluent {
149 fn to_json(&self) -> String {
150 serde_json::to_string(&self.attributes).unwrap_or_else(|_| "{}".to_string())
151
152 }
153
154 fn to_json_pretty(&self) -> String {
155 serde_json::to_string_pretty(&self.attributes).unwrap_or_else(|_| "{}".to_string())
156 }
157}
158
159impl Vectorable for Fluent {
160 type T = Value;
161
162 fn to_vec(&self) -> Vec<Value> {
163 self.attributes.iter()
164 .map(|(k, v)| v.clone())
165 .collect()
166 }
167}
168
169impl Arrayable for Fluent {
170 type T = Value;
171
172 fn to_array(&self) -> Vec<Value> {
173 self.attributes.iter()
174 .map(|(k, v)| v.clone())
175 .collect()
176 }
177}
178
179
180#[cfg(test)]
181mod test {
182 use super::*;
183 use crate::fluent;
184
185 #[test]
186 fn test_fluent_macro() {
187 let result = fluent! {
188 name: "John",
189 age: 20,
190 city: "Dallas"
191 };
192
193 let mut temp: HashMap<String, Value> = std::collections::HashMap::new();
194 temp.insert("name".to_string(), "John".into());
195 temp.insert("age".to_string(), 20.into());
196 temp.insert("city".to_string(), "Dallas".into());
197
198 let expected = Fluent {
199 attributes: temp
200 };
201
202 assert_eq!(result, expected);
203 }
204
205 #[test]
206 fn can_make_new_fluent_instance() {
207 let result = Fluent::make();
208
209 let expected = Fluent {
210 attributes: HashMap::new()
211 };
212
213 assert_eq!(result, expected);
214 }
215
216 #[test]
217 fn can_make_filled_fluent_from_from() {
218 let mut map: HashMap<String, Value> = std::collections::HashMap::new();
219 map.insert("name".to_string(), "John".into());
220 map.insert("age".to_string(), 20.into());
221 map.insert("city".to_string(), "Dallas".into());
222
223 let result = Fluent::from(map.to_owned());
224
225 let expected = Fluent {
226 attributes: map
227 };
228
229 assert_eq!(result, expected);
230 }
231
232 #[test]
233 fn can_check_that_fluent_has_value_and_not_has_value() {
234 let mut map: HashMap<String, Value> = std::collections::HashMap::new();
235 map.insert("name".to_string(), "John".into());
236 map.insert("age".to_string(), 20.into());
237 map.insert("city".to_string(), "Dallas".into());
238
239 let result = Fluent::from(map.to_owned()).has("name");
240
241 assert!(result);
242
243 let result = Fluent::from(map.to_owned()).has("job");
244
245 assert!(!result);
246 }
247
248 #[test]
249 fn can_set_and_get_value_on_instance() {
250 let mut fluent= fluent! {
251 name: "John",
252 age: 20,
253 city: "Dallas"
254 };
255
256 let result = fluent.get("name").unwrap().to_owned();
257
258 let expected = Value::String("John".to_string());
259
260 assert_eq!(result, expected);
261
262 fluent.set("job", "Developer");
263
264 let result = fluent.get("job").unwrap().to_owned();
265
266 let expected = Value::String("Developer".to_string());
267
268 assert_eq!(result, expected);
269 }
270
271 #[test]
272 fn can_get_all_values_on_instance() {
273 let mut fluent= fluent! {
274 name: "John",
275 age: 20,
276 city: "Dallas"
277 };
278
279 let result = fluent.all().to_owned();
280
281 let mut map: HashMap<String, Value> = HashMap::new();
282 map.insert("name".to_string(), "John".into());
283 map.insert("age".to_string(), 20.into());
284 map.insert("city".to_string(), "Dallas".into());
285
286 assert_eq!(result, map);
287 }
288
289 #[test]
290 fn can_get_a_value_and_default_value_when_empty() {
291 let mut fluent= fluent! {
292 name: "John",
293 age: 20,
294 city: "Dallas"
295 };
296
297 let result = fluent.value("name", Value::String("Bob".to_string()));
298
299 let expected = Value::String("John".to_string());
300
301 assert_eq!(result, expected);
302
303 let result = fluent.value("job", Value::String("Developer".to_string()));
304
305 let expected = Value::String("Developer".to_string());
306
307 assert_eq!(result, expected);
308 }
309
310 #[test]
311 fn can_scope_value_to_new_fluent_instance() {
312 let mut fluent= fluent! {
313 name: "John",
314 age: 20,
315 city: "Dallas"
316 };
317
318 let result = fluent.scope("name", Value::String("Bob".to_string()));
319
320 let expected = fluent! {
321 name: "John"
322 };
323
324 assert_eq!(result, expected);
325 }
326
327
328 #[test]
329 fn can_get_all_attributes_on_instance() {
330 let mut fluent= fluent! {
331 name: "John",
332 age: 20,
333 city: "Dallas"
334 };
335
336 let result = fluent.get_attributes().to_owned();
337
338 let mut map: HashMap<String, Value> = HashMap::new();
339 map.insert("name".to_string(), "John".into());
340 map.insert("age".to_string(), 20.into());
341 map.insert("city".to_string(), "Dallas".into());
342
343 assert_eq!(result, map);
344 }
345}