path_absolutize/lib.rs
1/*!
2# Path Absolutize
3
4This is a library for extending `Path` and `PathBuf` in order to get an absolute path and remove the containing dots.
5
6The difference between `absolutize` and `canonicalize` methods is that `absolutize` does not care about whether the file exists and what the file really is.
7
8Please read the following examples to know the parsing rules.
9
10## Examples
11
12There are two methods you can use.
13
14### absolutize
15
16Get an absolute path.
17
18The dots in a path will be parsed even if it is already an absolute path (which means the path starts with a `MAIN_SEPARATOR` on Unix-like systems).
19
20```rust
21use std::path::Path;
22
23use path_absolutize::*;
24
25let p = Path::new("/path/to/123/456");
26
27# if cfg!(unix) {
28assert_eq!("/path/to/123/456", p.absolutize().unwrap().to_str().unwrap());
29# }
30```
31
32```rust
33use std::path::Path;
34
35use path_absolutize::*;
36
37let p = Path::new("/path/to/./123/../456");
38
39# if cfg!(unix) {
40assert_eq!("/path/to/456", p.absolutize().unwrap().to_str().unwrap());
41# }
42```
43
44If a path starts with a single dot, the dot means your program's **current working directory** (CWD).
45
46```rust
47use std::path::Path;
48use std::env;
49
50use path_absolutize::*;
51
52let p = Path::new("./path/to/123/456");
53
54# if cfg!(unix) {
55assert_eq!(Path::join(env::current_dir().unwrap().as_path(), Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
56# }
57```
58
59If a path starts with a pair of dots, the dots means the parent of the CWD. If the CWD is **root**, the parent is still **root**.
60
61```rust
62use std::path::Path;
63use std::env;
64
65use path_absolutize::*;
66
67let p = Path::new("../path/to/123/456");
68
69let cwd = env::current_dir().unwrap();
70
71let cwd_parent = cwd.parent();
72
73# if cfg!(unix) {
74match cwd_parent {
75 Some(cwd_parent) => {
76 assert_eq!(Path::join(&cwd_parent, Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
77 }
78 None => {
79 assert_eq!(Path::join(Path::new("/"), Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
80 }
81}
82# }
83```
84
85A path which does not start with a `MAIN_SEPARATOR`, **Single Dot** and **Double Dots**, will act like having a single dot at the start when `absolutize` method is used.
86
87```rust
88use std::path::Path;
89use std::env;
90
91use path_absolutize::*;
92
93let p = Path::new("path/to/123/456");
94
95# if cfg!(unix) {
96assert_eq!(Path::join(env::current_dir().unwrap().as_path(), Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
97# }
98```
99
100```rust
101use std::path::Path;
102use std::env;
103
104use path_absolutize::*;
105
106let p = Path::new("path/../../to/123/456");
107
108let cwd = env::current_dir().unwrap();
109
110let cwd_parent = cwd.parent();
111
112# if cfg!(unix) {
113match cwd_parent {
114 Some(cwd_parent) => {
115 assert_eq!(Path::join(&cwd_parent, Path::new("to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
116 }
117 None => {
118 assert_eq!(Path::join(Path::new("/"), Path::new("to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
119 }
120}
121# }
122```
123
124### Starting from a given current working directory
125
126With the `absolutize_from` function, you can provide the current working directory that the relative paths should be resolved from.
127
128```rust
129use std::env;
130use std::path::Path;
131
132use path_absolutize::*;
133
134let p = Path::new("../path/to/123/456");
135let cwd = env::current_dir().unwrap();
136
137println!("{}", p.absolutize_from(cwd).to_str().unwrap());
138```
139
140### absolutize_virtually
141
142Get an absolute path **only under a specific directory**.
143
144The dots in a path will be parsed even if it is already an absolute path (which means the path starts with a `MAIN_SEPARATOR` on Unix-like systems).
145
146```rust
147use std::path::Path;
148
149use path_absolutize::*;
150
151let p = Path::new("/path/to/123/456");
152
153# if cfg!(unix) {
154assert_eq!("/path/to/123/456", p.absolutize_virtually("/").unwrap().to_str().unwrap());
155# }
156```
157
158```rust
159use std::path::Path;
160
161use path_absolutize::*;
162
163let p = Path::new("/path/to/./123/../456");
164
165# if cfg!(unix) {
166assert_eq!("/path/to/456", p.absolutize_virtually("/").unwrap().to_str().unwrap());
167# }
168```
169
170Every absolute path should under the virtual root.
171
172```rust
173use std::path::Path;
174
175use std::io::ErrorKind;
176
177use path_absolutize::*;
178
179let p = Path::new("/path/to/123/456");
180
181# if cfg!(unix) {
182assert_eq!(ErrorKind::InvalidInput, p.absolutize_virtually("/virtual/root").unwrap_err().kind());
183# }
184```
185
186Every relative path should under the virtual root.
187
188```rust
189use std::path::Path;
190
191use std::io::ErrorKind;
192
193use path_absolutize::*;
194
195let p = Path::new("./path/to/123/456");
196
197# if cfg!(unix) {
198assert_eq!(ErrorKind::InvalidInput, p.absolutize_virtually("/virtual/root").unwrap_err().kind());
199# }
200```
201
202```rust
203use std::path::Path;
204
205use std::io::ErrorKind;
206
207use path_absolutize::*;
208
209let p = Path::new("../path/to/123/456");
210
211# if cfg!(unix) {
212assert_eq!(ErrorKind::InvalidInput, p.absolutize_virtually("/virtual/root").unwrap_err().kind());
213# }
214```
215
216A path which does not start with a `MAIN_SEPARATOR`, **Single Dot** and **Double Dots**, will be located in the virtual root after the `absolutize_virtually` method is used.
217
218```rust
219use std::path::Path;
220
221use path_absolutize::*;
222
223let p = Path::new("path/to/123/456");
224
225# if cfg!(unix) {
226assert_eq!("/virtual/root/path/to/123/456", p.absolutize_virtually("/virtual/root").unwrap().to_str().unwrap());
227# }
228```
229
230```rust
231use std::path::Path;
232
233use path_absolutize::*;
234
235let p = Path::new("path/to/../../../../123/456");
236
237# if cfg!(unix) {
238assert_eq!("/virtual/root/123/456", p.absolutize_virtually("/virtual/root").unwrap().to_str().unwrap());
239# }
240```
241
242## Caching
243
244By default, the `absolutize` method and the `absolutize_virtually` method create a new `PathBuf` instance of the CWD every time they need it in their operation. The overhead is obvious. Although it allows us to safely change the CWD at runtime by the program itself (e.g. using the `std::env::set_current_dir` function) or outside controls (e.g. using gdb to call `chdir`), we don't need that in most cases.
245
246In order to parse paths with better performance, the `fixed_workdir` feature can be enabled.
247
248```toml
249[dependencies.path-absolutize]
250version = "*"
251features = ["fixed_workdir"]
252```
253
254## Benchmark
255
256#### No-cache
257
258```bash
259cargo bench
260```
261
262#### fixed_workdir
263
264```bash
265cargo bench --features fixed_workdir
266```
267
268*/
269
270#![cfg_attr(docsrs, feature(doc_cfg))]
271
272pub extern crate path_dedot;
273
274use std::{
275 borrow::Cow,
276 io,
277 path::{Path, PathBuf},
278};
279
280#[cfg(feature = "fixed_workdir")]
281pub use path_dedot::CWD;
282
283mod absolutize;
284
285#[macro_use]
286mod macros;
287
288#[cfg(any(unix, target_family = "wasm"))]
289mod unix;
290
291#[cfg(windows)]
292mod windows;
293
294pub use absolutize::*;
295
296impl Absolutize for PathBuf {
297 #[inline]
298 fn absolutize(&self) -> io::Result<Cow<'_, Path>> {
299 self.as_path().absolutize()
300 }
301
302 #[inline]
303 fn absolutize_from(&self, cwd: impl AsRef<Path>) -> Cow<'_, Path> {
304 self.as_path().absolutize_from(cwd)
305 }
306
307 #[inline]
308 fn absolutize_virtually(&self, virtual_root: impl AsRef<Path>) -> io::Result<Cow<'_, Path>> {
309 self.as_path().absolutize_virtually(virtual_root)
310 }
311}