Skip to main content

luminos_support/
fluent.rs

1use 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    /// 
14    /// Create a new fluent instance.
15    /// 
16    pub fn new() -> Self {
17        Self {
18            attributes: HashMap::new(),
19        }
20    }
21    
22    ///
23    /// Create a new fluent instance.
24    /// 
25    /// Proxies to Fluent::new()
26    /// 
27    pub fn make() -> Self {
28        Self::new() 
29    }
30
31    ///
32    /// Create a fluent instance from the given attributes.
33    /// 
34    pub fn from(attributes: HashMap<String, Value>) -> Self {
35        Self { attributes }
36    }
37
38    ///
39    /// Set an attribute on the fluent instance using "dot" notation.
40    /// 
41    pub fn set(&mut self, key: &str, value: impl Into<Value>) {
42        self.attributes.insert(key.to_string(), value.into());
43    }
44
45    ///
46    /// Get an attribute from the fluent instance using "dot" notation.
47    /// 
48    pub fn get(&self, key: &str) -> Option<&Value> {
49        self.attributes.get(key)
50    }
51
52    ///
53    /// Get a value off the instance and cast to str.
54    /// 
55    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    ///
62    /// Fill the fluent instance with the attributes.
63    /// 
64    pub fn fill(&mut self, attributes: HashMap<String, Value>) -> &mut Self {
65        self.attributes.extend(attributes);
66        self
67    }
68
69    /// 
70    /// Does the "key" exist on the fluent instance?
71    /// 
72    pub fn has(&self, key: &str) -> bool {
73        self.attributes.contains_key(key)
74    }
75
76    ///
77    /// Get all attributes of the fluent instance
78    /// 
79    pub fn all(&self) -> &HashMap<String, Value> {
80        &self.attributes
81    }
82
83    ///
84    /// Get and attribute from the fluent instance.
85    /// 
86    pub fn value(&self, key: &str, default: Value) -> Value {
87        self.get(key).cloned().unwrap_or(default)
88    }
89
90    ///
91    /// Wrap the value of key in a new fluent instance.
92    /// 
93    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    ///
103    /// Get the attributes on the fluent instance.
104    /// 
105    pub fn get_attributes(&self) -> &HashMap<String, Value> {
106        &self.attributes
107    }
108
109    ///
110    /// Insert an optional value into the Fluent instance.
111    /// If the value is Some, insert it as-is.
112    /// If None, insert serde_json::Value::Null.
113    ///
114    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}