Path Absolutize
====================
[![CI](https://github.com/magiclen/path-absolutize/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/path-absolutize/actions/workflows/ci.yml)
This is a library for extending `Path` and `PathBuf` in order to get an absolute path and remove the containing dots.
The difference between `absolutize` and `canonicalize` methods is that `absolutize` does not care about whether the file exists and what the file really is.
Please read the following examples to know the parsing rules.
## Examples
There are two methods you can use.
### absolutize
Get an absolute path.
The 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).
```rust
use std::path::Path;
use path_absolutize::*;
let p = Path::new("/path/to/123/456");
assert_eq!("/path/to/123/456", p.absolutize().unwrap().to_str().unwrap());
```
```rust
use std::path::Path;
use path_absolutize::*;
let p = Path::new("/path/to/./123/../456");
assert_eq!("/path/to/456", p.absolutize().unwrap().to_str().unwrap());
```
If a path starts with a single dot, the dot means your program's **current working directory** (CWD).
```rust
use std::path::Path;
use std::env;
use path_absolutize::*;
let p = Path::new("./path/to/123/456");
assert_eq!(Path::join(env::current_dir().unwrap().as_path(), Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
```
If 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**.
```rust
use std::path::Path;
use std::env;
use path_absolutize::*;
let p = Path::new("../path/to/123/456");
let cwd = env::current_dir().unwrap();
let cwd_parent = cwd.parent();
match cwd_parent {
Some(cwd_parent) => {
assert_eq!(Path::join(&cwd_parent, Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
}
None => {
assert_eq!(Path::join(Path::new("/"), Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
}
}
```
A 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.
```rust
use std::path::Path;
use std::env;
use path_absolutize::*;
let p = Path::new("path/to/123/456");
assert_eq!(Path::join(env::current_dir().unwrap().as_path(), Path::new("path/to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
```
```rust
use std::path::Path;
use std::env;
use path_absolutize::*;
let p = Path::new("path/../../to/123/456");
let cwd = env::current_dir().unwrap();
let cwd_parent = cwd.parent();
match cwd_parent {
Some(cwd_parent) => {
assert_eq!(Path::join(&cwd_parent, Path::new("to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
}
None => {
assert_eq!(Path::join(Path::new("/"), Path::new("to/123/456")).to_str().unwrap(), p.absolutize().unwrap().to_str().unwrap());
}
}
```
### Starting from a given current working directory
With the `absolutize_from` function, you can provide the current working directory that the relative paths should be resolved from.
```rust
use std::env;
use std::path::Path;
use path_absolutize::*;
let p = Path::new("../path/to/123/456");
let cwd = env::current_dir().unwrap();
println!("{}", p.absolutize_from(cwd).unwrap().to_str().unwrap());
```
### absolutize_virtually
Get an absolute path **only under a specific directory**.
The 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).
```rust
use std::path::Path;
use path_absolutize::*;
let p = Path::new("/path/to/123/456");
assert_eq!("/path/to/123/456", p.absolutize_virtually("/").unwrap().to_str().unwrap());
```
```rust
use std::path::Path;
use path_absolutize::*;
let p = Path::new("/path/to/./123/../456");
assert_eq!("/path/to/456", p.absolutize_virtually("/").unwrap().to_str().unwrap());
```
Every absolute path should under the virtual root.
```rust
use std::path::Path;
use std::io::ErrorKind;
use path_absolutize::*;
let p = Path::new("/path/to/123/456");
assert_eq!(ErrorKind::InvalidInput, p.absolutize_virtually("/virtual/root").unwrap_err().kind());
```
Every relative path should under the virtual root.
```rust
use std::path::Path;
use std::io::ErrorKind;
use path_absolutize::*;
let p = Path::new("./path/to/123/456");
assert_eq!(ErrorKind::InvalidInput, p.absolutize_virtually("/virtual/root").unwrap_err().kind());
```
```rust
use std::path::Path;
use std::io::ErrorKind;
use path_absolutize::*;
let p = Path::new("../path/to/123/456");
assert_eq!(ErrorKind::InvalidInput, p.absolutize_virtually("/virtual/root").unwrap_err().kind());
```
A 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.
```rust
use std::path::Path;
use path_absolutize::*;
let p = Path::new("path/to/123/456");
assert_eq!("/virtual/root/path/to/123/456", p.absolutize_virtually("/virtual/root").unwrap().to_str().unwrap());
```
```rust
use std::path::Path;
use path_absolutize::*;
let p = Path::new("path/to/../../../../123/456");
assert_eq!("/virtual/root/123/456", p.absolutize_virtually("/virtual/root").unwrap().to_str().unwrap());
```
## Caching
By default, the `absolutize` method and the `absolutize_virtually` method create a new `PathBuf` instance of the CWD every time in their operation. 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.
In order to parse paths with better performance, this crate provides three ways to cache the CWD.
### once_cell_cache
Enabling the `once_cell_cache` feature can let this crate use `once_cell` to cache the CWD. It's thread-safe and does not need to modify any code, but once the CWD is cached, it cannot be changed anymore at runtime.
```toml
[dependencies.path-absolutize]
version = "*"
features = ["once_cell_cache"]
```
### lazy_static_cache
Enabling the `lazy_static_cache` feature can let this crate use `lazy_static` to cache the CWD. It's thread-safe and does not need to modify any code, but once the CWD is cached, it cannot be changed anymore at runtime.
```toml
[dependencies.path-absolutize]
version = "*"
features = ["lazy_static_cache"]
```
### unsafe_cache
Enabling the `unsafe_cache` feature can let this crate use a mutable static variable to cache the CWD. It allows the program to change the CWD at runtime by the program itself, but it's not thread-safe.
You need to use the `update_cwd` function to initialize the CWD first. The function should also be used to update the CWD after the CWD is changed.
```toml
[dependencies.path-absolutize]
version = "*"
features = ["unsafe_cache"]
```
```rust
use std::path::Path;
use path_absolutize::*;
unsafe {
update_cwd();
}
let p = Path::new("./path/to/123/456");
println!("{}", p.absolutize().unwrap().to_str().unwrap());
std::env::set_current_dir("/").unwrap();
unsafe {
update_cwd();
}
println!("{}", p.absolutize().unwrap().to_str().unwrap());
```
## Benchmark
#### No-cache
```bash
cargo bench
```
#### once_cell_cache
```bash
cargo bench --features once_cell_cache
```
#### lazy_static_cache
```bash
cargo bench --features lazy_static_cache
```
#### unsafe_cache
```bash
cargo bench --features unsafe_cache
```
## Crates.io
https://crates.io/crates/path-absolutize
## Documentation
https://docs.rs/path-absolutize
## License
[MIT](LICENSE)