mod dyn_eq;
mod state;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;
use crate::warn_log;
pub(crate) use dyn_eq::DynEq;
pub use state::RespoState;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RespoStatesTree {
#[serde(skip)]
pub data: Option<RespoStateBranch>,
pub backup: Option<Value>,
pub cursor: Vec<Rc<str>>,
pub branches: BTreeMap<Rc<str>, Box<RespoStatesTree>>,
}
impl Hash for RespoStatesTree {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.cursor.hash(state);
self.data.hash(state);
self.branches.hash(state);
}
}
impl PartialEq for RespoStatesTree {
fn eq(&self, other: &Self) -> bool {
self.cursor == other.cursor && self.data == other.data && self.branches == other.branches
}
}
impl Eq for RespoStatesTree {}
impl RespoStatesTree {
pub fn path(&self) -> Vec<Rc<str>> {
self.cursor.to_owned()
}
pub fn cast_branch<T>(&self) -> Rc<T>
where
T: Clone + Default + RespoState + 'static,
{
if let Some(v) = &self.data {
if let Some(v) = v.0.as_ref().as_any().downcast_ref::<T>() {
return Rc::new(v.to_owned());
} else {
warn_log!("failed to cast state to {} , at {:?}", std::any::type_name::<T>(), self.cursor);
}
}
match &self.backup {
Some(v) => {
let mut t = T::default();
if let Err(e) = t.restore_from(v) {
warn_log!("failed to restore from backup: {} , at {:?}", e, self.cursor);
}
Rc::new(t)
}
None => Rc::new(T::default()),
}
}
pub fn pick(&self, name: &str) -> RespoStatesTree {
let mut next_cursor = self.cursor.to_owned();
next_cursor.push(Rc::from(name));
if self.branches.contains_key(name) {
let prev = &self.branches[name];
Self {
data: prev.data.to_owned(),
backup: prev.backup.to_owned(),
cursor: next_cursor,
branches: prev.branches.to_owned(),
}
} else {
Self {
data: None,
backup: None,
cursor: next_cursor,
branches: BTreeMap::new(),
}
}
}
pub(crate) fn set_in_mut(&mut self, change: RespoUpdateState) {
if change.cursor.is_empty() {
change.data.clone_into(&mut self.data);
change.backup.clone_into(&mut self.backup);
} else {
let (p_head, p_rest) = change.cursor.split_at(1);
let p0 = &p_head[0];
if let Some(branch) = self.branches.get_mut(p0) {
branch.set_in_mut(RespoUpdateState {
cursor: p_rest.to_vec(),
..change
});
} else {
let mut branch = self.pick(p0);
branch.set_in_mut(RespoUpdateState {
cursor: p_rest.to_vec(),
..change
});
self.branches.insert(p0.to_owned(), Box::new(branch));
}
}
}
}
#[derive(Debug, Clone)]
pub struct RespoStateBranch(pub Rc<dyn DynEq>);
impl PartialEq for RespoStateBranch {
fn eq(&self, other: &Self) -> bool {
self.0.as_ref().do_eq(other.0.as_ref())
}
}
impl Eq for RespoStateBranch {}
impl Hash for RespoStateBranch {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_usize(Rc::as_ptr(&self.0) as *const () as usize);
}
}
impl RespoStateBranch {
pub fn new(state: Rc<dyn DynEq>) -> Self {
Self(state)
}
}
#[derive(Clone, Debug)]
pub struct RespoUpdateState {
pub cursor: Vec<Rc<str>>,
pub data: Option<RespoStateBranch>,
pub backup: Option<Value>,
}