path_dedot/
unix.rs

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