use std::any::Any;
use arcstr::{ArcStr, literal};
use rustc_hash::FxHashSet;
use serde::{ser::Error, Deserialize, Serialize};
use crate::model::{StofDataContainer, DataRef, NodeRef, SId, StofData};
pub const INVALID_DATA_NAME: ArcStr = literal!("name");
pub const INVALID_DATA_NODES: ArcStr = literal!("nodes");
pub const INVALID_DATA_VALUE: ArcStr = literal!("value");
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Data {
pub id: DataRef,
pub name: SId,
pub nodes: FxHashSet<NodeRef>,
#[serde(deserialize_with = "deserialize_data_field")]
#[serde(serialize_with = "serialize_data_field")]
pub data: Box<dyn StofData>,
#[serde(skip)]
pub dirty: FxHashSet<ArcStr>,
}
impl From<Box<dyn StofData>> for Data {
fn from(value: Box<dyn StofData>) -> Self {
let id = SId::default();
Self::new(id.clone(), id, value)
}
}
impl Data {
pub fn new(id: DataRef, name: SId, data: Box<dyn StofData>) -> Self {
Self {
id,
name,
nodes: Default::default(),
data,
dirty: Default::default(),
}
}
#[inline(always)]
pub fn invalidate(&mut self, symbol: ArcStr) -> bool {
self.dirty.insert(symbol)
}
#[inline(always)]
pub fn invalidate_name(&mut self) -> bool {
self.invalidate(INVALID_DATA_NAME)
}
#[inline(always)]
pub fn invalidate_nodes(&mut self) -> bool {
self.invalidate(INVALID_DATA_NODES)
}
#[inline(always)]
pub fn invalidate_value(&mut self) -> bool {
self.invalidate(INVALID_DATA_VALUE)
}
#[inline(always)]
pub fn validate(&mut self, symbol: &ArcStr) -> bool {
self.dirty.remove(symbol)
}
#[inline(always)]
pub fn validate_name(&mut self) -> bool {
self.validate(&INVALID_DATA_NAME)
}
#[inline(always)]
pub fn validate_nodes(&mut self) -> bool {
self.validate(&INVALID_DATA_NODES)
}
#[inline(always)]
pub fn validate_value(&mut self) -> bool {
self.validate(&INVALID_DATA_VALUE)
}
#[inline]
pub fn validate_clear(&mut self) -> bool {
let res = self.dirty.len() > 0;
self.dirty.clear();
res
}
#[inline(always)]
pub fn dirty(&self, symbol: &ArcStr) -> bool {
self.dirty.contains(symbol)
}
#[inline(always)]
pub fn any_dirty(&self) -> bool {
self.dirty.len() > 0
}
#[inline]
pub fn set_name(&mut self, name: SId) -> bool {
if name != self.name {
self.name = name;
self.invalidate_name();
true
} else {
false
}
}
#[inline]
pub(crate) fn node_added(&mut self, node: NodeRef) -> bool {
if self.nodes.insert(node) {
self.invalidate_nodes();
true
} else {
false
}
}
#[inline]
pub(crate) fn node_removed(&mut self, node: &NodeRef) -> bool {
if self.nodes.remove(node) {
self.invalidate_nodes();
true
} else {
false
}
}
#[inline(always)]
pub fn ref_count(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn is_type<T: Any>(&self) -> bool {
if let Some(_) = self.get::<T>() {
true
} else {
false
}
}
#[inline]
pub fn set(&mut self, data: Box<dyn StofData>) {
self.data = data;
self.invalidate_value();
}
pub fn get<T: Any>(&self) -> Option<&T> {
let any = self.data.as_dyn_any();
if let Some(data) = any.downcast_ref::<T>() {
Some(data)
} else {
None
}
}
pub fn get_mut<T: Any>(&mut self) -> Option<&mut T> {
let any = self.data.as_mut_dyn_any();
if let Some(data) = any.downcast_mut::<T>() {
Some(data)
} else {
None
}
}
#[inline(always)]
pub fn tagname(&self) -> String {
self.data.typetag_name().to_string()
}
#[inline(always)]
pub fn core_data(&self) -> bool {
self.data.core_data()
}
}
fn serialize_data_field<S>(data: &Box<dyn StofData>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer {
if data.core_data() {
data.serialize(serializer)
} else {
if let Ok(bytes) = bincode::serialize(data) {
let container: Box<dyn StofData> = Box::new(StofDataContainer { contained: bytes });
container.as_ref().serialize(serializer)
} else {
Err(S::Error::custom("error serializing containerized (non-core) data to bytes"))
}
}
}
fn deserialize_data_field<'de, D>(deserializer: D) -> Result<Box<dyn StofData>, D::Error>
where
D: serde::Deserializer<'de> {
let mut data: Box<dyn StofData> = Deserialize::deserialize(deserializer)?;
if data.is_container() {
let any = data.as_dyn_any();
if let Some(container) = any.downcast_ref::<StofDataContainer>() {
if let Ok(res) = bincode::deserialize::<Box<dyn StofData>>(container.contained.as_ref()) {
data = res;
}
}
}
Ok(data)
}