path_absolutize/
unix.rs

1use std::{
2    borrow::Cow,
3    ffi::OsString,
4    io::{self, ErrorKind},
5    path::{Component, Path, PathBuf},
6};
7
8use crate::{
9    path_dedot::{ParseDot, MAIN_SEPARATOR},
10    Absolutize,
11};
12
13impl Absolutize for Path {
14    #[inline]
15    fn absolutize(&self) -> io::Result<Cow<Path>> {
16        let cwd = get_cwd!();
17
18        self.absolutize_from(cwd)
19    }
20
21    fn absolutize_from(&self, cwd: impl AsRef<Path>) -> io::Result<Cow<Path>> {
22        let mut iter = self.components();
23
24        let mut has_change = false;
25
26        if let Some(first_component) = iter.next() {
27            let mut tokens = Vec::new();
28
29            let first_is_root = match first_component {
30                Component::RootDir => {
31                    tokens.push(MAIN_SEPARATOR.as_os_str());
32
33                    true
34                },
35                Component::CurDir => {
36                    has_change = true;
37
38                    let cwd = cwd.as_ref();
39
40                    for token in cwd.iter() {
41                        tokens.push(token);
42                    }
43
44                    !tokens.is_empty() && tokens[0] == MAIN_SEPARATOR.as_os_str()
45                },
46                Component::ParentDir => {
47                    has_change = true;
48
49                    let cwd = cwd.as_ref();
50
51                    match cwd.parent() {
52                        Some(cwd_parent) => {
53                            for token in cwd_parent.iter() {
54                                tokens.push(token);
55                            }
56
57                            !tokens.is_empty() && tokens[0] == MAIN_SEPARATOR.as_os_str()
58                        },
59                        None => {
60                            // don't care about `cwd` is "//" or "///"
61                            if cwd == MAIN_SEPARATOR.as_os_str() {
62                                tokens.push(MAIN_SEPARATOR.as_os_str());
63
64                                true
65                            } else {
66                                false
67                            }
68                        },
69                    }
70                },
71                _ => {
72                    has_change = true;
73
74                    let cwd = cwd.as_ref();
75
76                    for token in cwd.iter() {
77                        tokens.push(token);
78                    }
79
80                    let first_is_root =
81                        !tokens.is_empty() && tokens[0] == MAIN_SEPARATOR.as_os_str();
82
83                    tokens.push(first_component.as_os_str());
84
85                    first_is_root
86                },
87            };
88
89            for component in iter {
90                match component {
91                    Component::CurDir => {
92                        // may be unreachable
93                        has_change = true;
94                    },
95                    Component::ParentDir => {
96                        let tokens_length = tokens.len();
97
98                        if tokens_length > 0 && (tokens_length != 1 || !first_is_root) {
99                            tokens.remove(tokens_length - 1);
100                        }
101
102                        has_change = true;
103                    },
104                    _ => {
105                        tokens.push(component.as_os_str());
106                    },
107                }
108            }
109
110            let tokens_length = tokens.len();
111
112            debug_assert!(tokens_length > 0);
113
114            let mut size = tokens.iter().fold(tokens_length - 1, |acc, &x| acc + x.len());
115
116            if first_is_root && tokens_length > 1 {
117                size -= 1;
118            }
119
120            if has_change || size != self.as_os_str().len() {
121                let mut path_string = OsString::with_capacity(size);
122
123                let mut iter = tokens.iter();
124
125                path_string.push(iter.next().unwrap());
126
127                if tokens_length > 1 {
128                    if !first_is_root {
129                        path_string.push(MAIN_SEPARATOR.as_os_str());
130                    }
131
132                    for token in iter.take(tokens_length - 2) {
133                        path_string.push(token);
134
135                        path_string.push(MAIN_SEPARATOR.as_os_str());
136                    }
137
138                    path_string.push(tokens[tokens_length - 1]);
139                }
140
141                let path_buf = PathBuf::from(path_string);
142
143                Ok(Cow::from(path_buf))
144            } else {
145                Ok(Cow::from(self))
146            }
147        } else {
148            Ok(Cow::from(cwd.as_ref().to_owned()))
149        }
150    }
151
152    fn absolutize_virtually(&self, virtual_root: impl AsRef<Path>) -> io::Result<Cow<Path>> {
153        let virtual_root = virtual_root.as_ref().absolutize()?;
154
155        let path = self.parse_dot()?;
156
157        if path.is_absolute() {
158            if !path.starts_with(&virtual_root) {
159                return Err(io::Error::from(ErrorKind::InvalidInput));
160            }
161
162            Ok(path)
163        } else {
164            let mut virtual_root = virtual_root.into_owned();
165
166            virtual_root.push(path);
167
168            Ok(Cow::from(virtual_root))
169        }
170    }
171}