path_calculate/
calculate.rs

1use std::io::{self, ErrorKind};
2use std::env;
3use std::borrow::Cow;
4use std::path::{Component, Path, PathBuf};
5
6//extern crate path_absolutize;
7use path_absolutize::*;
8
9/// Let `Path` and `PathBuf` have some path calculate methods.
10pub trait Calculate {
11    /// Get current env's home_dir if it exist.
12    fn home_dir(&self) -> io::Result<Cow<Path>>;
13
14    /// Get the absolute path, even if the path is not exist.
15    fn as_absolute_path(&self) -> io::Result<Cow<Path>>;
16
17    /// Get a relative root path betwwen two pathes.
18    fn relative_root_with(&self, path_b: &Path) -> io::Result<Cow<Path>>;
19
20    /// Get dst_path's relative path from the src_path.
21    fn related_to(&self, src_path: &Path) -> io::Result<Cow<Path>>;
22}
23
24impl Calculate for Path {
25    fn home_dir(&self) -> io::Result<Cow<Path>> {
26        #[allow(deprecated)]
27        let home_dir = env::home_dir().unwrap();
28        if home_dir.to_str().unwrap() == "" {
29            // do not set or support env $HOME/~
30            return Err(io::Error::from(ErrorKind::InvalidInput))
31        }
32
33        Ok(Cow::from(home_dir))
34    }
35    fn as_absolute_path(&self) -> io::Result<Cow<Path>> {
36        let mut iters = self.components();
37
38        let first_component = iters.next();
39
40        // if not start with `~`, return self.absolutize() directly.
41        match first_component {
42            Some(Component::RootDir) => {
43                return self.absolutize()
44            },
45            Some(Component::Normal(dir)) => {
46                if dir.to_str().unwrap() == "~" {} else {return self.absolutize()}
47            },
48            None => {
49                return self.absolutize()
50            }
51            _ => {},
52        }
53
54        // here get replace HOME by abs_path
55        let mut path_buf = PathBuf::new();
56
57        let home_dir = self.home_dir()?;
58        let home_iters = home_dir.components();
59        for iter in home_iters {
60            path_buf.push(iter)
61        }
62
63        for iter in iters {
64            path_buf.push(iter)
65        }
66
67        Ok(Cow::from(path_buf))
68    }
69
70    fn relative_root_with(&self, path_b: &Path) -> io::Result<Cow<Path>> {
71        // Absolutize 
72        let pa = self.as_absolute_path()?;
73        let pb = path_b.as_absolute_path()?;
74
75        // new pathbuf
76        let mut path_buf = PathBuf::new();
77
78        let mut itera = pa.components();
79        let mut iterb = pb.components();
80
81        let first_componenta = itera.next().unwrap();
82        let first_componentb = iterb.next().unwrap();
83
84        // On Windows, do not support diff Prefix Pathes calculate.
85        if first_componenta == first_componentb {
86            path_buf.push(first_componenta);
87        } else {
88            return Err(io::Error::from(ErrorKind::InvalidInput))
89        }
90
91        for componenta in itera {
92            if let Some(componentb) = iterb.next() {
93               if componenta == componentb {
94                   path_buf.push(componenta);
95               } else {
96                   break;
97               }
98            }
99        }
100
101        Ok(Cow::from(path_buf))
102    }
103
104    fn related_to(&self, src_path: &Path) -> io::Result<Cow<Path>> {
105        // /home/cc/work/a related_to /home/cc => work/a
106        // /home/cc/work/a related_to /home/cc/App/demo => ../../work/a
107        // return a absolutily path
108        let pa = self.as_absolute_path().unwrap();
109        let pb = src_path.as_absolute_path().unwrap();
110        let relative_root = self.relative_root_with(src_path).unwrap();
111
112        let mut path_buf = PathBuf::new();
113
114        // pop relative_root
115        let mut itera = pa.components();
116        let mut iterb = pb.components();
117
118        let iterr = relative_root.components();
119
120        // drop same root
121        for _item in iterr {
122            itera.next();
123            iterb.next();
124        }
125
126        // from src to relative_root
127        for _item in iterb {
128            path_buf.push(Component::ParentDir);
129        }
130
131        // relative_root to self
132        for item in itera {
133            path_buf.push(item)
134        }
135
136        Ok(Cow::from(path_buf))
137    }
138}
139
140impl Calculate for PathBuf {
141    fn home_dir(&self) -> io::Result<Cow<Path>> {
142        self.as_path().home_dir()
143    }
144
145    fn as_absolute_path(&self) -> io::Result<Cow<Path>> {
146        self.as_path().as_absolute_path()
147    }
148
149    fn relative_root_with(&self, path_b: &Path) -> io::Result<Cow<Path>> {
150        self.as_path().relative_root_with(path_b)
151    }
152
153    fn related_to(&self, src_path: &Path) -> io::Result<Cow<Path>> {
154        self.as_path().related_to(src_path)
155    }
156}