tiedcrossing_type/tree/
path.rs

1// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use super::Name;
5
6use std::fmt::Display;
7use std::ops::Deref;
8use std::str::FromStr;
9
10use serde::{Deserialize, Serialize};
11
12#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
13pub struct Path(Vec<Name>);
14
15impl Deref for Path {
16    type Target = Vec<Name>;
17
18    fn deref(&self) -> &Self::Target {
19        &self.0
20    }
21}
22
23impl Path {
24    pub const ROOT: Self = Self(vec![]);
25
26    pub fn intersperse(&self, sep: &str) -> String {
27        let mut it = self.0.iter();
28        match it.next() {
29            None => Default::default(),
30            Some(first) => {
31                let mut s = String::with_capacity(
32                    self.0.iter().map(|p| p.len()).sum::<usize>() + self.0.len() - 1,
33                );
34                s.push_str(first);
35                for p in it {
36                    s.push_str(sep);
37                    s.push_str(p);
38                }
39                s
40            }
41        }
42    }
43}
44
45impl From<Name> for Path {
46    fn from(name: Name) -> Self {
47        Self(vec![name])
48    }
49}
50
51impl FromIterator<Name> for Path {
52    fn from_iter<T: IntoIterator<Item = Name>>(iter: T) -> Self {
53        Self(Vec::<Name>::from_iter(iter))
54    }
55}
56
57impl FromStr for Path {
58    type Err = anyhow::Error;
59
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        s.trim_start_matches('/')
62            .split_terminator('/')
63            .map(FromStr::from_str)
64            .collect::<Result<Vec<_>, Self::Err>>()
65            .map(Self)
66    }
67}
68
69impl Display for Path {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        write!(f, "{}", self.intersperse("/"))
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn from_str() {
81        assert_eq!("/".parse::<Path>().unwrap(), Path::ROOT);
82        assert_eq!(
83            "/foo".parse::<Path>().unwrap(),
84            Path(vec!["foo".parse().unwrap()])
85        );
86        assert_eq!(
87            "/foo/".parse::<Path>().unwrap(),
88            Path(vec!["foo".parse().unwrap()])
89        );
90        assert_eq!(
91            "/foo/bar".parse::<Path>().unwrap(),
92            Path(vec!["foo".parse().unwrap(), "bar".parse().unwrap()])
93        );
94    }
95}