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}