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 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 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}