pub_just/
module_path.rs

1use super::*;
2
3#[derive(Debug, PartialEq, Clone)]
4pub struct ModulePath {
5  pub path: Vec<String>,
6  pub spaced: bool,
7}
8
9impl TryFrom<&[&str]> for ModulePath {
10  type Error = ();
11
12  fn try_from(path: &[&str]) -> Result<Self, Self::Error> {
13    let spaced = path.len() > 1;
14
15    let path = if path.len() == 1 {
16      let first = path[0];
17
18      if first.starts_with(':') || first.ends_with(':') || first.contains(":::") {
19        return Err(());
20      }
21
22      first
23        .split("::")
24        .map(str::to_string)
25        .collect::<Vec<String>>()
26    } else {
27      path.iter().map(|s| (*s).to_string()).collect()
28    };
29
30    for name in &path {
31      if name.is_empty() {
32        return Err(());
33      }
34
35      for (i, c) in name.chars().enumerate() {
36        if i == 0 {
37          if !Lexer::is_identifier_start(c) {
38            return Err(());
39          }
40        } else if !Lexer::is_identifier_continue(c) {
41          return Err(());
42        }
43      }
44    }
45
46    Ok(Self { path, spaced })
47  }
48}
49
50impl Display for ModulePath {
51  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
52    for (i, name) in self.path.iter().enumerate() {
53      if i > 0 {
54        if self.spaced {
55          write!(f, " ")?;
56        } else {
57          write!(f, "::")?;
58        }
59      }
60      write!(f, "{name}")?;
61    }
62    Ok(())
63  }
64}
65
66#[cfg(test)]
67mod tests {
68  use super::*;
69
70  #[test]
71  fn try_from_ok() {
72    #[track_caller]
73    fn case(path: &[&str], expected: &[&str], display: &str) {
74      let actual = ModulePath::try_from(path).unwrap();
75      assert_eq!(actual.path, expected);
76      assert_eq!(actual.to_string(), display);
77    }
78
79    case(&[], &[], "");
80    case(&["foo"], &["foo"], "foo");
81    case(&["foo0"], &["foo0"], "foo0");
82    case(&["foo", "bar"], &["foo", "bar"], "foo bar");
83    case(&["foo::bar"], &["foo", "bar"], "foo::bar");
84  }
85
86  #[test]
87  fn try_from_err() {
88    #[track_caller]
89    fn case(path: &[&str]) {
90      assert!(ModulePath::try_from(path).is_err());
91    }
92
93    case(&[":foo"]);
94    case(&["foo:"]);
95    case(&["foo:::bar"]);
96    case(&["0foo"]);
97    case(&["f$oo"]);
98    case(&[""]);
99  }
100}