1use crate::petri_net::PetriNet;
2
3const MAX_TOKENS_AS_DOT: usize = 5;
4
5impl PetriNet {
6 pub fn to_dot_string(&self) -> Result<String, std::io::Error> {
12 let mut writer = Vec::new();
13 self.to_dot(&mut writer)?;
14 String::from_utf8(writer).map_err(|_|
15 std::io::Error::new(
17 std::io::ErrorKind::Other,
18 "Could not convert the string to UTF-8",
19 ))
20 }
21
22 pub fn to_dot<T>(&self, writer: &mut T) -> Result<(), std::io::Error>
29 where
30 T: std::io::Write,
31 {
32 writer.write_all(b"digraph petrinet {\n")?;
33 self.write_dot_places(writer)?;
34 self.write_dot_transitions(writer)?;
35 self.write_dot_arcs(writer)?;
36 writer.write_all(b"}\n")?;
37 Ok(())
38 }
39
40 fn write_dot_places<T>(&self, writer: &mut T) -> Result<(), std::io::Error>
43 where
44 T: std::io::Write,
45 {
46 for (place_ref, place) in self.places_iter() {
47 let label = Self::sanitize_string(place_ref.label());
48 let marking = Self::marking_to_string(place.marking());
49 let line =
50 format!(" {label} [shape=\"circle\" xlabel=\"{label}\" label=\"{marking}\"];\n");
51 writer.write_all(line.as_bytes())?;
52 }
53 Ok(())
54 }
55
56 fn write_dot_transitions<T>(&self, writer: &mut T) -> Result<(), std::io::Error>
59 where
60 T: std::io::Write,
61 {
62 for (transition_ref, _) in self.transitions_iter() {
63 let label = Self::sanitize_string(transition_ref.label());
64 let line = format!(" {label} [shape=\"box\" xlabel=\"\" label=\"{label}\"];\n");
65 writer.write_all(line.as_bytes())?;
66 }
67 Ok(())
68 }
69
70 fn write_dot_arcs<T>(&self, writer: &mut T) -> Result<(), std::io::Error>
73 where
74 T: std::io::Write,
75 {
76 let arcs = self.find_arcs_place_transition();
77 for (place_ref, transition_ref) in arcs {
78 let line = format!(
79 " {} -> {};\n",
80 Self::sanitize_string(place_ref.label()),
81 Self::sanitize_string(transition_ref.label())
82 );
83 writer.write_all(line.as_bytes())?;
84 }
85
86 let arcs = self.find_arcs_transition_place();
87 for (transition_ref, place_ref) in arcs {
88 let line = format!(
89 " {} -> {};\n",
90 Self::sanitize_string(transition_ref.label()),
91 Self::sanitize_string(place_ref.label()),
92 );
93 writer.write_all(line.as_bytes())?;
94 }
95
96 Ok(())
97 }
98
99 fn sanitize_string(string: &str) -> String {
105 string.replace('\n', "").replace('\"', "\\\"")
106 }
107
108 fn marking_to_string(marking: usize) -> String {
110 match marking {
111 0 => String::new(),
112 1..=MAX_TOKENS_AS_DOT => "•".repeat(marking),
113 _ => marking.to_string(),
114 }
115 }
116}
117
118#[cfg(test)]
119mod dot_tests {
120 use super::*;
121 use crate::export::test_export_examples::*;
122 use crate::net_creator::*;
123
124 #[test]
125 fn dot_string_empty_net() {
126 let net = PetriNet::new();
127 let result = net.to_dot_string();
128 assert!(result.is_ok());
129 assert_eq!(result.unwrap(), DOT_STRING_EMPTY_NET);
130 }
131
132 #[test]
133 fn dot_string_only_empty_places_net() {
134 let (net, _, _) = create_basic_unconnected_net(5, 0);
135 let result = net.to_dot_string();
136
137 assert!(result.is_ok());
138 assert_eq!(result.unwrap(), DOT_STRING_ONLY_EMPTY_PLACES_NET);
139 }
140
141 #[test]
142 fn dot_string_marked_places_net() {
143 let mut net = PetriNet::new();
144 let p1 = net.add_place("P1");
145 let p2 = net.add_place("P2");
146 let p3 = net.add_place("P3");
147 let p4 = net.add_place("P4");
148 let p5 = net.add_place("P5");
149
150 assert!(net.add_token(&p1, 5).is_ok());
151 assert!(net.add_token(&p2, 6).is_ok());
152 assert!(net.add_token(&p3, 3).is_ok());
153 assert!(net.add_token(&p4, 2).is_ok());
154 assert!(net.add_token(&p5, 1).is_ok());
155 let result = net.to_dot_string();
156
157 assert!(result.is_ok());
158 assert_eq!(result.unwrap(), DOT_STRING_MARKED_PLACES_NET);
159 }
160
161 #[test]
162 fn dot_string_only_empty_transitions_net() {
163 let (net, _, _) = create_basic_unconnected_net(0, 5);
164 let result = net.to_dot_string();
165
166 assert!(result.is_ok());
167 assert_eq!(result.unwrap(), DOT_STRING_ONLY_EMPTY_TRANSITIONS_NET);
168 }
169
170 #[test]
171 fn dot_string_net_with_chain_topology() {
172 let (net, _, _) = create_net_chain_topology(3);
173 let result = net.to_dot_string();
174
175 assert!(result.is_ok());
176 assert_eq!(result.unwrap(), DOT_STRING_NET_WITH_CHAIN_TOPOLOPY);
177 }
178
179 #[test]
180 fn dot_string_net_with_loop_topology() {
181 let (net, _, _) = create_net_loop_topology();
182 let result = net.to_dot_string();
183
184 assert!(result.is_ok());
185 assert_eq!(result.unwrap(), DOT_STRING_NET_WITH_LOOP_TOPOLOGY);
186 }
187}