crate::ix!();
#[derive(Hash,SaveLoad,PartialEq,Eq,AiJsonTemplate,Serialize,Deserialize,Getters, Setters, Builder, Debug, Clone)]
#[getset(get = "pub", set = "pub")]
pub struct CoreStringSkeleton {
#[builder(default = "\"target\".to_string()")]
#[serde(default)]
target_name: String,
#[builder(default)]
dispatch_nodes: Vec<CoreSkeletalDispatchNode>,
#[builder(default)]
aggregate_nodes: Vec<CoreSkeletalAggregateNode>,
#[builder(default)]
leaf_holder_nodes: Vec<CoreSkeletalLeafHolderNode>,
}
impl CoreStringSkeleton {
#[instrument(level = "trace", skip_all)]
pub fn sort_in_place_top_down(&mut self) {
use std::collections::{HashMap, HashSet, VecDeque};
let mut child_set = HashSet::<String>::new();
let mut adj: HashMap<String, Vec<String>> = HashMap::new();
for n in &self.dispatch_nodes {
let entry = adj.entry(n.name().clone()).or_default();
for ch in n.children() {
child_set.insert(ch.name().clone());
entry.push(ch.name().clone());
}
}
for n in &self.aggregate_nodes {
let entry = adj.entry(n.name().clone()).or_default();
for ch in n.children() {
child_set.insert(ch.name().clone());
entry.push(ch.name().clone());
}
}
let mut all_names = HashSet::<String>::new();
for n in &self.dispatch_nodes { all_names.insert(n.name().clone()); }
for n in &self.aggregate_nodes { all_names.insert(n.name().clone()); }
for n in &self.leaf_holder_nodes{ all_names.insert(n.name().clone()); }
let roots: Vec<String> = all_names.iter()
.filter(|n| !child_set.contains(*n))
.cloned()
.collect();
trace!(?roots, "identified root nodes");
let mut depth: HashMap<String, usize> = HashMap::new();
let mut q: VecDeque<(String, usize)> = VecDeque::new();
for r in &roots { q.push_back((r.clone(), 0)); }
while let Some((name, d)) = q.pop_front() {
if depth.contains_key(&name) { continue; }
depth.insert(name.clone(), d);
for ch in adj.get(&name).unwrap_or(&Vec::new()) {
q.push_back((ch.clone(), d + 1));
}
}
let fallback = depth.values().copied().max().unwrap_or(0) + 1;
for n in &all_names { depth.entry(n.clone()).or_insert(fallback); }
enum NodeWrap {
Dispatch(CoreSkeletalDispatchNode),
Aggregate(CoreSkeletalAggregateNode),
Leaf(CoreSkeletalLeafHolderNode),
}
let mut combined: Vec<NodeWrap> = Vec::new();
combined.extend(std::mem::take(&mut self.dispatch_nodes)
.into_iter()
.map(NodeWrap::Dispatch));
combined.extend(std::mem::take(&mut self.aggregate_nodes)
.into_iter()
.map(NodeWrap::Aggregate));
combined.extend(std::mem::take(&mut self.leaf_holder_nodes)
.into_iter()
.map(NodeWrap::Leaf));
let name_of = |n: &NodeWrap| -> String {
match n {
NodeWrap::Dispatch(d) => d.name().to_string(),
NodeWrap::Aggregate(a) => a.name().to_string(),
NodeWrap::Leaf(l) => l.name().to_string(),
}
};
combined.sort_by(|a, b| {
depth[&name_of(a)]
.cmp(&depth[&name_of(b)])
.then(name_of(a).cmp(&name_of(b)))
});
let mut dispatch_sorted = Vec::new();
let mut aggregate_sorted = Vec::new();
let mut leaf_sorted = Vec::new();
for n in combined {
match n {
NodeWrap::Dispatch(d) => dispatch_sorted.push(d),
NodeWrap::Aggregate(a) => aggregate_sorted.push(a),
NodeWrap::Leaf(l) => leaf_sorted.push(l),
}
}
self.dispatch_nodes = dispatch_sorted;
self.aggregate_nodes = aggregate_sorted;
self.leaf_holder_nodes = leaf_sorted;
trace!("CoreStringSkeleton fully sorted top‑down across all node categories.");
}
}