1use std::{collections::HashMap, fs::{read_to_string, File}, io::Write};
2
3use anyhow::Context;
4use log::{debug, info};
5use crate::dot_parser::attributs::Attributs;
6use super::{attribut::Attribut, edge::Edge, graph_type::GraphType, node::Node, parsing_error::ParsingError};
7
8#[derive(PartialEq,Clone, Eq)]
9#[cfg_attr(
10 feature = "serde",
11 derive(serde::Serialize, serde::Deserialize)
12)]
13pub struct DotGraph {
14 family: GraphType,
15 nodes: Vec<Node>,
16 edges: Vec<Edge>,
17 sous_graphes: Vec<DotGraph>,
18 attributs: Attributs,
19 name: String
20}
21
22impl Default for DotGraph {
23 fn default() -> Self {
24 Self {
25 family: GraphType::Graph,
26 nodes: Default::default(),
27 edges: Default::default(),
28 sous_graphes: Default::default(),
29 attributs: Default::default(),
30 name: Default::default() }
31 }
32}
33
34impl DotGraph {
35 pub fn graph_from_file(path: &str) -> Result<DotGraph, ParsingError> {
36 info!("Opening graph from: {}", path);
37 let file = read_to_string(path)
38 .with_context(|| format!("Reading file {}", path))?;
39
40 let cleaned_file = file.lines()
41 .map(|line| line.trim_ascii())
42 .filter(|line| !line.is_empty() || line.starts_with("//"))
43 .collect::<Vec<&str>>()
44 .join("\r\n");
45
46 DotGraph::try_from(cleaned_file.as_str())
47 }
48
49
50 pub fn new(family: GraphType, nodes: Vec<Node>, edges: Vec<Edge>, sous_graphes: Vec<DotGraph>, attributs: Attributs, name: String) -> Self {
51 DotGraph {
52 family,
53 name,
54 nodes,
55 edges,
56 attributs,
57 sous_graphes
58 }
59 }
60
61}
62
63
64impl TryFrom<&str> for DotGraph {
66 type Error = ParsingError;
67 fn try_from(content: &str) -> Result<Self, Self::Error> {
68 let mut cleaned_content = content.lines()
69 .map(clean_line)
70 .filter(|l| !l.is_empty() && !l.starts_with("//"))
71 .collect::<Vec<_>>().join(";")
72 .to_string();
73
74 Self::create_graph(&mut cleaned_content, None)
75 }
76}
77
78
79impl DotGraph {
80
81 pub fn nodes(&self) ->Vec<Node> {
82 let mut nodes = self.nodes.clone();
83
84 nodes
85 .extend(
86 self.sous_graphes.iter().flat_map(|g| g.nodes.clone()));
87
88 nodes
89 }
90
91 pub fn edges(&self) -> Vec<Edge> {
92 let mut edges = self.edges.clone();
93 edges.extend(self.sous_graphes.iter().flat_map(|g| g.edges.clone()));
94 edges
95 }
96
97 fn create_graph(content: &mut String, parent: Option<GraphType>) -> Result<DotGraph, ParsingError>{
98 debug!("creating graph from: {}", content);
99 if content.trim().is_empty() {
100 return Ok(DotGraph::default());
101 }
102
103 let head_and_body = content.split_once("{").ok_or(ParsingError::DefaultError("Pas de corps ?".to_string()))?;
104 let head = head_and_body.0;
105
106 let type_graph = get_type_graph(head, parent)?;
107 let name = head.split_once(" ").map(|(_gtype,name)| name).unwrap_or("NoName").trim();
108
109 let mut body = head_and_body.1.to_string();
110
111 let sous_graphes = Self::extract_subgraphes(&mut body, type_graph)?;
112 body.pop(); let mut attributs = HashMap::default();
115 let mut nodes =vec![];
116 let mut edges =vec![];
117 let mut default_node_attribute = Attributs::default();
118 let mut default_edge_attribute = Attributs::default();
119
120 body
121 .split(";")
122 .map(clean_line)
123 .filter(|l| !l.is_empty())
124 .try_for_each(|line| {
125 if line.contains(&type_graph.symbol()) {
127 let edge = Edge::try_from((line, type_graph.symbol().as_str()))?;
128 edges.push(edge);
129 return Ok(());
130 }
131
132 if line.contains("[") || !line.contains("=") {
133 let node = Node::try_from(&line.to_string())?;
134 if node.identifier == "node" {
135 default_node_attribute=node.attributes
136 } else if node.identifier == "edge" {
137 default_edge_attribute=node.attributes
138 } else {
139 nodes.push(node);
140 }
141 return Ok(());
142 }
143
144 let att = Attribut::try_from(line)?;
145 attributs.insert(att.key, att.value);
146 Ok::<(), ParsingError>(())
147 })?;
148
149 Ok(DotGraph {name: name.to_string(), family: type_graph, sous_graphes, nodes, edges, attributs: Attributs::from(attributs) })
150 }
151
152 fn extract_subgraphes(body: &mut String, parent: GraphType) -> Result<Vec<DotGraph>, ParsingError> {
153 let mut sous_graphes_position = extract_subgraphes_position(body)?;
154
155 let sous_graphes = sous_graphes_position
156 .iter()
157 .map(|(start,end)|Self::create_graph(&mut body[*start..*end+1].to_string(), Some(parent)))
158 .collect::<Result<Vec<DotGraph>, ParsingError>>()?;
159
160 sous_graphes_position.reverse();
161 for i in sous_graphes_position {
162 body.replace_range(i.0..i.1+1, "");
163 }
164
165 Ok(sous_graphes)
166 }
167
168 pub fn name(&self) -> &String {
169 &self.name
170 }
171
172 pub fn write(&self, path: &str) -> Result<(), ParsingError> {
173 let content = self.as_dot_content();
174 let mut file = File::create(path).unwrap();
175 file.write_all(content.as_bytes()).unwrap();
176 Ok(())
177 }
178
179 fn as_dot_content(&self) -> String {
180 let mut content = String::default();
181
182 content = content + &self.family.to_string() + " " + &self.name + " { \r\n";
183
184 let nodes = self.nodes.iter().map(Node::to_string).fold(String::default(), |acc, node| acc + "\r\n" + &node);
185 let edges = self.edges.iter().map(Edge::to_string).fold(String::default(), |acc, edge| acc + "\r\n" + &edge);
186
187 content = content + &nodes + "\r\n\r\n" + &edges;
188
189 let subgraphes_string = self.sous_graphes.iter().map(DotGraph::as_dot_content).fold(String::default(), |acc, sous_graph| acc + &sous_graph + "\r\n");
190
191
192 content + "\r\n\r\n" + &subgraphes_string + "\r\n}"
193 }
194}
195
196
197fn get_type_graph(content: &str, parent: Option<GraphType>) -> Result<GraphType, ParsingError> {
199 if content.starts_with("digraph") {
200 return Ok(GraphType::Digraph);
201 }
202
203 if content.starts_with("graph") {
204 return Ok(GraphType::Graph);
205 }
206
207 if content.starts_with("subgraph") {
208 return parent.ok_or(ParsingError::DefaultError("Should have a parent".to_string()));
209 }
210
211 Err(ParsingError::DefaultError("No graph type detected".to_string()))
212}
213
214fn clean_line(line: &str) -> &str {
216 line.split_once("//").map(|a|a.0).unwrap_or(line).trim()
217}
218
219fn extract_subgraphes_position(inside_block: &str) -> Result<Vec<(usize, usize)>, ParsingError> {
220
221 let mut remaining = inside_block.to_string();
222
223 let mut sub_graphes_ranges = vec![];
224 let mut stack = 0;
225 while remaining.contains("subgraph"){
226 let start = remaining.find("subgraph").unwrap();
227 let end = next_block_range(&remaining)?.1;
228 sub_graphes_ranges.push((start+stack, end+stack));
229 stack =end+1;
230
231 remaining = remaining.split_at(end+1).1.to_string();
232 }
233 Ok(sub_graphes_ranges)
234}
235
236fn next_block_range(block: &str) -> Result<(usize, usize), ParsingError>{
237 let mut stack = 0;
238 let mut index = 0;
239
240 let mut range : (Option<usize>, Option<usize>)= (None, None);
241 let mut chars = block.chars();
242 let mut next= chars.next();
243 while next.is_some() {
244 let char = next.unwrap();
245
246 if char == '{' {
247 stack+=1;
248 if range.0.is_none() {
249 range.0 = Some(index);
250 }
251 }
252
253 if char == '}' {
254 stack -= 1;
255 if stack == 0 {
256 return match range.0 {
257 Some(start) => Ok((start, index)),
258 None => Err(ParsingError::DefaultError("Parsing exception error: no starting brackets".to_string()))
259 }
260 }
261 if stack < 0 {
262 return Err(ParsingError::DefaultError("Too many }".to_string()));
263 }
264 }
265 index +=1;
266 next = chars.next();
267 }
268
269 Err(ParsingError::DefaultError("Missing ending }".to_string()))
270}
271
272
273#[cfg(test)]
274mod tests {
275 use std::{collections::HashMap, vec};
276
277 use super::*;
278
279
280
281 #[test]
282 fn test_find_ending_pos_combinations() {
283 let combinations :Vec<(&str, (usize,usize))> = vec![
284 ("{test -> a;}", (0,11)),
285 ("{{}}", (0,3)),
286 ("{icitoutvabien}", (0,14)),
287 ("{{{{}}}}", (0,7)),
288 ("{{{{}}}}}", (0,7)),
289 ("graph Test {A;subgraph{D;}A->C}", (11, 30))
290 ];
291
292 combinations.iter().for_each(|combinaisons| assert_eq!(next_block_range(combinaisons.0).unwrap(), combinaisons.1));
293 }
294
295 #[test]
296 fn test_find_ending_pos_combinations_ko() {
297 let combinations :Vec<(&str, &str)> = vec![
298 ("}test{}", "Too many }"),
299 ("{testt", "Missing ending }"),
300 ("{test{}", "Missing ending }")
301 ];
302
303 combinations.iter().for_each(|combinaisons| assert_eq!(next_block_range(combinaisons.0).unwrap_err().to_string(), ParsingError::DefaultError(combinaisons.1.to_string()).to_string()));
304
305 }
306
307
308 #[test]
309 fn graph_try_from() {
310 let input = "digraph Test {A; B [label=test, encore=toto]; A -> B;subgraph{C;D;C->D;}B -> A [label=\"to B\"];value=type;subgraph{C;D;C->D;}A->C;}";
311
312 let result = DotGraph::try_from(input).unwrap();
313 let mut map_attribut = HashMap::new();
314 map_attribut.insert("label".to_string(), "test".to_string());
315 map_attribut.insert("encore".to_string(), "toto".to_string());
316 assert_eq!(result.name, "Test".to_string());
317 assert_eq!(result.nodes,
318 vec![
319 Node::new("A",Attributs::default()),
320 Node::new("B", Attributs::from(map_attribut))]);
321 assert_eq!(result.edges,
322 vec![
323 Edge::try_from(("A->B", "->")).unwrap(),
324 Edge::try_from(("B->A[label=\"to B\"", "->")).unwrap(),
325 Edge::try_from(("A->C", "->")).unwrap()]);
326 assert_eq!(result.sous_graphes.len(), 2);
327 }
328
329 #[test]
330 fn graph_try_from_with_new_line_instead_of_semilicons() {
331 let input = "digraph Test {A\r\n B [label=test, encore=toto]; A -> B;subgraph{C;D\r\nC->D;}B -> A [label=\"to B\"]\r\nvalue=type;subgraph{C;D;C->D;}A->C;}";
332
333 let result = DotGraph::try_from(input).unwrap();
334 let mut map_attribut = HashMap::new();
335 map_attribut.insert("encore".to_string(), "toto".to_string());
336 map_attribut.insert("label".to_string(), "test".to_string());
337 assert_eq!(result.name, "Test".to_string());
338 assert_eq!(result.nodes,
339 vec![
340 Node::new("A",Attributs::default()),
341 Node::new("B", Attributs::from(map_attribut))]);
342 assert_eq!(result.edges,
343 vec![
344 Edge::try_from(("A->B", "->")).unwrap(),
345 Edge::try_from(("B->A[label=\"to B\"", "->")).unwrap(),
346 Edge::try_from(("A->C", "->")).unwrap()]);
347 assert_eq!(result.sous_graphes.len(), 2);
348 }
349
350
351 #[test]
352 fn extract_subgraphes_position_ok() {
353 let combinations :Vec<(&str,Vec<(usize, usize)>)> = vec![
354 ("subgraph{tetsautres}",vec![(0,19)]),
355 ("another what ?subgraph{tetsautres}", vec![(14,33)]),
356 ("subgraph{} subgraph{}",vec![(0,9), (11,20)]),
357 ("subgraph{E;} subgraph{H;}",vec![(0,11), (13,24)]),
358 ("encore un test subgraph{tetsautres} et au subgraph{ } voila du boulout", vec![(15,34),(42,52)]),
359 ("subgraph{C;D;C->D;}\r\n", vec![(0,18)]),
360 ("no sub grhaph", vec![])
361 ];
362
363 combinations.iter()
364 .for_each(|combinaisons|
365 {
366 let result = extract_subgraphes_position(combinaisons.0).unwrap();
367 assert_eq!(result, combinaisons.1);
368 }
369 );
370 }
371
372 #[test]
373 fn extract_subgraphes_ok() {
374 let combinations :Vec<(&str,usize, &str)> = vec![
375 ("subgraph{A->B}", 1, ""),
376 ("another what ?subgraph{C->D}", 1, "another what ?"),
377 ("subgraph{E;} subgraph{H;}",2, " "),
378 ("encore un test subgraph {G->D;} et au subgraph {A;} voila du boulout", 2, "encore un test et au voila du boulout"),
379 ("subgraph{X;Y;Z->E;}\r\n", 1, "\r\n" ),
380 ];
381
382 combinations.iter()
383 .for_each(|combinaisons|
384 {
385 let mut content = combinaisons.0.to_string();
386 let result = DotGraph::extract_subgraphes(&mut content, GraphType::Digraph).unwrap();
387 assert_eq!(result.len(), combinaisons.1);
388 assert_eq!(content, combinaisons.2)
389 }
390 );
391 }
392}