gix_path/lib.rs
1//! This crate contains an assortment of utilities to deal with paths and their conversions.
2//!
3//! Generally `git` treats paths as bytes, but inherently assumes non-illformed UTF-8 as encoding on windows. Internally, it expects
4//! slashes to be used as path separators and paths in files must have slashes, with conversions being performed on windows accordingly.
5//!
6//! ## Examples
7//!
8//! ```
9//! use bstr::ByteSlice;
10//!
11//! use std::path::Path;
12//!
13//! let normalized = gix_path::normalize(Path::new("a/./b/..").into(), Path::new("/cwd")).unwrap();
14//! assert_eq!(normalized.as_ref(), Path::new("a"));
15//!
16//! let unix = gix_path::to_unix_separators(b"dir\\subdir\\file".as_bstr());
17//! assert_eq!(unix.as_ref(), b"dir/subdir/file".as_bstr());
18//! ```
19//!
20//! <details>
21//!
22//! ### Research
23//!
24//! * **windows**
25//! - [`dirent.c`](https://github.com/git/git/blob/main/compat/win32/dirent.c#L31:L31) contains all implementation (seemingly) of opening directories and reading their entries, along with all path conversions (UTF-16 for windows). This is done on the fly so git can work with [in UTF-8](https://github.com/git/git/blob/main/compat/win32/dirent.c#L12:L12).
26//! - mingw [is used for the conversion](https://github.com/git/git/blob/main/compat/mingw.h#L579:L579) and it appears they handle surrogates during the conversion, maybe some sort of non-strict UTF-8 converter? Actually it uses [WideCharToMultiByte](https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte)
27//! under the hood which by now does fail if the UTF-8 would be invalid unicode, i.e. unicode pairs.
28//! - `OsString` on windows already stores strings as WTF-8, which supports [surrogate pairs](https://unicodebook.readthedocs.io/unicode_encodings.html),
29//! something that UTF-8 isn't allowed do it for security reasons, after all it's UTF-16 specific and exists only to extend
30//! the encodable code-points.
31//! - informative reading on [WTF-8](https://simonsapin.github.io/wtf-8/#motivation) which is the encoding used by Rust
32//! internally that deals with surrogates and non-wellformed surrogates (those that aren't in pairs).
33//! * **unix**
34//! - It uses [opendir](https://man7.org/linux/man-pages/man3/opendir.3.html) and [readdir](https://man7.org/linux/man-pages/man3/readdir.3.html)
35//! respectively. There is no encoding specified, except that these paths are null-terminated.
36//!
37//! ### Learnings
38//!
39//! Surrogate pairs are a way to extend the encodable value range in UTF-16 encodings, used primarily on windows and in Javascript.
40//! For a long time these codepoints used for surrogates, always to be used in pairs, were not assigned, until…they were for rare
41//! emojies and the likes. The unicode standard does not require surrogates to happen in pairs, even though by now unpaired surrogates
42//! in UTF-16 are considered ill-formed, which aren't supposed to be converted to UTF-8 for example.
43//!
44//! This is the reason we have to deal with `to_string_lossy()`, it's _just_ for that quirk.
45//!
46//! This also means the only platform ever eligible to see conversion errors is windows, and there it's only older pre-vista
47//! windows versions which incorrectly allow ill-formed UTF-16 strings. Newer versions don't perform such conversions anymore, for
48//! example when going from UTF-16 to UTF-8, they will trigger an error.
49//!
50//! ### Conclusions
51//!
52//! Since [WideCharToMultiByte](https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte) by now is
53//! fixed (Vista onward) to produce valid UTF-8, lone surrogate codepoints will cause failure, which `git`
54//! [doesn't care about](https://github.com/git/git/blob/main/compat/win32/dirent.c#L12:L12).
55//!
56//! We will, though, which means from now on we can just convert to UTF-8 on windows and bubble up errors where necessary,
57//! preventing potential mismatched surrogate pairs to ever be saved on disk by gitoxide.
58//!
59//! Even though the error only exists on older windows versions, we will represent it in the type system through fallible function calls.
60//! Callers may `.expect()` on the result to indicate they don't wish to handle this special and rare case. Note that servers should not
61//! ever get into a code-path which does panic though.
62//! </details>
63#![deny(missing_docs, rust_2018_idioms)]
64#![cfg_attr(not(test), deny(unsafe_code))]
65
66/// A dummy type to represent path specs and help finding all spots that take path specs once it is implemented.
67mod convert;
68pub use convert::*;
69
70mod util;
71pub use util::is_absolute;
72
73///
74pub mod realpath;
75pub use realpath::function::{realpath, realpath_opts};
76
77/// Information about the environment in terms of locations of resources.
78pub mod env;
79
80///
81pub mod relative_path;
82pub use relative_path::types::RelativePath;