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  }