1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::path::PathBuf;

pub type Component = String;

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct RelativePathBuf(Vec<Component>);

impl RelativePathBuf {
    pub fn join(mut self, c: Component) -> Self {
        self.0.push(c);
        self
    }

    pub fn from_components<I: Iterator<Item = Component>>(i: I) -> Self {
        RelativePathBuf(i.collect())
    }

    pub fn as_path_buf(&self) -> PathBuf {
        let mut p = PathBuf::new();
        self.0.iter().for_each(|c| p.push(c));
        p
    }

    pub fn as_git_path(&self) -> String {
        self.0
            .iter()
            .map(|x| x.clone())
            .collect::<Vec<_>>()
            .join("/")
    }
}

impl From<&str> for RelativePathBuf {
    fn from(s: &str) -> Self {
        RelativePathBuf::from_components(s.split("/").filter_map(|x| {
            if x.is_empty() {
                None
            } else {
                Some(x.to_owned())
            }
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::RelativePathBuf;

    #[test]
    fn join_works() {
        let mut path = RelativePathBuf(vec![]).join("some".to_owned());
        assert_eq!(path.0.len(), 1);
        assert_eq!(path.0[0], "some");

        path = path.join("dir".to_owned());
        assert_eq!(path.0.len(), 2);
        assert_eq!(path.0[0], "some");
        assert_eq!(path.0[1], "dir");
    }

    #[test]
    fn from_components_works() {
        let path =
            RelativePathBuf::from_components(vec!["some".to_owned(), "dir".to_owned()].into_iter());
        assert_eq!(path.0.len(), 2);
        assert_eq!(path.0[0], "some");
        assert_eq!(path.0[1], "dir");
    }

    #[test]
    fn from_str_works() {
        {
            let path: RelativePathBuf = "some/dir".into();
            assert_eq!(path.0.len(), 2);
            assert_eq!(path.0[0], "some");
            assert_eq!(path.0[1], "dir");
        }

        {
            let path: RelativePathBuf = "some".into();
            assert_eq!(path.0.len(), 1);
            assert_eq!(path.0[0], "some");
        }

        {
            let path: RelativePathBuf = "".into();
            assert_eq!(path.0.len(), 0);
        }

        {
            let path: RelativePathBuf = "/some//dir/".into();
            assert_eq!(path.0.len(), 2);
            assert_eq!(path.0[0], "some");
            assert_eq!(path.0[1], "dir");
        }
    }
}