use crate::sync::AsNamedRef;
use gantz_ca::Registry;
use gantz_core::node::graph::Graph;
use std::collections::HashSet;
pub fn would_cycle<N>(registry: &Registry<Graph<N>>, target: &str, editing: &str) -> bool
where
N: AsNamedRef,
{
let mut stack = vec![target];
let mut visited = HashSet::new();
while let Some(name) = stack.pop() {
if name == editing {
return true;
}
if !visited.insert(name) {
continue;
}
let Some(&commit) = registry.names().get(name) else {
continue;
};
let Some(graph) = registry.commit_graph_ref(&commit) else {
continue;
};
for weight in graph.node_weights() {
if let Some(named_ref) = weight.as_named_ref() {
stack.push(named_ref.name());
}
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
use crate::node::NamedRef;
fn commit_named_refs(registry: &mut Registry<Graph<NamedRef>>, name: &str, refs: &[&str]) {
let mut graph = Graph::<NamedRef>::default();
for &r in refs {
let ca: gantz_ca::ContentAddr = registry
.names()
.get(r)
.copied()
.map(Into::into)
.unwrap_or_else(|| gantz_ca::graph_addr(&Graph::<NamedRef>::default()).into());
graph.add_node(NamedRef::new(r.to_string(), gantz_core::node::Ref::new(ca)));
}
let graph_ca = gantz_ca::graph_addr(&graph);
registry.commit_graph_to_name(std::time::Duration::ZERO, graph_ca, || graph, name);
}
#[test]
fn detects_cycles_by_name() {
let mut registry = Registry::<Graph<NamedRef>>::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, "a", "a"));
assert!(would_cycle(®istry, "b", "a"));
assert!(!would_cycle(®istry, "c", "a"));
assert!(!would_cycle(®istry, "not-a-name", "a"));
}
}