hydrate_model/
asset_path.rs

1// assumed to end with /. We don't just use / to make it clear that it's not a file path
2
3//TODO: I want to change this "db:" to match the data source name
4const ROOT_PATH_STR: &str = "db:/";
5const ROOT_PATH: AssetPath = AssetPath(None);
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct AssetPath(Option<String>);
9
10impl AssetPath {
11    pub fn new(s: &str) -> Self {
12        // We assume all paths are absolute
13        if !s.starts_with(ROOT_PATH_STR) {
14            panic!("Invalid asset path str");
15        }
16
17        if s.len() == ROOT_PATH_STR.len() {
18            AssetPath(None)
19        } else {
20            AssetPath(Some(s.to_string()))
21        }
22    }
23
24    pub fn root_ref() -> &'static Self {
25        &ROOT_PATH
26    }
27
28    pub fn new_root(name: &str) -> Self {
29        assert!(!name.is_empty());
30        AssetPath(Some(format!("{}://", name)))
31    }
32
33    pub fn join(
34        &self,
35        rhs: &str,
36    ) -> AssetPath {
37        if rhs.is_empty() {
38            return self.clone();
39        }
40
41        // Joining an absolute path to an absolute path is not allowed
42        assert!(!rhs.starts_with(ROOT_PATH_STR));
43        assert!(!rhs.contains("/"));
44
45        match &self.0 {
46            Some(x) => {
47                if x.ends_with("/") {
48                    AssetPath(Some(format!("{}{}", x, rhs)))
49                } else {
50                    AssetPath(Some(format!("{}/{}", x, rhs)))
51                }
52            }
53            None => AssetPath(Some(format!("{}{}", ROOT_PATH_STR, rhs))),
54        }
55    }
56
57    pub fn parent_path_and_name(&self) -> Option<(Self, String)> {
58        match &self.0 {
59            None => None, // Parent of root path does not exist
60            Some(path) => {
61                if let Some(index) = path.rfind("/") {
62                    if index >= ROOT_PATH_STR.len() {
63                        // We have a parent path that isn't root
64                        let parent = AssetPath(Some(path[0..index].to_string()));
65                        let name = path[index + 1..].to_string();
66                        Some((parent, name))
67                    } else {
68                        // Parent path is root
69                        let parent = AssetPath(None);
70                        let name = path[ROOT_PATH_STR.len()..].to_string();
71                        Some((parent, name))
72                    }
73                } else {
74                    // Path with no slash should not exist
75                    unimplemented!()
76                }
77            }
78        }
79    }
80
81    pub fn is_root_path(&self) -> bool {
82        return self.0.is_none();
83    }
84
85    pub fn split_components(&self) -> Vec<&str> {
86        match &self.0 {
87            Some(x) => x.split("/").skip(1).collect(),
88            None => vec![],
89        }
90    }
91
92    pub fn as_str(&self) -> &str {
93        self.0.as_ref().map(|x| x.as_str()).unwrap_or(ROOT_PATH_STR)
94    }
95
96    pub fn starts_with(
97        &self,
98        other: &AssetPath,
99    ) -> bool {
100        self.as_str().starts_with(other.as_str())
101    }
102}
103
104impl From<&str> for AssetPath {
105    fn from(s: &str) -> Self {
106        AssetPath::new(s)
107    }
108}
109
110impl From<String> for AssetPath {
111    fn from(s: String) -> Self {
112        AssetPath::new(&s)
113    }
114}