digraph_rs/
lib.rs

1//! The library allows creating and manipulating with directed graphs.
2//!
3//! # Description:
4//! The main structure is `DiGraph` that is defined in terms of three types:
5//!  - NId - id of the node. Should be unique and implement `Eq + Hash`
6//!  - NL - node payload (`EmptyPayload`) by default
7//!  - EL - edge payload (`EmptyPayload`) by default
8//!
9//! # Example of the struct:
10//! ```rust
11//! use digraph_rs::{DiGraph,EmptyPayload};
12//! use std::collections::HashMap;
13//! fn simple_creation_graph(){
14//!   let mut graph:DiGraph<usize, EmptyPayload,EmptyPayload> = DiGraph::empty();
15//!   graph.add_bare_node(1);  
16//!   graph.add_bare_node(2);  
17//!   graph.add_bare_node(3);
18//!   graph.add_bare_node(4);
19//!   
20//!   graph.add_bare_edge(1, 2);
21//!   graph.add_bare_edge(2, 3);
22//!   graph.add_bare_edge(3, 4);
23//!   graph.add_bare_edge(4, 1);
24//!   assert_eq!(graph.start(), &Some(1));
25//!        assert_eq!(
26//!            graph.successors(1),
27//!            Some(&HashMap::from_iter(
28//!                vec![(2usize, EmptyPayload)].into_iter()
29//!           ))
30//!       );
31//!   
32//!     
33//! }
34//! ```
35//! # Modules
36//!  - builder: the module allows creating graph using defined templates(macroses)
37//!  - analyzer: the module allows performing a set of default algorithms  
38//!  - visualizer: the module allows visualizing the graph and some extra information in graphviz format
39//!  - generator: the module allows generating random graphs according to the different modules
40//!
41//! # Example with modules:
42//! ```rust
43//!  
44//!    use digraph_rs::{DiGraph,EmptyPayload,digraph, extend_edges, extend_nodes,};
45//!    use digraph_rs::analyzer::dijkstra::{DijkstraPath, MinPathProcessor};
46//!     #[test]
47//!      fn complex_example() {
48//!          let mut graph = digraph!((usize,_,usize) => [1,2,3,4,5,6,7,8] => {
49//!            1 => [(2,3),(3,1),(4,2)];
50//!            [2,3,4] => (5,2);
51//!            5 => (6,1);
52//!            6 => [(7,2),(8,3)];
53//!          });
54//!  
55//!          let v_res = graph.visualize().str_to_dot_file("dots/graph.svg");
56//!          assert!(v_res.is_ok());
57//!  
58//!          assert!(graph.analyze().edge(&1, &2).is_some());
59//!          assert!(graph.analyze().edge(&1, &6).is_none());
60//!  
61//!          let mut path_finder = DijkstraPath::new(&graph);
62//!          let paths = path_finder.on_edge(1);
63//!          let trail = paths.trail(&8).unwrap();
64//!          assert_eq!(trail, vec![1, 3, 5, 6, 8]);
65//!          let r = graph
66//!              .visualize()
67//!              .to_dot_file("dots/graph_path_1_8.svg", MinPathProcessor::new(trail));
68//!          assert!(r.is_ok());
69//!  }
70//! ```
71//!
72
73pub mod analyzer;
74pub mod builder;
75pub mod generator;
76pub mod visualizer;
77use crate::analyzer::GraphAnalyzer;
78use crate::visualizer::dot::*;
79use crate::visualizer::{vis, vis_to_file};
80
81use self::visualizer::DotGraphVisualizer;
82use graphviz_rust::dot_generator::{graph, id, node};
83use graphviz_rust::dot_structures::{Graph, Id, Stmt};
84use std::collections::{HashMap, HashSet};
85use std::fmt::{Debug, Error, Formatter};
86use std::hash::Hash;
87
88/// The base structure denoting a directed graph with a start in the first added node.
89///  - NId: id of the node. should be unique and implement `Eq + Hash`
90///  - NL: payload for node
91///  - EL: payload for edge
92#[derive(Debug)]
93pub struct DiGraph<NId, NL, EL>
94where
95    NId: Eq + Hash,
96{
97    nodes: HashMap<NId, NL>,
98    edges: HashMap<NId, HashMap<NId, EL>>,
99    start: Option<NId>,
100}
101
102impl DiGraph<usize, EmptyPayload, EmptyPayload> {
103    /// Default empty payload graph
104    pub fn empty() -> Self {
105        Self::new()
106    }
107}
108
109impl<NId, NL, EL> DiGraph<NId, NL, EL>
110where
111    NId: Clone + Eq + Hash,
112{
113    fn insert_new_node(&mut self, payload: NL, id: NId) -> NId {
114        self.nodes.insert(id.clone(), payload);
115        if self.start.is_none() {
116            self.start = Some(id.clone())
117        }
118
119        id
120    }
121
122    pub fn new() -> Self {
123        Self {
124            nodes: HashMap::new(),
125            edges: HashMap::new(),
126            start: None,
127        }
128    }
129    /// Adds new node. If the given id is presented then it will be replaced with a new payload.
130    /// Returns this id  
131    fn add_node(&mut self, id: NId, payload: NL) -> Option<NId> {
132        Some(self.insert_new_node(payload, id))
133    }
134    /// Removes a node.
135    /// Returns a payload if the node is presented.
136    ///
137    /// **Note**: this operation should be accompanied by the remove_edge operations.  
138    pub fn remove_node(&mut self, id: NId) -> Option<NL> {
139        self.nodes.remove(&id)
140    }
141
142    /// Adds new edge. Returns prev.
143    pub fn add_edge(&mut self, from: NId, to: NId, payload: EL) -> Option<EL> {
144        self.edges.entry(from).or_default().insert(to, payload)
145    }
146    /// Removes edge.
147    /// Returns a payload on the edge if it exists.
148    pub fn remove_edge(&mut self, from: NId, to: NId) -> Option<EL> {
149        self.edges.entry(from).or_default().remove(&to)
150    }
151
152    /// Returns a reference to the successors.
153    pub fn successors(&self, from: NId) -> Option<&HashMap<NId, EL>> {
154        self.edges.get(&from)
155    }
156
157    /// Returns a reference to a start node.
158    pub fn start(&self) -> &Option<NId> {
159        &self.start
160    }
161
162    /// Invokes a graph analyzer `GraphAnalyzer`
163    pub fn analyze(&self) -> GraphAnalyzer<NId, NL, EL> {
164        GraphAnalyzer { graph: &self }
165    }
166
167    /// Invokes a graph visualizer `DotGraphVisualizer`
168    pub fn visualize(&self) -> DotGraphVisualizer<NId, NL, EL> {
169        DotGraphVisualizer::new(self)
170    }
171}
172
173impl<NId, NL, EL> DiGraph<NId, NL, EL>
174where
175    NId: Clone + Eq + Hash,
176    NL: Default,
177{
178    /// Adds a node with an `EmptyPayload`
179    pub fn add_bare_node(&mut self, id: NId) -> Option<NId> {
180        self.add_node(id, Default::default())
181    }
182}
183
184impl<NId, NL, EL> DiGraph<NId, NL, EL>
185where
186    NId: Clone + Eq + Hash,
187    EL: Default,
188{
189    /// Adds an edge with an `EmptyPayload`
190    pub fn add_bare_edge(&mut self, from: NId, to: NId) -> Option<EL> {
191        self.add_edge(from, to, Default::default())
192    }
193}
194
195#[derive(Copy, Clone, PartialEq, Default)]
196pub struct EmptyPayload;
197
198impl ToString for EmptyPayload {
199    fn to_string(&self) -> String {
200        "x".to_string()
201    }
202}
203
204impl Debug for EmptyPayload {
205    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
206        f.write_str("x")
207    }
208}