summaryrefslogtreecommitdiffstats
path: root/template/src/base_dir.rs
blob: 91e824ab67b558a645833b9506247ef354d6c8b5 (plain)
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use std::{
    path::{Path, PathBuf},
    ops::{Deref, DerefMut},
    env, io
};

use serde::{
    ser::{Serialize, Serializer},
    de::{Deserialize, Deserializer}
};

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CwdBaseDir(PathBuf);

impl CwdBaseDir {

    /// Creates a new `CwdBaseDir` instance containing exactly the given path.
    pub fn new_unchanged(path: PathBuf) -> Self {
        CwdBaseDir(path)
    }

    /// Creates a `CwdBaseDir` from a path by prefixing the path with the
    /// current working dir if it's relative.
    ///
    /// If the path is not relative it's directly used.
    ///
    /// # Os state side effects
    ///
    /// As this function accesses the current working directory (CWD) it's
    /// not pure as the CWD can be changed (e.g. by `std::env::set_current_dir`).
    ///
    /// # Error
    ///
    /// As getting the CWD can fail this function can fail with a I/O Error, too.
    pub fn from_path<P>(path: P) -> Result<Self, io::Error>
        where P: AsRef<Path> + Into<PathBuf>
    {
        let path =
            if path.as_ref().is_absolute() {
                path.into()
            } else {
                let mut cwd = env::current_dir()?;
                cwd.push(path.as_ref());
                cwd
            };

        Ok(CwdBaseDir(path))
    }

    /// Turns this path into a `PathBuf` by stripping the current working dir
    /// if it starts with it.
    ///
    /// If this path does not start with the CWD it's returned directly.
    ///
    /// # Os state side effects
    ///
    /// As this function used the  current working dir (CWD) it is affected
    /// by any function changing the CWD as a side effect.
    ///
    /// # Error
    ///
    /// Accessing the current working dir can fail, as such this function
    /// can fail.
    pub fn to_base_path(&self) -> Result<&Path, io::Error> {
        let cwd = env::current_dir()?;
        self.strip_prefix(&cwd)
            .or_else(|_err_does_not_has_that_prefix| {
                Ok(&self)
            })
    }

    /// Turns this instance into the `PathBuf` it dereferences to.
    pub fn into_inner_with_prefix(self) -> PathBuf {
        let CwdBaseDir(path) = self;
        path
    }
}

impl Deref for CwdBaseDir {
    type Target = PathBuf;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for CwdBaseDir {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl AsRef<Path> for CwdBaseDir {
    fn as_ref(&self) -> &Path {
        &self.0
    }
}



impl<'de> Deserialize<'de> for CwdBaseDir {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>
    {
        use serde::de::Error;
        let path_buf = PathBuf::deserialize(deserializer)?;
        Self::from_path(path_buf)
            .map_err(|err| D::Error::custom(err))
    }
}

impl Serialize for CwdBaseDir {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer,
    {
        use serde::ser::Error;
        let path = self.to_base_path()
            .map_err(|err| S::Error::custom(err))?;

        path.serialize(serializer)
    }
}


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

    #[test]
    fn from_path_does_not_affect_absolute_paths() {
        let path = Path::new("/the/dog");
        let base_dir = CwdBaseDir::from_path(path).unwrap();
        assert_eq!(&*base_dir, Path::new("/the/dog"))
    }

    #[test]
    fn from_path_prefixes_with_cwd() {
        let cwd = env::current_dir().unwrap();
        let expected = cwd.join("./the/dog");

        let base_dir = CwdBaseDir::from_path("./the/dog").unwrap();
        assert_eq!(&*base_dir, &expected);
    }

    #[test]
    fn to_base_path_removes_cwd_prefix() {
        let cwd = env::current_dir().unwrap();
        let dir = cwd.join("hy/there");
        let base_dir = CwdBaseDir::new_unchanged(dir);
        let path = base_dir.to_base_path().unwrap();
        assert_eq!(path, Path::new("hy/there"));
    }
}