use arcstr::{literal, ArcStr};
use rustc_hash::FxHashSet;
use serde::{Deserialize, Serialize};
use crate::model::{DataRef, Graph, NodeRef, SPath, StofData};
pub const PROTOTYPE_TYPE_ATTR: ArcStr = literal!("type");
pub const PROTOTYPE_EXTENDS_ATTR: ArcStr = literal!("extends");
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Prototype {
pub node: NodeRef,
}
#[typetag::serde(name = "Proto")]
impl StofData for Prototype {
fn core_data(&self) -> bool {
true
}
}
impl Prototype {
pub fn from_path(graph: &Graph, path: &str, start: Option<NodeRef>) -> Vec<DataRef> {
let spath = SPath::from(path);
if spath.path.is_empty() { return vec![]; }
if let Some(node) = SPath::node(&graph, spath, start) {
return Self::prototype_refs(graph, &node);
}
vec![]
}
pub fn prototype_refs(graph: &Graph, node: &NodeRef) -> Vec<DataRef> {
let mut protos = Vec::new();
if let Some(node) = node.node(graph) {
for (_, dref) in &node.data {
if dref.type_of::<Self>(&graph) {
protos.push(dref.clone());
}
}
}
protos
}
pub fn prototype_nodes(graph: &Graph, node: &NodeRef, recursive: bool) -> Vec<NodeRef> {
let mut seen = FxHashSet::default();
Self::internal_prototype_nodes(graph, node, recursive, &mut seen)
}
fn internal_prototype_nodes(graph: &Graph, node: &NodeRef, recursive: bool, seen: &mut FxHashSet<NodeRef>) -> Vec<NodeRef> {
let mut protos = Vec::new();
if let Some(node) = node.node(graph) {
for (_, dref) in &node.data {
if let Some(proto) = graph.get_stof_data::<Self>(dref) {
if !seen.contains(&proto.node) {
seen.insert(proto.node.clone());
protos.push(proto.node.clone());
if recursive {
protos.append(&mut Self::internal_prototype_nodes(graph, &proto.node, true, seen));
}
}
}
}
}
protos
}
}