use super::common::{JsonValue, StringValue};
use serde::Serialize;
use std::collections::HashMap;
use std::convert::Into;
pub(crate) type UpdatesSchemaSet = HashMap<StringValue, JsonValue>;
pub(crate) type UpdatesSchemaIncrement = HashMap<StringValue, f64>;
pub(crate) type UpdatesSchemaAppend = HashMap<StringValue, Vec<JsonValue>>;
pub(crate) type UpdatesSchemaPrepend = HashMap<StringValue, Vec<JsonValue>>;
pub(crate) type UpdatesSchemaDelete = Vec<StringValue>;
#[derive(Serialize, Debug, PartialEq)]
pub(crate) struct UpdatesSchema {
set: Option<UpdatesSchemaSet>,
increment: Option<UpdatesSchemaIncrement>,
append: Option<UpdatesSchemaAppend>,
prepend: Option<UpdatesSchemaPrepend>,
delete: Option<UpdatesSchemaDelete>,
}
impl<'a> UpdatesSchema {
fn new() -> Self {
Self {
set: None,
increment: None,
append: None,
prepend: None,
delete: None,
}
}
}
#[derive(Debug, Clone)]
pub enum Action {
Set(JsonValue),
Increment(f64),
Append(Vec<JsonValue>),
Prepend(Vec<JsonValue>),
Delete,
}
impl Action {
pub fn set<T>(value: T) -> serde_json::Result<Self>
where
T: Serialize,
{
let serde_value = serde_json::to_value(value)?;
Ok(Self::Set(serde_value))
}
pub fn increment<T>(value: T) -> Self
where
T: Into<f64>,
{
Self::Increment(value.into())
}
pub fn append<T>(value: T) -> serde_json::Result<Self>
where
T: Serialize,
{
let serde_value = serde_json::to_value(value)?;
Ok(Self::Append(vec![serde_value]))
}
pub fn append_many<T>(value: &[T]) -> serde_json::Result<Self>
where
T: Serialize,
{
let mut serde_value = serde_json::to_value(value)?;
let serde_value_array: Vec<JsonValue> = serde_value
.as_array_mut()
.unwrap() .iter_mut()
.map(|item| item.take())
.collect();
Ok(Self::Append(serde_value_array))
}
pub fn prepend<T>(value: T) -> serde_json::Result<Self>
where
T: Serialize,
{
let serde_value = serde_json::to_value(value)?;
Ok(Self::Prepend(vec![serde_value]))
}
pub fn prepend_many<T>(value: &[T]) -> serde_json::Result<Self>
where
T: Serialize,
{
let mut serde_value = serde_json::to_value(value)?;
let serde_value_array: Vec<JsonValue> = serde_value
.as_array_mut()
.unwrap() .iter_mut()
.map(|item| item.take())
.collect();
Ok(Self::Prepend(serde_value_array))
}
pub fn delete() -> Self {
Self::Delete
}
pub(crate) fn render<'a>(
self,
key: StringValue,
mut target: UpdatesSchema,
) -> serde_json::Result<UpdatesSchema> {
match self {
Self::Set(set_value) => {
if let None = target.set {
target.set = Some(HashMap::new());
}
if let Some(value) = &mut target.set {
value.insert(key, set_value);
}
}
Self::Increment(increment_value) => {
if let None = target.increment {
target.increment = Some(HashMap::new());
}
if let Some(value) = &mut target.increment {
value.insert(key, increment_value);
}
}
Self::Append(append_value) => {
if let None = target.append {
target.append = Some(HashMap::new());
}
if let Some(value) = &mut target.append {
value.insert(key, append_value);
}
}
Self::Prepend(prepend_value) => {
if let None = target.prepend {
target.prepend = Some(HashMap::new());
}
if let Some(value) = &mut target.prepend {
value.insert(key, prepend_value);
}
}
Self::Delete => {
if let None = target.delete {
target.delete = Some(vec![]);
}
if let Some(value) = &mut target.delete {
value.push(key);
}
}
};
Ok(target)
}
}
impl From<Action> for serde_json::Result<Action> {
fn from(action: Action) -> serde_json::Result<Action> {
Ok(action)
}
}
type PartialActions = Vec<(StringValue, serde_json::Result<Action>)>;
pub struct Updates {
actions: PartialActions,
}
impl Updates {
pub fn init() -> Self {
Self {
actions: Vec::new(),
}
}
pub fn add<T, D>(mut self, attr: T, action: D) -> Self
where
T: Into<StringValue>,
D: Into<serde_json::Result<Action>>,
{
self.actions.push((attr.into(), action.into()));
self
}
pub(crate) fn render(self) -> serde_json::Result<JsonValue> {
let mut target = UpdatesSchema::new();
for (k, v) in self.actions {
target = v?.render(k, target)?;
}
let target_json = serde_json::to_value(target)?;
Ok(target_json)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn render_for_general_updates() {
let target = Updates::init()
.add("profile.age", Action::set(33))
.add("profile.active", Action::set(true))
.add("profile.email", Action::set("jimmy@deta.sh"))
.add("purchases", Action::increment(2))
.add("likes", Action::append_many(&["ramen", "jimmy"]))
.add("watchers", Action::prepend_many(&["mark"]))
.add("profile.hometown", Action::delete())
.add("count", Action::increment(1))
.add("age", Action::delete())
.add("clients", Action::append("jacob"))
.add("fans", Action::prepend("alex"))
.render()
.expect("Render failed");
let expected_target = serde_json::json!({
"set": {
"profile.active": true,
"profile.age": 33,
"profile.email": "jimmy@deta.sh"
},
"increment": {
"count": 1.,
"purchases": 2.,
},
"append": {
"likes": ["ramen", "jimmy"],
"clients": ["jacob"]
},
"prepend": {
"watchers": ["mark"],
"fans": ["alex"]
},
"delete": ["profile.hometown", "age"]
});
assert_eq!(target, expected_target)
}
#[test]
fn render_for_overrides() {
let target = Updates::init()
.add("profile.age", Action::set(33))
.add("count", Action::set(7))
.add("likes", Action::prepend_many(&["tom", "adam"]))
.add("likes", Action::prepend("julie"))
.add("profile.age", Action::set(57))
.add("count", Action::increment(8))
.render()
.expect("Render failed");
let expected_target = serde_json::json!({
"set": {
"count": 7,
"profile.age": 57
},
"increment": {
"count": 8.,
},
"append": null,
"prepend": {
"likes": ["julie"]
},
"delete": null
});
assert_eq!(target, expected_target);
}
}