wolf-graph 0.1.0

Data structures and algorithms for working with graphs with reference or value semantics.
Documentation
use std::marker::PhantomData;

use anyhow::Result;

use crate::{DFSVisitor, DepthFirstSearch, EdgeID, NodeID, Nodes, VisitableGraph};

/// A trait for a graph that supports checking whether a path exists between two nodes.
pub trait PathExists {
    type Graph: VisitableGraph;

    fn path_exists_opt(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>, excluded_edge: Option<impl AsRef<EdgeID>>) -> Result<bool>;

    fn path_exists(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>) -> Result<bool> {
        self.path_exists_opt(from, to, None::<EdgeID>)
    }

    fn can_add_dag_edge(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>, excluded_edge: Option<impl AsRef<EdgeID>>) -> Result<bool> {
        Ok(!self.path_exists_opt(to, from, excluded_edge)?)
    }

    fn can_move_dag_edge(&self, edge: impl AsRef<EdgeID>, new_source: impl AsRef<NodeID>, new_target: impl AsRef<NodeID>) -> Result<bool> {
        self.can_add_dag_edge(new_source, new_target, Some(edge))
    }
}

impl<G> PathExists for G
where
    G: DepthFirstSearch<G, bool> + VisitableGraph,
{
    type Graph = G;

    fn path_exists_opt(&self, from: impl AsRef<NodeID>, to: impl AsRef<NodeID>, excluded_edge: Option<impl AsRef<EdgeID>>) -> Result<bool> {
        let from = from.as_ref();
        let to = to.as_ref();
        let mut visitor = PathExistsVisitor::new(from.clone(), to.clone());
        self.depth_first_search_opt(&mut visitor, &Nodes::from([from.clone()]), false, excluded_edge)
    }
}

pub struct PathExistsVisitor<Graph> {
    from: NodeID,
    to: NodeID,
    graph: PhantomData<Graph>,
}

impl<Graph> PathExistsVisitor<Graph>
where
    Graph: VisitableGraph,
{
    pub fn new(from: NodeID, to: NodeID) -> Self {
        Self {
            from,
            to,
            graph: PhantomData,
        }
    }
}

impl<Graph> DFSVisitor for PathExistsVisitor<Graph>
where
    Graph: VisitableGraph,
{
    type Graph = Graph;
    type Output = bool;

    fn discover_node(&mut self, _graph: &Self::Graph, node: &NodeID) -> Result<Option<bool>> {
        Ok(if node == &self.to { Some(true) } else { None })
    }

    fn finish_node(&mut self, _graph: &Self::Graph, node: &NodeID) -> Result<Option<bool>> {
        Ok(if node == &self.from { Some(false) } else { None })
    }

    fn finish(&mut self, _graph: &Self::Graph) -> Result<bool> {
        Ok(false)
    }
}