lxutil/path.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#[cfg(unix)]
use super::unix as sys;
#[cfg(windows)]
use super::windows as sys;
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
/// Extensions to `PathBuf` to convert Unix-style paths to a native path.
///
/// # Windows
///
/// Paths are converted to native by switching the separators from / to \\, and by escaping
/// characters that are not legal in NTFS file names (such as \\, :, and others) by mapping
/// them into a private unicode range (0xf000).
///
/// This is primarily useful for relative paths which will be passed to the methods of `LxVolume`.
/// While you can translate absolute paths, the result will have no drive letter.
///
/// # Unix
///
/// Paths are already native so no conversion is performed.
pub trait PathBufExt {
/// Creates a `PathBuf` by converting a Unix-style path to its native representation.
fn from_lx(path: impl AsRef<lx::LxStr>) -> lx::Result<PathBuf>;
/// Extends `self` with `path`, first converting it from a Unix-style path to its native
/// representation.
fn push_lx(&mut self, path: impl AsRef<lx::LxStr>) -> lx::Result<()>;
}
impl PathBufExt for PathBuf {
fn from_lx(path: impl AsRef<lx::LxStr>) -> lx::Result<PathBuf> {
Ok(Self::from(Path::from_lx(&path)?))
}
fn push_lx(&mut self, path: impl AsRef<lx::LxStr>) -> lx::Result<()> {
self.push(Path::from_lx(&path)?);
Ok(())
}
}
/// Extensions to `Path` to convert Unix-style paths to a native path, using the same rules as
/// `PathBuf`.
pub trait PathExt {
/// Creates a `Path` by converting a Unix-style path to its native representation, avoiding
/// allocation if unnecessary.
///
/// # Windows
///
/// This function does not allocate (returns a `Cow::Borrowed`) if the path contains no
/// separators and no characters that need to be escaped. Otherwise, it allocates a `PathBuf`
/// and returns a `Cow::Owned`.
///
/// # Unix
///
/// This function never allocates and always returns a `Cow::Borrowed`.
fn from_lx(path: &(impl AsRef<lx::LxStr> + ?Sized)) -> lx::Result<Cow<'_, Self>>
where
Self: ToOwned;
}
impl PathExt for Path {
fn from_lx(path: &(impl AsRef<lx::LxStr> + ?Sized)) -> lx::Result<Cow<'_, Self>> {
sys::path::path_from_lx(path.as_ref().as_bytes())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::borrow::Cow;
#[test]
fn from_lx() {
let path = Path::from_lx("test").unwrap();
println!("{:?}", path);
assert!(matches!(path, Cow::Borrowed(_)));
assert_eq!(path.to_str(), Some("test"));
let path = Path::from_lx("foo:bar").unwrap();
println!("{:?}", path);
if cfg!(windows) {
assert!(matches!(path, Cow::Owned(_)));
assert_eq!(path.to_str(), Some("foo\u{f03a}bar"));
} else {
assert!(matches!(path, Cow::Borrowed(_)));
assert_eq!(path.to_str(), Some("foo:bar"));
}
let path = Path::from_lx("dir/file").unwrap();
println!("{:?}", path);
if cfg!(windows) {
assert!(matches!(path, Cow::Owned(_)));
assert_eq!(path.to_str(), Some("dir\\file"));
} else {
assert!(matches!(path, Cow::Borrowed(_)));
assert_eq!(path.to_str(), Some("dir/file"));
}
let path = PathBuf::from_lx("dir/foo:bar").unwrap();
if cfg!(windows) {
assert_eq!(path.to_str(), Some("dir\\foo\u{f03a}bar"));
} else {
assert_eq!(path.to_str(), Some("dir/foo:bar"));
}
}
#[test]
fn push() {
let mut path = PathBuf::new();
path.push_lx("dir/subdir").unwrap();
if cfg!(windows) {
assert_eq!(path.to_str(), Some("dir\\subdir"));
} else {
assert_eq!(path.to_str(), Some("dir/subdir"));
}
path.push_lx("foo:bar").unwrap();
if cfg!(windows) {
assert_eq!(path.to_str(), Some("dir\\subdir\\foo\u{f03a}bar"));
} else {
assert_eq!(path.to_str(), Some("dir/subdir/foo:bar"));
}
}
}