use crate::error::{Error, Result};
use serde::{de::DeserializeOwned, Serialize};
use serde_json::Value;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Mutex;
pub trait SyncState: Serialize + DeserializeOwned + Clone + Send + Sync + 'static {
const NAME: &'static str;
}
trait StateSlice: Send + Sync {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn to_json(&self) -> Result<Value>;
fn replace_from_json(&mut self, value: Value) -> Result<()>;
}
impl<S: SyncState> StateSlice for S {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn to_json(&self) -> Result<Value> {
serde_json::to_value(self).map_err(|e| Error::Serialize(e.to_string()))
}
fn replace_from_json(&mut self, value: Value) -> Result<()> {
*self = serde_json::from_value(value).map_err(|e| Error::Deserialize(e.to_string()))?;
Ok(())
}
}
pub struct StateRegistry {
slices: Mutex<HashMap<&'static str, Box<dyn StateSlice>>>,
}
impl StateRegistry {
pub fn new() -> Self {
Self {
slices: Mutex::new(HashMap::new()),
}
}
pub fn insert<S: SyncState>(&self, value: S) -> Result<()> {
let mut map = self.slices.lock().map_err(|_| Error::LockPoisoned)?;
if map.contains_key(S::NAME) {
return Err(Error::DuplicateRegistration(S::NAME.to_owned()));
}
map.insert(S::NAME, Box::new(value));
Ok(())
}
pub fn read<S: SyncState>(&self) -> Result<S> {
let map = self.slices.lock().map_err(|_| Error::LockPoisoned)?;
let slice = map
.get(S::NAME)
.ok_or_else(|| Error::NotFound(S::NAME.to_owned()))?;
slice
.as_any()
.downcast_ref::<S>()
.cloned()
.ok_or_else(|| Error::TypeMismatch(S::NAME.to_owned()))
}
pub fn mutate<S: SyncState>(&self, f: impl FnOnce(&mut S)) -> Result<S> {
let mut map = self.slices.lock().map_err(|_| Error::LockPoisoned)?;
let slice = map
.get_mut(S::NAME)
.ok_or_else(|| Error::NotFound(S::NAME.to_owned()))?;
let value = slice
.as_any_mut()
.downcast_mut::<S>()
.ok_or_else(|| Error::TypeMismatch(S::NAME.to_owned()))?;
f(value);
Ok(value.clone())
}
pub fn get_json(&self, name: &str) -> Result<Value> {
let map = self.slices.lock().map_err(|_| Error::LockPoisoned)?;
map.get(name)
.ok_or_else(|| Error::NotFound(name.to_owned()))?
.to_json()
}
pub fn set_json(&self, name: &str, value: Value) -> Result<()> {
let mut map = self.slices.lock().map_err(|_| Error::LockPoisoned)?;
map.get_mut(name)
.ok_or_else(|| Error::NotFound(name.to_owned()))?
.replace_from_json(value)
}
pub fn contains(&self, name: &str) -> bool {
self.slices
.lock()
.map(|m| m.contains_key(name))
.unwrap_or(false)
}
}
impl Default for StateRegistry {
fn default() -> Self {
Self::new()
}
}