github.com/charypar/monobuild@v0.0.0-20211122220434-fd884ed50212/rs/src/write.rs (about) 1 use std::fmt::Display; 2 3 use crate::core::Dependency; 4 5 pub enum TextFormat { 6 Simple, 7 Full, 8 } 9 10 pub struct Text<'g, G, V, InnIt> 11 where 12 G: IntoIterator<Item = (&'g V, InnIt)>, 13 V: Clone + PartialEq + 'g, 14 InnIt: Iterator<Item = (&'g V, Dependency)>, 15 { 16 graph: G, 17 format: TextFormat, 18 } 19 20 pub enum DotFormat { 21 Dependencies, 22 Schedule, 23 } 24 25 pub struct Dot<'g, G, V, InnIt> 26 where 27 G: IntoIterator<Item = (&'g V, InnIt)>, 28 V: Clone + PartialEq + 'g, 29 InnIt: Iterator<Item = (&'g V, Dependency)>, 30 { 31 graph: G, 32 format: DotFormat, 33 } 34 35 pub fn to_text<'g, G, V, InnIt>(graph: G, format: TextFormat) -> Text<'g, G, V, InnIt> 36 where 37 G: IntoIterator<Item = (&'g V, InnIt)> + Clone, 38 V: Clone + PartialEq + 'g, 39 InnIt: Iterator<Item = (&'g V, Dependency)>, 40 { 41 Text { graph, format } 42 } 43 44 pub fn to_dot<'g, G, V, InnIt>(graph: G, format: DotFormat) -> Dot<'g, G, V, InnIt> 45 where 46 G: IntoIterator<Item = (&'g V, InnIt)> + Clone, 47 V: Clone + Ord, 48 InnIt: Iterator<Item = (&'g V, Dependency)>, 49 { 50 Dot { graph, format } 51 } 52 53 impl<'g, G, V, InnIt> Display for Text<'g, G, V, InnIt> 54 where 55 G: IntoIterator<Item = (&'g V, InnIt)> + Clone, 56 V: Clone + Display + PartialEq, 57 InnIt: Iterator<Item = (&'g V, Dependency)>, 58 { 59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 60 for (component, dependencies) in self.graph.clone() { 61 let joined = dependencies 62 .map(|(d, kind)| match self.format { 63 TextFormat::Full if kind == Dependency::Strong => format!("!{}", d), 64 _ => format!("{}", d), 65 }) 66 .collect::<Vec<_>>() 67 .join(", "); 68 69 writeln!(f, "{}: {}", component, joined)?; 70 } 71 72 Ok(()) 73 } 74 } 75 76 impl<'g, G, V, InnIt> Display for Dot<'g, G, V, InnIt> 77 where 78 G: IntoIterator<Item = (&'g V, InnIt)> + Clone, 79 V: Clone + Ord + Display, 80 InnIt: Iterator<Item = (&'g V, Dependency)>, 81 { 82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 83 // FIXME check schedule prints correctly (may need reversing) 84 match self.format { 85 DotFormat::Dependencies => write!(f, "digraph dependencies {{\n")?, 86 DotFormat::Schedule => write!( 87 f, 88 "digraph schedule {{\n randir=\"LR\"\n node [shape=box]\n" 89 )?, 90 } 91 92 for (cmp, dependencies) in self.graph.clone() { 93 let mut deps = dependencies.peekable(); 94 if let None = deps.peek() { 95 write!(f, " \"{}\"\n", cmp)?; 96 continue; 97 } 98 99 for (dep, kind) in deps { 100 match kind { 101 Dependency::Weak => write!(f, " \"{}\" -> \"{}\" [style=dashed]\n", cmp, dep)?, 102 Dependency::Strong => write!(f, " \"{}\" -> \"{}\"\n", cmp, dep)?, 103 } 104 } 105 } 106 107 write!(f, "}}\n")?; 108 109 Ok(()) 110 } 111 } 112 113 #[cfg(test)] 114 mod tests { 115 use crate::core::Dependency; 116 use crate::graph::Graph; 117 118 mod text { 119 use super::super::{to_text, TextFormat::Full, TextFormat::Simple}; 120 use super::*; 121 122 #[test] 123 fn empty_graph() { 124 let graph = example(); 125 let filtered = graph.filter_vertices(|_| false); 126 127 let actual = format!("{}", to_text(&filtered, Simple)); 128 let expected = ""; 129 130 assert_eq!(actual, expected); 131 } 132 133 #[test] 134 fn single_vertex() { 135 let graph = example(); 136 let filtered = graph.filter_vertices(|v| *v == "a".to_string()); 137 138 let actual = format!("{}", to_text(&filtered, Simple)); 139 let expected = "a: \n"; 140 141 assert_eq!(actual, expected); 142 } 143 144 #[test] 145 fn single_edge() { 146 let graph = example(); 147 let filtered = graph.filter_vertices(|v| ["a", "b"].contains(&v.as_str())); 148 149 let actual = format!("{}", to_text(&filtered, Simple)); 150 let expected = "a: b\nb: \n"; 151 152 assert_eq!(actual, expected); 153 } 154 155 #[test] 156 fn edge_fan() { 157 let graph = example(); 158 let filtered = graph.filter_vertices(|v| ["a", "b", "c"].contains(&v.as_str())); 159 160 let actual = format!("{}", to_text(&filtered, Simple)); 161 let expected = "a: b, c\nb: c\nc: \n"; 162 163 assert_eq!(actual, expected); 164 } 165 166 #[test] 167 fn graph() { 168 let graph = example(); 169 let filtered = graph.filter_vertices(|v| ["a", "b", "c", "d"].contains(&v.as_str())); 170 171 let actual = format!("{}", to_text(&filtered, Simple)); 172 let expected = "a: b, c\nb: c\nc: \nd: a\n"; 173 174 assert_eq!(actual, expected); 175 } 176 177 #[test] 178 fn full_format() { 179 let graph = example(); 180 let filtered = graph.filter_vertices(|v| ["a", "b", "c", "d"].contains(&v.as_str())); 181 182 let actual = format!("{}", to_text(&filtered, Full)); 183 let expected = "a: b, c\nb: c\nc: \nd: !a\n"; 184 185 assert_eq!(actual, expected); 186 } 187 } 188 189 mod dot { 190 use super::super::{to_dot, DotFormat::Dependencies, DotFormat::Schedule}; 191 use super::*; 192 193 #[test] 194 fn empty_graph() { 195 let graph = example(); 196 let filtered = graph.filter_vertices(|_| false); 197 198 let actual = format!("{}", to_dot(&filtered, Dependencies)); 199 let expected = "digraph dependencies {\n}\n"; 200 201 assert_eq!(actual, expected); 202 } 203 204 #[test] 205 fn single_vertex() { 206 let graph = example(); 207 let filtered = graph.filter_vertices(|v| *v == "a".to_string()); 208 209 let actual = format!("{}", to_dot(&filtered, Dependencies)); 210 let expected = "digraph dependencies {\n \"a\"\n}\n"; 211 212 assert_eq!(actual, expected); 213 } 214 215 #[test] 216 fn single_edge() { 217 let graph = example(); 218 let filtered = graph.filter_vertices(|v| ["a", "b"].contains(&v.as_str())); 219 220 let actual = format!("{}", to_dot(&filtered, Dependencies)); 221 let expected = "digraph dependencies {\n \"a\" -> \"b\" [style=dashed]\n \"b\"\n}\n"; 222 223 assert_eq!(actual, expected); 224 } 225 226 #[test] 227 fn single_strong_edge() { 228 let graph = example(); 229 let filtered = graph.filter_vertices(|v| ["a", "d"].contains(&v.as_str())); 230 231 let actual = format!("{}", to_dot(&filtered, Dependencies)); 232 let expected = "digraph dependencies {\n \"a\"\n \"d\" -> \"a\"\n}\n"; 233 234 assert_eq!(actual, expected); 235 } 236 237 #[test] 238 fn graph() { 239 let graph = example(); 240 let filtered = graph.filter_vertices(|v| ["a", "b", "c", "d"].contains(&v.as_str())); 241 242 let actual = format!("{}", to_dot(&filtered, Dependencies)); 243 let expected = "digraph dependencies {\n \ 244 \"a\" -> \"b\" [style=dashed]\n \ 245 \"a\" -> \"c\" [style=dashed]\n \ 246 \"b\" -> \"c\" [style=dashed]\n \ 247 \"c\"\n \ 248 \"d\" -> \"a\"\n\ 249 }\n"; 250 251 assert_eq!(actual, expected); 252 } 253 254 #[test] 255 fn schedule() { 256 let graph = example().reverse(); 257 let filtered = graph.filter_edges(|c| *c == Dependency::Strong); 258 259 let actual = format!("{}", to_dot(&filtered, Schedule)); 260 let expected = "digraph schedule {\n \ 261 randir=\"LR\"\n \ 262 node [shape=box]\n \ 263 \"a\" -> \"d\"\n \ 264 \"a\" -> \"e\"\n \ 265 \"b\" -> \"e\"\n \ 266 \"c\"\n \ 267 \"d\"\n \ 268 \"e\"\n\ 269 }\n"; 270 271 assert_eq!(actual, expected); 272 } 273 } 274 275 // Fixture 276 277 fn example() -> Graph<String, Dependency> { 278 Graph::from([ 279 ( 280 "a".into(), 281 vec![ 282 ("b".into(), Dependency::Weak), 283 ("c".into(), Dependency::Weak), 284 ], 285 ), 286 ("b".into(), vec![("c".into(), Dependency::Weak)]), 287 ("c".into(), vec![]), 288 ("d".into(), vec![("a".into(), Dependency::Strong)]), 289 ( 290 "e".into(), 291 vec![ 292 ("a".into(), Dependency::Strong), 293 ("b".into(), Dependency::Strong), 294 ], 295 ), 296 ]) 297 } 298 }