1#![forbid(unsafe_code)]
41
42use irox_bits::{BitsError, FormatBits, MutBits};
43use std::collections::HashSet;
44use std::fmt::Write;
45use std::hash::{Hash, Hasher};
46use std::string::String;
47use std::vec::Vec;
48
49pub trait DotLine {
50 fn get_line(&self) -> String;
51}
52
53#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
54pub enum GraphType {
55 #[default]
56 Graph,
57 Digraph,
58}
59
60impl GraphType {
61 pub fn get_arrow(&self) -> &'static str {
62 match self {
63 GraphType::Graph => "--",
64 GraphType::Digraph => "->",
65 }
66 }
67 pub fn get_name(&self) -> &'static str {
68 match self {
69 GraphType::Graph => "graph",
70 GraphType::Digraph => "digraph",
71 }
72 }
73}
74
75#[derive(Debug, Clone, Eq, PartialEq, Hash)]
76pub enum Element {
77 Node(Node),
78 NodeAttr(Attribute),
79 Edge(Edge),
80 EdgeAttr(Attribute),
81 Attribute(Attribute),
82 Subgraph(Subgraph),
83}
84
85impl DotLine for Element {
86 fn get_line(&self) -> String {
87 match self {
88 Element::Node(n) => n.get_line(),
89 Element::Edge(e) => e.get_line(),
90 Element::Attribute(a) => a.get_line(),
91 _ => {
92 todo!()
93 }
94 }
95 }
96}
97
98#[derive(Default, Debug, Clone, Eq, PartialEq)]
99pub struct Graph {
100 pub is_strict: bool,
101 pub graph_type: GraphType,
102 pub id: Option<String>,
103 pub elements: Vec<Element>,
104 pub known_nodes: HashSet<String>,
105}
106impl Hash for Graph {
107 fn hash<H: Hasher>(&self, state: &mut H) {
108 self.is_strict.hash(state);
109 self.graph_type.hash(state);
110 self.id.hash(state);
111 self.elements.hash(state);
112 }
113}
114
115impl Graph {
116 pub fn named(name: &str) -> Self {
117 Self {
118 id: Some(name.to_string()),
119 ..Default::default()
120 }
121 }
122 pub fn add_node(&mut self, node: Node) {
123 self.elements.push(Element::Node(node));
124 }
125 pub fn add_edge(&mut self, edge: Edge) {
126 self.elements.push(Element::Edge(edge));
127 }
128 pub fn add_graph_attr(&mut self, key: &str, val: &str) {
129 self.elements
130 .push(Element::Attribute(Attribute::new(key, val)))
131 }
132 pub fn write_to<T: MutBits>(&self, out: &mut T) -> Result<(), BitsError> {
133 let mut out = FormatBits(out);
134 if self.is_strict {
135 write!(out, "strict ")?;
136 }
137 write!(out, "{} ", self.graph_type.get_name())?;
138 if let Some(name) = &self.id {
139 write!(out, "{name} ")?;
140 }
141 writeln!(out, "{{")?;
142 for elem in &self.elements {
143 writeln!(out, "\t{}", elem.get_line())?;
144 }
145 writeln!(out, "}}")?;
146 Ok(())
147 }
148}
149
150#[derive(Default, Debug, Clone, Eq, PartialEq, Hash)]
151pub struct Subgraph {
152 pub id: Option<String>,
153 pub elements: Vec<Element>,
154}
155
156#[derive(Default, Debug, Clone, Eq, PartialEq, Hash)]
157pub struct Attribute {
158 pub name: String,
159 pub value: String,
160}
161impl Attribute {
162 pub fn new(key: &str, val: &str) -> Self {
163 Attribute {
164 name: key.to_string(),
165 value: val.to_string(),
166 }
167 }
168}
169impl DotLine for Attribute {
170 fn get_line(&self) -> String {
171 format!("{}={}", self.name, self.value)
172 }
173}
174
175#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
176pub struct AttrList(pub Vec<Attribute>);
177
178impl DotLine for AttrList {
179 fn get_line(&self) -> String {
180 if self.0.is_empty() {
181 String::default()
182 } else {
183 format!(
184 "[{}]",
185 self.0
186 .iter()
187 .map(DotLine::get_line)
188 .collect::<Vec<_>>()
189 .join("; ")
190 )
191 }
192 }
193}
194
195#[derive(Debug, Clone, Eq, PartialEq, Hash)]
196pub struct Node {
197 pub id: String,
198 pub attributes: AttrList,
199}
200
201impl Node {
202 pub fn new(id: &str) -> Node {
203 Node {
204 id: id.to_string(),
205 attributes: AttrList::default(),
206 }
207 }
208}
209
210impl DotLine for Node {
211 fn get_line(&self) -> String {
212 format!("\"{}\" {}", self.id, self.attributes.get_line())
213 }
214}
215
216#[derive(Debug, Clone, Eq, PartialEq, Hash)]
217pub struct Edge {
218 pub edge_type: GraphType,
219 pub first_node: String,
220 pub second_node: String,
221 pub attributes: AttrList,
222}
223impl Edge {
224 pub fn new(graph: &Graph, first: &str, second: &str) -> Self {
225 Edge {
226 edge_type: graph.graph_type,
227 first_node: first.to_string(),
228 second_node: second.to_string(),
229 attributes: AttrList::default(),
230 }
231 }
232}
233
234impl DotLine for Edge {
235 fn get_line(&self) -> String {
236 format!(
237 "\"{}\" {} \"{}\" {}",
238 self.first_node,
239 self.edge_type.get_arrow(),
240 self.second_node,
241 self.attributes.get_line()
242 )
243 }
244}