Skip to main content

normal_path/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2//! A library to ensure paths are normalized without touching the filesystem.
3//!
4//! This library provides two types, [`NormpathBuf`] and [`Normpath`] (akin to
5//! [`PathBuf`] and [`Path`]), for working with normalized paths abstractly.
6//! Similar to the standard library, these types are thin wrappers around
7//! [`OsString`] and [`OsStr`] that represent normalized paths according to the
8//! local platform's path syntax.
9//!
10//! In general, a path is considered normalized if and only if:
11//!
12//! 1. It is [absolute][1], i.e. independent of the current directory.
13//!
14//! 2. It is canonical, meaning it does not contain any pattern that is
15//!    considered non-canonical and has a corresponding canonical form
16//!    depending on the platform.
17//!
18//! 3. It does not contain any parent directory component (`..`).
19//!
20//! A normalized path is not necessarily the ["real"][2] path to the filesystem
21//! object it denotes, which may not even exist. Instead, it defines a path
22//! unambiguously on the string level, unaffected by the state of the
23//! filesystem.
24//!
25//! All [`Normpath`] slices and [`NormpathBuf`] instances are guaranteed to
26//! uphold these invariants, and can be obtained by validating an existing path,
27//! or even further, by normalizing non-canonical patterns found in a path into
28//! their canonical forms.
29//!
30//! This library never touches the filesystem, and will never attempt to
31//! alter a path in a way that might change the object it denotes, such as
32//! eliminating parent directory components on Unix. Since filesystem access is
33//! inevitable, [`std::fs`] or third-party crates should be used in order to
34//! resolve such paths.
35//!
36//! # Canonicality
37//!
38//! These patterns are considered non-canonical on both Unix and Windows:
39//! 1. Multiple consecutive slashes (`foo//bar`).
40//! 2. Trailing slashes (`foo/bar/`).
41//! 3. Current directory components (`foo/.` or `./foo`).
42//!
43//! Specifically on Windows, the following patterns are also considered
44//! non-canonical:
45//! 1. Forward slashes (`D:\foo/bar` or `//./COM11`).
46//! 2. Lowercase drive letters (`d:\`).
47//! 3. Parent directory components (that can be normalized). See more on that
48//!    below.
49//!
50//! All of these patterns can be normalized into their canonical forms if
51//! desired.
52//!
53//! # Handling Parent Directories
54//!
55//! On Unix, any parent component is an error, since it is impossible to
56//! eliminate them without filesystem access.
57//!
58//! Windows, on the other hand, collapses parent components lexically *before*
59//! walking the filesystem (unless the path starts with `\\?\`). Therefore,
60//! it is only a *hard error* if a parent component points outside of the base
61//! directory, like `C:\..`.
62//!
63//! This leads to a subtlety on Windows about which error a parent directory
64//! component should raise during validation:
65//!
66//! 1. If the parent component can be normalized away, e.g. `C:\foo\..`, then
67//!    it is only an issue on the canonicality of the path.
68//!
69//! 2. But if the parent component points outside of the base directory, e.g.
70//!    `C:\..`, then it is instead a parent component error similar to Unix.
71//!
72//! It worth noting that no parent directory component is *ever* allowed for a
73//! path to be considered normalized. It is *always* an error; the distinction
74//! is merely about which category the error falls into.
75//!
76//! # Notes on Windows
77//!
78//! This library always assumes case-sensitivity as Windows can be
79//! [case-sensitive][3] with respect to filesystem paths.
80//!
81//! Verbatim paths (starting with `\\?\`) are *always* considered normalized.
82//! Consider using third-party crates like [`dunce`][4] to remove the verbatim
83//! prefix if that is not desired.
84//!
85//! # Feature Flags
86//! - `serde`: adds support for serialization and deserialization.
87//!
88//! [1]: std::path::Path::is_absolute
89//! [2]: std::fs::canonicalize
90//! [3]: https://learn.microsoft.com/en-us/windows/wsl/case-sensitivity
91//! [4]: https://crates.io/crates/dunce
92//!
93//! [`OsStr`]: std::ffi::OsStr
94//! [`OsString`]: std::ffi::OsString
95//! [`Path`]: std::path::Path
96//! [`PathBuf`]: std::path::PathBuf
97
98mod draw;
99mod trivial;
100pub use trivial::{ConvertError, Error, Normpath, NormpathBuf};
101
102mod imp;
103
104mod public;
105pub use public::*;