use std::{collections::HashSet, hash::Hash};
pub struct CycleDetector<T: Clone + Eq + Hash> {
path_stack: Vec<T>,
nodes_in_path: HashSet<T>,
}
#[must_use = "if unused, the node will immediately be considered exited"]
pub struct NodeGuard<'a, T: Clone + Eq + Hash> {
detector: &'a mut CycleDetector<T>,
}
impl<'a, T: Clone + Eq + Hash> NodeGuard<'a, T> {
pub fn get_detector(&self) -> &CycleDetector<T> {
self.detector
}
pub fn get_detector_mut(&mut self) -> &mut CycleDetector<T> {
self.detector
}
}
impl<T: Clone + Eq + Hash> CycleDetector<T> {
pub fn new() -> Self {
CycleDetector {
path_stack: Vec::new(),
nodes_in_path: HashSet::new(),
}
}
pub fn visit(&mut self, node: T) -> Result<NodeGuard<'_, T>, T> {
if self.nodes_in_path.contains(&node) {
return Err(node); }
self.path_stack.push(node.clone());
self.nodes_in_path.insert(node);
Ok(NodeGuard { detector: self })
}
fn leave_node(&mut self) {
if let Some(node) = self.path_stack.pop() {
self.nodes_in_path.remove(&node);
} else {
}
}
pub fn current_path(&self) -> &Vec<T> {
&self.path_stack
}
pub fn is_node_in_path(&self, node: &T) -> bool {
self.nodes_in_path.contains(node)
}
}
impl<'a, T: Clone + Eq + Hash> Drop for NodeGuard<'a, T> {
fn drop(&mut self) {
self.detector.leave_node();
}
}
impl<T: Clone + Eq + Hash> Default for CycleDetector<T> {
fn default() -> Self {
Self::new()
}
}