use std::collections::HashMap;
use std::ops::{Index, IndexMut};
use serde_json::Value;
use crate::contracts::{Jsonable, JsonSerializable, Vectorable, Arrayable};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Fluent {
attributes: HashMap<String, Value>,
}
impl Fluent {
pub fn new() -> Self {
Self {
attributes: HashMap::new(),
}
}
pub fn make() -> Self {
Self::new()
}
pub fn from(attributes: HashMap<String, Value>) -> Self {
Self { attributes }
}
pub fn set(&mut self, key: &str, value: impl Into<Value>) {
self.attributes.insert(key.to_string(), value.into());
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.attributes.get(key)
}
pub fn get_as_str(&self, key: &str) -> Option<&str> {
self.attributes
.get(key)
.and_then(|value| value.as_str())
}
pub fn fill(&mut self, attributes: HashMap<String, Value>) -> &mut Self {
self.attributes.extend(attributes);
self
}
pub fn has(&self, key: &str) -> bool {
self.attributes.contains_key(key)
}
pub fn all(&self) -> &HashMap<String, Value> {
&self.attributes
}
pub fn value(&self, key: &str, default: Value) -> Value {
self.get(key).cloned().unwrap_or(default)
}
pub fn scope(&self, key: &str, default: Value) -> Self {
let mut map = HashMap::new();
map.insert(key.to_string(), self.value(key, default));
Self {
attributes: map
}
}
pub fn get_attributes(&self) -> &HashMap<String, Value> {
&self.attributes
}
pub fn set_from_option(&mut self, key: &str, value: Option<impl Into<Value>>) {
match value {
Some(v) => {
self.attributes.insert(key.to_string(), v.into());
},
None => {
self.attributes.insert(key.to_string(), Value::Null);
},
}
}
}
impl Index<&str> for Fluent {
type Output = Value;
fn index(&self, key: &str) -> &Self::Output {
self.attributes.get(key).expect("Key does not exist")
}
}
impl IndexMut<&str> for Fluent {
fn index_mut(&mut self, key: &str) -> &mut Self::Output {
self.attributes.get_mut(key).expect("Key does not exist")
}
}
impl JsonSerializable for Fluent {
type Value = serde_json::Value;
fn json_serialize(&self) -> Value {
serde_json::Value::Object(self.attributes.clone().into_iter().collect())
}
}
impl Jsonable for Fluent {
fn to_json(&self) -> String {
serde_json::to_string(&self.attributes).unwrap_or_else(|_| "{}".to_string())
}
fn to_json_pretty(&self) -> String {
serde_json::to_string_pretty(&self.attributes).unwrap_or_else(|_| "{}".to_string())
}
}
impl Vectorable for Fluent {
type T = Value;
fn to_vec(&self) -> Vec<Value> {
self.attributes.iter()
.map(|(k, v)| v.clone())
.collect()
}
}
impl Arrayable for Fluent {
type T = Value;
fn to_array(&self) -> Vec<Value> {
self.attributes.iter()
.map(|(k, v)| v.clone())
.collect()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::fluent;
#[test]
fn test_fluent_macro() {
let result = fluent! {
name: "John",
age: 20,
city: "Dallas"
};
let mut temp: HashMap<String, Value> = std::collections::HashMap::new();
temp.insert("name".to_string(), "John".into());
temp.insert("age".to_string(), 20.into());
temp.insert("city".to_string(), "Dallas".into());
let expected = Fluent {
attributes: temp
};
assert_eq!(result, expected);
}
#[test]
fn can_make_new_fluent_instance() {
let result = Fluent::make();
let expected = Fluent {
attributes: HashMap::new()
};
assert_eq!(result, expected);
}
#[test]
fn can_make_filled_fluent_from_from() {
let mut map: HashMap<String, Value> = std::collections::HashMap::new();
map.insert("name".to_string(), "John".into());
map.insert("age".to_string(), 20.into());
map.insert("city".to_string(), "Dallas".into());
let result = Fluent::from(map.to_owned());
let expected = Fluent {
attributes: map
};
assert_eq!(result, expected);
}
#[test]
fn can_check_that_fluent_has_value_and_not_has_value() {
let mut map: HashMap<String, Value> = std::collections::HashMap::new();
map.insert("name".to_string(), "John".into());
map.insert("age".to_string(), 20.into());
map.insert("city".to_string(), "Dallas".into());
let result = Fluent::from(map.to_owned()).has("name");
assert!(result);
let result = Fluent::from(map.to_owned()).has("job");
assert!(!result);
}
#[test]
fn can_set_and_get_value_on_instance() {
let mut fluent= fluent! {
name: "John",
age: 20,
city: "Dallas"
};
let result = fluent.get("name").unwrap().to_owned();
let expected = Value::String("John".to_string());
assert_eq!(result, expected);
fluent.set("job", "Developer");
let result = fluent.get("job").unwrap().to_owned();
let expected = Value::String("Developer".to_string());
assert_eq!(result, expected);
}
#[test]
fn can_get_all_values_on_instance() {
let mut fluent= fluent! {
name: "John",
age: 20,
city: "Dallas"
};
let result = fluent.all().to_owned();
let mut map: HashMap<String, Value> = HashMap::new();
map.insert("name".to_string(), "John".into());
map.insert("age".to_string(), 20.into());
map.insert("city".to_string(), "Dallas".into());
assert_eq!(result, map);
}
#[test]
fn can_get_a_value_and_default_value_when_empty() {
let mut fluent= fluent! {
name: "John",
age: 20,
city: "Dallas"
};
let result = fluent.value("name", Value::String("Bob".to_string()));
let expected = Value::String("John".to_string());
assert_eq!(result, expected);
let result = fluent.value("job", Value::String("Developer".to_string()));
let expected = Value::String("Developer".to_string());
assert_eq!(result, expected);
}
#[test]
fn can_scope_value_to_new_fluent_instance() {
let mut fluent= fluent! {
name: "John",
age: 20,
city: "Dallas"
};
let result = fluent.scope("name", Value::String("Bob".to_string()));
let expected = fluent! {
name: "John"
};
assert_eq!(result, expected);
}
#[test]
fn can_get_all_attributes_on_instance() {
let mut fluent= fluent! {
name: "John",
age: 20,
city: "Dallas"
};
let result = fluent.get_attributes().to_owned();
let mut map: HashMap<String, Value> = HashMap::new();
map.insert("name".to_string(), "John".into());
map.insert("age".to_string(), 20.into());
map.insert("city".to_string(), "Dallas".into());
assert_eq!(result, map);
}
}