use std::{any::Any, mem::swap};
use rustc_hash::FxHashSet;
use crate::model::{Data, Graph, Node, SId, SPath};
pub type NodeRef = SId;
impl NodeRef {
#[inline(always)]
pub fn node_exists(&self, graph: &Graph) -> bool {
graph.nodes.contains_key(self)
}
#[inline(always)]
pub fn node<'a>(&self, graph: &'a Graph) -> Option<&'a Node> {
graph.nodes.get(self)
}
#[inline(always)]
pub fn node_mut<'a>(&self, graph: &'a mut Graph) -> Option<&'a mut Node> {
graph.nodes.get_mut(self)
}
pub fn node_name(&self, graph: &Graph) -> Option<SId> {
if let Some(node) = self.node(graph) {
Some(node.name.clone())
} else {
None
}
}
pub fn node_parent(&self, graph: &Graph) -> Option<NodeRef> {
if let Some(node) = self.node(graph) {
node.parent.clone()
} else {
None
}
}
pub fn node_data_named<'a>(&self, graph: &'a Graph, name: &str) -> Option<&'a DataRef> {
if let Some(node) = self.node(graph) {
node.get_data(name)
} else {
None
}
}
#[inline]
pub fn is_root(&self, graph: &Graph) -> bool {
for root in &graph.roots {
if root == self {
return true;
}
}
false
}
pub fn root(&self, graph: &Graph) -> Option<NodeRef> {
if let Some(node) = self.node(graph) {
if let Some(parent) = &node.parent {
return parent.root(graph);
}
return Some(node.id.clone());
}
None
}
pub fn child_of(&self, graph: &Graph, other: &NodeRef) -> bool {
if self == other { return true; }
if let Some(node) = self.node(graph) {
if let Some(parent) = &node.parent {
return parent.child_of(graph, other);
}
}
false
}
pub fn rename_node(&self, graph: &mut Graph, name: impl Into<SId>) -> bool {
if let Some(node) = self.node_mut(graph) {
node.set_name(name.into())
} else {
false
}
}
pub fn child_of_distance(&self, graph: &Graph, other: &NodeRef) -> i32 {
if self == other { return 0; }
let mut node_parent = None;
if let Some(node) = self.node(graph) {
node_parent = node.parent.clone();
}
let mut dist = 0;
while node_parent.is_some() {
dist += 1;
if let Some(np) = &node_parent {
if np == other {
return dist;
} else if let Some(node) = np.node(graph) {
node_parent = node.parent.clone();
} else {
node_parent = None;
}
}
}
-1
}
pub fn node_path(&self, graph: &Graph, names: bool) -> Option<SPath> {
let mut node = self.node(graph);
if node.is_some() {
let mut res = Vec::new();
let mut seen = FxHashSet::default();
while node.is_some() {
let inner = node.unwrap();
if seen.contains(&inner.id) { break; }
if names {
res.push(inner.name.clone());
} else {
res.push(inner.id.clone());
}
seen.insert(inner.id.clone());
if let Some(parent) = &inner.parent {
node = parent.node(graph);
} else {
node = None;
}
}
res.reverse();
return Some(SPath {
names,
path: res,
});
}
None
}
pub fn distance_to(&self, graph: &Graph, other: &Self) -> i32 {
if !self.node_exists(graph) { return -2; }
if !other.node_exists(graph) { return -2; }
if self == other { return 0; }
let mut node_a_id_path = self.node_path(graph, false).unwrap().path;
let mut node_b_id_path = other.node_path(graph, false).unwrap().path;
if node_a_id_path.len() < 1 || node_b_id_path.len() < 1 {
return -1;
}
if &node_a_id_path[0] != &node_b_id_path[0] {
return (node_a_id_path.len() as i32 - 1) + (node_b_id_path.len() as i32 - 1);
}
if node_a_id_path.len() > node_b_id_path.len() {
swap(&mut node_a_id_path, &mut node_b_id_path);
}
let mut to_remove = FxHashSet::default();
let mut last = SId::default();
for i in 0..node_a_id_path.len() {
let aid = &node_a_id_path[i];
let bid = &node_b_id_path[i];
if aid == bid {
to_remove.insert(aid.clone());
last = aid.clone();
} else {
break;
}
}
to_remove.remove(&last);
node_a_id_path.retain(|x| !to_remove.contains(x));
node_b_id_path.retain(|x| !to_remove.contains(x));
(node_a_id_path.len() as i32 - 1) + (node_b_id_path.len() as i32 - 1)
}
}
pub type DataRef = SId;
impl DataRef {
#[inline(always)]
pub fn data_exists(&self, graph: &Graph) -> bool {
graph.data.contains_key(self)
}
#[inline(always)]
pub fn data<'a>(&self, graph: &'a Graph) -> Option<&'a Data> {
graph.data.get(self)
}
#[inline(always)]
pub fn data_mut<'a>(&self, graph: &'a mut Graph) -> Option<&'a mut Data> {
graph.data.get_mut(self)
}
#[inline]
pub fn data_nodes(&self, graph: &Graph) -> FxHashSet<DataRef> {
if let Some(data) = self.data(graph) {
data.nodes.clone()
} else {
Default::default()
}
}
pub fn data_any_path(&self, graph: &Graph, sep: &str) -> String {
if let Some(data) = self.data(graph) {
for node in &data.nodes {
if let Some(path) = node.node_path(graph, true) {
return path.join(sep);
}
}
}
Default::default()
}
pub fn type_of<T: Any>(&self, graph: &Graph) -> bool {
if let Some(data) = self.data(graph) {
data.is_type::<T>()
} else {
false
}
}
pub fn data_name(&self, graph: &Graph) -> Option<SId> {
if let Some(data) = self.data(graph) {
Some(data.name.clone())
} else {
None
}
}
pub fn tagname(&self, graph: &Graph) -> Option<String> {
if let Some(data) = self.data(graph) {
Some(data.tagname())
} else {
None
}
}
pub fn core_data(&self, graph: &Graph) -> bool {
if let Some(data) = self.data(graph) {
data.core_data()
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use crate::model::Graph;
#[test]
fn distance_to() {
let mut graph = Graph::default();
let root = graph.ensure_main_root();
let base;
let a;
{
base = graph.insert_child("base", &root, false);
{
a = graph.insert_child("a", &base, false);
}
}
let another = graph.insert_root("Another");
let top;
let b;
{
top = graph.insert_child("top", &another, false);
{
b = graph.insert_child("b", &top, false);
}
}
assert_eq!(a.distance_to(&graph, &b), 4);
assert_eq!(b.distance_to(&graph, &a), 4);
assert_eq!(a.distance_to(&graph, &base), 1);
assert_eq!(a.distance_to(&graph, &root), 2);
assert_eq!(b.distance_to(&graph, &top), 1);
assert_eq!(b.distance_to(&graph, &b), 0);
assert_eq!(b.distance_to(&graph, &root), 2);
assert_eq!(top.distance_to(&graph, &base), 2);
}
}