use arcstr::{literal, ArcStr};
use colored::Colorize;
use indexmap::{IndexMap, IndexSet};
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{Deserialize, Serialize};
use crate::{model::{DataRef, Graph, NodeRef, SId}, runtime::Val};
pub const INVALID_NODE_NEW: ArcStr = literal!("new");
pub const INVALID_NODE_NAME: ArcStr = literal!("name");
pub const INVALID_NODE_PARENT: ArcStr = literal!("parent");
pub const INVALID_NODE_CHILDREN: ArcStr = literal!("children");
pub const INVALID_NODE_DATA: ArcStr = literal!("data");
pub const INVALID_NODE_ATTRS: ArcStr = literal!("attributes");
const FIELD_NODE_ATTR: ArcStr = literal!("field");
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Node {
pub id: NodeRef,
pub name: SId,
pub parent: Option<NodeRef>,
pub children: IndexSet<NodeRef>,
pub data: IndexMap<String, DataRef>,
pub attributes: FxHashMap<String, Val>,
#[serde(skip)]
pub dirty: FxHashSet<ArcStr>,
}
impl Node {
pub fn new(name: SId, id: NodeRef, field: bool) -> Self {
let mut attributes = FxHashMap::default();
if field {
attributes.insert(FIELD_NODE_ATTR.to_string(), Val::Null);
}
Self {
id,
name,
parent: None,
children: Default::default(),
data: Default::default(),
dirty: Default::default(),
attributes,
}
}
#[inline(always)]
pub fn is_field(&self) -> bool {
self.attributes.contains_key(FIELD_NODE_ATTR.as_str())
}
#[inline]
pub fn make_field(&mut self) -> bool {
let res = self.attributes.insert(FIELD_NODE_ATTR.to_string(), Val::Null).is_none();
if res {
self.invalidate_attrs();
}
res
}
#[inline]
pub fn not_field(&mut self) -> bool {
let res = self.attributes.remove(FIELD_NODE_ATTR.as_str()).is_some();
if res {
self.invalidate_attrs();
}
res
}
#[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_NODE_NAME)
}
#[inline(always)]
pub fn invalidate_attrs(&mut self) -> bool {
self.invalidate(INVALID_NODE_ATTRS)
}
#[inline(always)]
pub fn invalidate_parent(&mut self) -> bool {
self.invalidate(INVALID_NODE_PARENT)
}
#[inline(always)]
pub fn invalidate_children(&mut self) -> bool {
self.invalidate(INVALID_NODE_CHILDREN)
}
#[inline(always)]
pub fn invalidate_data(&mut self) -> bool {
self.invalidate(INVALID_NODE_DATA)
}
#[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_NODE_NAME)
}
#[inline(always)]
pub fn validate_attrs(&mut self) -> bool {
self.validate(&INVALID_NODE_ATTRS)
}
#[inline(always)]
pub fn validate_parent(&mut self) -> bool {
self.validate(&INVALID_NODE_PARENT)
}
#[inline(always)]
pub fn validate_children(&mut self) -> bool {
self.validate(&INVALID_NODE_CHILDREN)
}
#[inline(always)]
pub fn validate_data(&mut self) -> bool {
self.validate(&INVALID_NODE_DATA)
}
#[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 insert_attribute(&mut self, id: String, val: Val) -> bool {
let res = self.attributes.insert(id, val).is_none();
if res {
self.invalidate_attrs();
}
res
}
#[inline]
pub fn remove_attribute(&mut self, id: &str) -> bool {
let res = self.attributes.remove(id).is_some();
if res {
self.invalidate_attrs();
}
res
}
#[inline]
pub fn set_name(&mut self, name: SId) -> bool {
if name != self.name {
self.name = name;
self.invalidate_name();
true
} else {
false
}
}
#[inline(always)]
pub fn has_child(&self, child: &NodeRef) -> bool {
self.children.contains(child)
}
#[inline]
pub fn add_child(&mut self, child: NodeRef) -> bool {
if self.children.insert(child) {
self.invalidate_children();
true
} else {
false
}
}
#[inline]
pub fn remove_child(&mut self, child: &NodeRef) -> bool {
if self.children.shift_remove(child) {
self.invalidate_children();
true
} else {
false
}
}
#[inline(always)]
pub fn has_data_named(&self, name: &str) -> bool {
self.data.contains_key(name)
}
#[inline]
pub fn has_data(&self, data: &DataRef) -> bool {
for (_, id) in &self.data {
if id == data { return true; }
}
false
}
#[inline]
pub fn add_data(&mut self, name: String, data: DataRef) -> Option<DataRef> {
let old = self.data.insert(name, data);
self.invalidate_data();
old
}
pub fn remove_data(&mut self, data: &DataRef) -> bool {
let mut remove_name = None;
for (name, id) in &self.data {
if id == data {
remove_name = Some(name.clone());
break;
}
}
if let Some(name) = remove_name {
self.data.shift_remove(&name).is_some()
} else {
false
}
}
#[inline(always)]
pub fn remove_data_named(&mut self, name: &str) -> Option<DataRef> {
self.data.shift_remove(name)
}
#[inline(always)]
pub fn get_data(&self, name: &str) -> Option<&DataRef> {
self.data.get(name)
}
pub fn dump(&self, graph: &Graph, level: i32, data: bool) -> String {
let mut res = String::new();
let mut ident = String::from("\n");
for _ in 0..level { ident.push('\t'); }
let mut parent_str = "None".to_string();
if let Some(parent) = &self.parent {
parent_str = format!("{}", &parent);
}
res.push_str(&format!("{}{} ({}{}, {}{}) {}", &ident, &self.name.as_ref().blue(), "ID: ".dimmed(), &self.id.as_ref().cyan(), "parent: ".dimmed(), &parent_str.purple(), "{".bright_blue()));
if level < 1 { res = res.replace('\n', ""); }
if data {
let mut ident = String::from("\n");
for _ in 0..(level + 1) { ident.push('\t'); }
let mut iident = String::from("\n");
for _ in 0..(level + 2) { iident.push('\t'); }
for (data_name, data_ref) in &self.data {
if let Some(data) = data_ref.data(graph) {
res.push_str(&format!("{}{} ({}{}) {}", &ident, &data_name.green(), "ID: ".dimmed(), &data_ref.as_ref().cyan().dimmed(), "{".green()));
let json = serde_json::to_string(&data.data);
if let Ok(json) = json {
res.push_str(&format!("{}{}", &iident, json.dimmed()));
} else {
res.push_str(&format!("{}{}", &iident, "DATA SERIALIZATION ERROR".red()));
}
res.push_str(&format!("{}{}", &ident, "}".green()));
}
}
res.push('\n');
}
for child_ref in &self.children {
if let Some(child) = child_ref.node(graph) {
res.push_str(&child.dump(graph, level + 1, data));
}
}
res.push_str(&format!("{}{}", &ident, "}".bright_blue()));
res
}
}