use crate::node::NamedRef;
use gantz_ca::{Name, NodeData, Registry};
use gantz_nodetag::NodeTag;
use std::collections::HashSet;
pub(crate) fn named_ref_of(weight: &NodeData) -> Option<NamedRef> {
if weight.tag != <NamedRef as NodeTag>::TAG {
return None;
}
match gantz_core::data::reify_node_concrete::<NamedRef>(weight) {
Ok(named_ref) => Some(named_ref),
Err(e) => {
log::error!("failed to decode a stored `NamedRef`: {e}");
None
}
}
}
pub fn would_cycle(registry: &Registry, target: &Name, editing: &Name) -> bool {
let mut stack = vec![target.clone()];
let mut visited = HashSet::new();
while let Some(name) = stack.pop() {
if name == *editing {
return true;
}
if !visited.insert(name.clone()) {
continue;
}
let Some(graph_addr) = registry.named_commit(&name).map(|c| c.graph) else {
continue;
};
let Some(graph) = registry.graph(&graph_addr) else {
continue;
};
for weight in graph.node_weights() {
if let Some(named_ref) = named_ref_of(weight) {
stack.push(named_ref.name().clone());
}
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_node::{TestGraph, named_ref};
fn name(s: &str) -> Name {
s.parse().unwrap()
}
fn commit_named_refs(registry: &mut Registry, graph_name: &str, refs: &[&str]) {
let mut graph = TestGraph::default();
for &r in refs {
let ga: gantz_ca::GraphAddr = registry
.named_commit(&name(r))
.map(|c| c.graph)
.unwrap_or_else(|| {
gantz_core::data::erase_with_addr(&TestGraph::default())
.unwrap()
.1
});
graph.add_node(named_ref(r, ga));
}
let (data_graph, graph_ca) = gantz_core::data::erase_with_addr(&graph).unwrap();
registry.commit_graph_to_name(
std::time::Duration::ZERO,
graph_ca,
|| data_graph,
&name(graph_name),
);
}
#[test]
fn detects_cycles_by_name() {
let mut registry = Registry::default();
commit_named_refs(&mut registry, "b", &[]);
commit_named_refs(&mut registry, "a", &["b"]);
commit_named_refs(&mut registry, "b", &["a"]);
commit_named_refs(&mut registry, "c", &[]);
assert!(would_cycle(®istry, &name("a"), &name("a")));
assert!(would_cycle(®istry, &name("b"), &name("a")));
assert!(!would_cycle(®istry, &name("c"), &name("a")));
assert!(!would_cycle(®istry, &name("not-a-name"), &name("a")));
}
}