github.com/charypar/monobuild@v0.0.0-20211122220434-fd884ed50212/rs/src/read.rs (about)

     1  use std::collections::{BTreeMap, BTreeSet, HashSet};
     2  use std::fmt::Display;
     3  
     4  use crate::core::Dependency;
     5  use crate::graph::Graph;
     6  
     7  #[derive(PartialEq, Debug)]
     8  pub enum Warning {
     9      Unknown(String, String),      // dependent, dependency
    10      BadLineFormat(usize, String), // line number, line
    11  }
    12  
    13  impl Display for Warning {
    14      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    15          match self {
    16              Warning::Unknown(of, what) => write!(f, "Unknown dependency {} of {}.", what, of),
    17              Warning::BadLineFormat(l, line) => write!(f, "Bad line format: {}: '{}' expected 'component: dependency, dependency, dependency, ...", l, line),
    18          }
    19      }
    20  }
    21  
    22  fn manifest(manifest: &str) -> HashSet<(String, Dependency)> {
    23      manifest
    24          .lines()
    25          .flat_map(|l| match l.trim().trim_end_matches("/") {
    26              "" => None,
    27              d if d.starts_with("#") => None,
    28              d if d.starts_with("!") => Some((d[1..].to_string(), Dependency::Strong)),
    29              d => Some((d.to_string(), Dependency::Weak)),
    30          })
    31          .collect()
    32  }
    33  
    34  pub fn manifests(manifests: BTreeMap<String, String>) -> (Graph<String, Dependency>, Vec<Warning>) {
    35      let components: BTreeSet<_> = manifests.keys().cloned().collect();
    36  
    37      let mut warnings = Vec::new();
    38      let graph = manifests
    39          .into_iter()
    40          .map(|(c, m)| {
    41              let (deps, ws): (HashSet<_>, HashSet<_>) = manifest(&m)
    42                  .into_iter()
    43                  .partition(|(to, _)| components.contains(to));
    44  
    45              let mut warns = ws
    46                  .into_iter()
    47                  .map(|(to, _)| Warning::Unknown(c.clone(), to))
    48                  .collect();
    49  
    50              warnings.append(&mut warns);
    51  
    52              (c, deps)
    53          })
    54          .into();
    55  
    56      (graph, warnings)
    57  }
    58  
    59  pub fn repo_manifest(manifest: String) -> (Graph<String, Dependency>, Vec<Warning>) {
    60      let mut warnings = Vec::new();
    61  
    62      let lines = manifest
    63          .lines()
    64          .map(|l| l.trim())
    65          .filter(|l| !l.starts_with("#") && *l != "")
    66          .enumerate();
    67  
    68      let graph = lines
    69          .filter_map(|(i, line)| {
    70              if let Some((c, ds)) = line.split_once(":") {
    71                  let component = c.trim().to_owned();
    72                  let dependencies: HashSet<_> = ds
    73                      .trim()
    74                      .split(",")
    75                      .map(|d| d.trim())
    76                      .filter(|d| *d != "")
    77                      .map(|d| {
    78                          if d.starts_with("!") {
    79                              (d[1..].to_owned(), Dependency::Strong)
    80                          } else {
    81                              (d.to_owned(), Dependency::Weak)
    82                          }
    83                      })
    84                      .collect();
    85  
    86                  Some((component, dependencies))
    87              } else {
    88                  warnings.push(Warning::BadLineFormat(i, line.to_owned()));
    89                  None
    90              }
    91          })
    92          .into();
    93  
    94      (graph, warnings)
    95  }
    96  
    97  #[cfg(test)]
    98  mod test {
    99      mod manifest {
   100          use super::super::*;
   101          use std::collections::HashSet;
   102  
   103          #[test]
   104          fn empty() {
   105              let text = "\n   \n \n   # comment   \n";
   106  
   107              let actual = manifest(text);
   108              let expected = HashSet::new();
   109  
   110              assert_eq!(actual, expected);
   111          }
   112  
   113          #[test]
   114          fn single_dependency() {
   115              let text = "\n   other/\n \n   \n";
   116  
   117              let actual = manifest(text);
   118              let expected = vec![("other".to_string(), Dependency::Weak)]
   119                  .into_iter()
   120                  .collect();
   121  
   122              assert_eq!(actual, expected);
   123          }
   124  
   125          #[test]
   126          fn single_strong_dependency() {
   127              let text = "\n   !other\n \n   \n";
   128  
   129              let actual = manifest(text);
   130              let expected = vec![("other".to_string(), Dependency::Strong)]
   131                  .into_iter()
   132                  .collect();
   133  
   134              assert_eq!(actual, expected)
   135          }
   136  
   137          #[test]
   138          fn full() {
   139              let text = "\n  some  \n !other\n \none/more  \n  # comment  \n";
   140  
   141              let actual = manifest(text);
   142              let expected = vec![
   143                  ("some".to_string(), Dependency::Weak),
   144                  ("other".to_string(), Dependency::Strong),
   145                  ("one/more".to_string(), Dependency::Weak),
   146              ]
   147              .into_iter()
   148              .collect();
   149  
   150              assert_eq!(actual, expected)
   151          }
   152      }
   153  
   154      mod manifests {
   155          use crate::graph::Graph;
   156          use std::collections::BTreeMap;
   157  
   158          use super::super::*;
   159  
   160          #[test]
   161          fn no_manifests() {
   162              let files = BTreeMap::new();
   163  
   164              let actual = manifests(files);
   165              let expected = (Graph::new(), Vec::new());
   166  
   167              assert_eq!(actual, expected);
   168          }
   169  
   170          #[test]
   171          fn ignore_unknown_components() {
   172              let files = vec![("foo".to_string(), "\n bar\n".to_string())]
   173                  .into_iter()
   174                  .collect();
   175  
   176              let actual = manifests(files);
   177              let expected = (
   178                  Graph::from([("foo".into(), [])]),
   179                  vec![Warning::Unknown("foo".to_string(), "bar".to_string())],
   180              );
   181  
   182              assert_eq!(actual, expected);
   183          }
   184  
   185          #[test]
   186          fn two_manifests() {
   187              let files = vec![
   188                  ("foo".to_string(), "\n bar\nbaz".to_string()),
   189                  ("bar".to_string(), "\n baz\n".to_string()),
   190              ]
   191              .into_iter()
   192              .collect();
   193  
   194              let actual = manifests(files);
   195              let expected = (
   196                  Graph::from([
   197                      ("foo".into(), vec![("bar".into(), Dependency::Weak)]),
   198                      ("bar".into(), vec![]),
   199                  ]),
   200                  vec![
   201                      Warning::Unknown("bar".to_string(), "baz".to_string()),
   202                      Warning::Unknown("foo".to_string(), "baz".to_string()),
   203                  ],
   204              );
   205  
   206              assert_eq!(actual, expected);
   207          }
   208  
   209          #[test]
   210          fn complex_manifests() {
   211              let files = vec![
   212                  ("app1".to_string(), "\nlibs/lib1\nlibs/lib2/".to_string()),
   213                  ("app2".to_string(), "\nlibs/lib2\n\n\nlibs/lib3".to_string()),
   214                  ("app3".to_string(), "\n\nlibs/lib3".to_string()),
   215                  ("app4".to_string(), "\n\n# yo".to_string()),
   216                  ("libs/lib1".to_string(), "\n libs/lib3\n".to_string()),
   217                  ("libs/lib2".to_string(), "\n libs/lib3\n".to_string()),
   218                  ("libs/lib3".to_string(), "".to_string()),
   219                  (
   220                      "stack1".to_string(),
   221                      "# frontend\n!app1\n\n# backend\n!app2\n!app3".to_string(),
   222                  ),
   223              ]
   224              .into_iter()
   225              .collect();
   226  
   227              let actual = manifests(files);
   228              let expected = (
   229                  Graph::from([
   230                      (
   231                          "app1".into(),
   232                          vec![
   233                              ("libs/lib1".into(), Dependency::Weak),
   234                              ("libs/lib2".into(), Dependency::Weak),
   235                          ],
   236                      ),
   237                      (
   238                          "app2".into(),
   239                          vec![
   240                              ("libs/lib2".into(), Dependency::Weak),
   241                              ("libs/lib3".into(), Dependency::Weak),
   242                          ],
   243                      ),
   244                      ("app3".into(), vec![("libs/lib3".into(), Dependency::Weak)]),
   245                      ("app4".into(), vec![]),
   246                      (
   247                          "libs/lib1".into(),
   248                          vec![("libs/lib3".into(), Dependency::Weak)],
   249                      ),
   250                      (
   251                          "libs/lib2".into(),
   252                          vec![("libs/lib3".into(), Dependency::Weak)],
   253                      ),
   254                      ("libs/lib3".into(), vec![]),
   255                      (
   256                          "stack1".into(),
   257                          vec![
   258                              ("app1".into(), Dependency::Strong),
   259                              ("app2".into(), Dependency::Strong),
   260                              ("app3".into(), Dependency::Strong),
   261                          ],
   262                      ),
   263                  ]),
   264                  vec![],
   265              );
   266  
   267              assert_eq!(actual, expected);
   268          }
   269      }
   270  
   271      mod repo_manifest {
   272          use super::super::*;
   273          use crate::graph::Graph;
   274  
   275          #[test]
   276          fn empty_manifest() {
   277              let manifest = "".into();
   278  
   279              let (actual, _) = repo_manifest(manifest);
   280              let expected = Graph::new();
   281  
   282              assert_eq!(actual, expected);
   283          }
   284  
   285          #[test]
   286          fn single_component() {
   287              let manifest = "lib1:".into();
   288  
   289              let (actual, _) = repo_manifest(manifest);
   290              let expected = Graph::from([("lib1".into(), vec![])]);
   291  
   292              assert_eq!(actual, expected);
   293          }
   294  
   295          #[test]
   296          fn component_with_depednency() {
   297              let manifest = "lib1: lib2\nlib2:".into();
   298  
   299              let (actual, _) = repo_manifest(manifest);
   300              let expected = Graph::from([
   301                  ("lib1".into(), vec![("lib2".into(), Dependency::Weak)]),
   302                  ("lib2".into(), vec![]),
   303              ]);
   304  
   305              assert_eq!(actual, expected);
   306          }
   307  
   308          #[test]
   309          fn component_with_mutlitple_depednencies() {
   310              let manifest = "lib1: lib2, lib3\nlib2: \nlib3: ".into();
   311  
   312              let (actual, _) = repo_manifest(manifest);
   313              let expected = Graph::from([(
   314                  "lib1".into(),
   315                  vec![
   316                      ("lib2".into(), Dependency::Weak),
   317                      ("lib3".into(), Dependency::Weak),
   318                  ],
   319              )]); // Note that 'new' normalises the graph!
   320  
   321              assert_eq!(actual, expected);
   322          }
   323  
   324          #[test]
   325          fn component_with_unlisted_dependency() {
   326              let manifest = "lib1: lib2, lib3\n".into();
   327  
   328              let (actual, _) = repo_manifest(manifest);
   329              let expected = Graph::from([(
   330                  "lib1".into(),
   331                  vec![
   332                      ("lib2".into(), Dependency::Weak),
   333                      ("lib3".into(), Dependency::Weak),
   334                  ],
   335              )]); // Note that 'new' normalises the graph!
   336  
   337              assert_eq!(actual, expected);
   338          }
   339  
   340          #[test]
   341          fn complex_manifest() {
   342              let manifest = "# comment\napp1: lib1, lib2, lib3\napp2: \nlib1: \nlib2: lib3\nlib3: \n\nstack1: !app1, !app2".to_owned();
   343  
   344              let (actual, ws) = repo_manifest(manifest);
   345              let expected = Graph::from([
   346                  (
   347                      "app1".into(),
   348                      vec![
   349                          ("lib1".into(), Dependency::Weak),
   350                          ("lib2".into(), Dependency::Weak),
   351                          ("lib3".into(), Dependency::Weak),
   352                      ],
   353                  ),
   354                  ("lib2".into(), vec![("lib3".into(), Dependency::Weak)]),
   355                  (
   356                      "stack1".into(),
   357                      vec![
   358                          ("app1".into(), Dependency::Strong),
   359                          ("app2".into(), Dependency::Strong),
   360                      ],
   361                  ),
   362              ]);
   363  
   364              assert_eq!(actual, expected);
   365              assert_eq!(ws, vec![]);
   366          }
   367      }
   368  }