use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::oplog::OpLogEntry;
use crate::session::SessionOp;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StateDoc {
pub format_version: u32,
pub fields: BTreeMap<String, StateField>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StateField {
#[serde(rename = "type")]
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial: Option<Value>,
}
impl StateField {
pub fn initial_value(&self) -> Value {
self.initial
.clone()
.unwrap_or_else(|| match self.kind.as_str() {
"int" | "float" => Value::from(0),
"bool" => Value::from(false),
"string" => Value::from(""),
"map" => serde_json::json!({}),
"list" => serde_json::json!([]),
_ => Value::Null,
})
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct StateFold {
pub values: BTreeMap<String, Value>,
pub undeclared: Vec<String>,
}
pub fn fold_state(state_doc: &StateDoc, entries: &[OpLogEntry]) -> StateFold {
let mut values: BTreeMap<String, Value> = state_doc
.fields
.iter()
.map(|(name, field)| (name.clone(), field.initial_value()))
.collect();
let mut undeclared: Vec<String> = Vec::new();
for entry in entries {
for op in &entry.ops {
let SessionOp::State(record) = op else {
continue;
};
for (key, value) in &record.state {
if state_doc.fields.contains_key(key) {
match value {
Value::Null => {
values.insert(key.clone(), state_doc.fields[key].initial_value());
}
v => {
values.insert(key.clone(), v.clone());
}
}
continue;
}
if let Some((base, inner)) = key.split_once('.')
&& state_doc.fields.get(base).map(|f| f.kind.as_str()) == Some("map")
&& let Some(Value::Object(map)) = values.get_mut(base)
{
match value {
Value::Null => {
map.remove(inner);
}
v => {
map.insert(inner.to_string(), v.clone());
}
}
continue;
}
match value {
Value::Null => {
values.remove(key);
}
v => {
values.insert(key.clone(), v.clone());
if !undeclared.contains(key) {
undeclared.push(key.clone());
}
}
}
}
}
}
StateFold { values, undeclared }
}