Maybe 2024 changes? Note that we now set `edition = "2024"` explicitly in `rustfmt.toml`. Without this, it seems like it's possible in some cases for rustfmt to run under an older edition's style. Not sure how though.
134 lines
4.0 KiB
Rust
134 lines
4.0 KiB
Rust
use std::borrow::Cow;
|
|
|
|
use bstr::{ByteSlice, ByteVec};
|
|
|
|
/// The final component of the path, if it is a normal file.
|
|
///
|
|
/// If the path terminates in `..`, or consists solely of a root of prefix,
|
|
/// file_name will return `None`.
|
|
pub(crate) fn file_name<'a>(path: &Cow<'a, [u8]>) -> Option<Cow<'a, [u8]>> {
|
|
if path.is_empty() {
|
|
return None;
|
|
}
|
|
let last_slash = path.rfind_byte(b'/').map(|i| i + 1).unwrap_or(0);
|
|
let got = match *path {
|
|
Cow::Borrowed(path) => Cow::Borrowed(&path[last_slash..]),
|
|
Cow::Owned(ref path) => {
|
|
let mut path = path.clone();
|
|
path.drain_bytes(..last_slash);
|
|
Cow::Owned(path)
|
|
}
|
|
};
|
|
if got == &b".."[..] {
|
|
return None;
|
|
}
|
|
Some(got)
|
|
}
|
|
|
|
/// Return a file extension given a path's file name.
|
|
///
|
|
/// Note that this does NOT match the semantics of std::path::Path::extension.
|
|
/// Namely, the extension includes the `.` and matching is otherwise more
|
|
/// liberal. Specifically, the extension is:
|
|
///
|
|
/// * None, if the file name given is empty;
|
|
/// * None, if there is no embedded `.`;
|
|
/// * Otherwise, the portion of the file name starting with the final `.`.
|
|
///
|
|
/// e.g., A file name of `.rs` has an extension `.rs`.
|
|
///
|
|
/// N.B. This is done to make certain glob match optimizations easier. Namely,
|
|
/// a pattern like `*.rs` is obviously trying to match files with a `rs`
|
|
/// extension, but it also matches files like `.rs`, which doesn't have an
|
|
/// extension according to std::path::Path::extension.
|
|
pub(crate) fn file_name_ext<'a>(
|
|
name: &Cow<'a, [u8]>,
|
|
) -> Option<Cow<'a, [u8]>> {
|
|
if name.is_empty() {
|
|
return None;
|
|
}
|
|
let last_dot_at = match name.rfind_byte(b'.') {
|
|
None => return None,
|
|
Some(i) => i,
|
|
};
|
|
Some(match *name {
|
|
Cow::Borrowed(name) => Cow::Borrowed(&name[last_dot_at..]),
|
|
Cow::Owned(ref name) => {
|
|
let mut name = name.clone();
|
|
name.drain_bytes(..last_dot_at);
|
|
Cow::Owned(name)
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Normalizes a path to use `/` as a separator everywhere, even on platforms
|
|
/// that recognize other characters as separators.
|
|
#[cfg(unix)]
|
|
pub(crate) fn normalize_path(path: Cow<'_, [u8]>) -> Cow<'_, [u8]> {
|
|
// UNIX only uses /, so we're good.
|
|
path
|
|
}
|
|
|
|
/// Normalizes a path to use `/` as a separator everywhere, even on platforms
|
|
/// that recognize other characters as separators.
|
|
#[cfg(not(unix))]
|
|
pub(crate) fn normalize_path(mut path: Cow<[u8]>) -> Cow<[u8]> {
|
|
use std::path::is_separator;
|
|
|
|
for i in 0..path.len() {
|
|
if path[i] == b'/' || !is_separator(char::from(path[i])) {
|
|
continue;
|
|
}
|
|
path.to_mut()[i] = b'/';
|
|
}
|
|
path
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::borrow::Cow;
|
|
|
|
use bstr::{B, ByteVec};
|
|
|
|
use super::{file_name_ext, normalize_path};
|
|
|
|
macro_rules! ext {
|
|
($name:ident, $file_name:expr, $ext:expr) => {
|
|
#[test]
|
|
fn $name() {
|
|
let bs = Vec::from($file_name);
|
|
let got = file_name_ext(&Cow::Owned(bs));
|
|
assert_eq!($ext.map(|s| Cow::Borrowed(B(s))), got);
|
|
}
|
|
};
|
|
}
|
|
|
|
ext!(ext1, "foo.rs", Some(".rs"));
|
|
ext!(ext2, ".rs", Some(".rs"));
|
|
ext!(ext3, "..rs", Some(".rs"));
|
|
ext!(ext4, "", None::<&str>);
|
|
ext!(ext5, "foo", None::<&str>);
|
|
|
|
macro_rules! normalize {
|
|
($name:ident, $path:expr, $expected:expr) => {
|
|
#[test]
|
|
fn $name() {
|
|
let bs = Vec::from_slice($path);
|
|
let got = normalize_path(Cow::Owned(bs));
|
|
assert_eq!($expected.to_vec(), got.into_owned());
|
|
}
|
|
};
|
|
}
|
|
|
|
normalize!(normal1, b"foo", b"foo");
|
|
normalize!(normal2, b"foo/bar", b"foo/bar");
|
|
#[cfg(unix)]
|
|
normalize!(normal3, b"foo\\bar", b"foo\\bar");
|
|
#[cfg(not(unix))]
|
|
normalize!(normal3, b"foo\\bar", b"foo/bar");
|
|
#[cfg(unix)]
|
|
normalize!(normal4, b"foo\\bar/baz", b"foo\\bar/baz");
|
|
#[cfg(not(unix))]
|
|
normalize!(normal4, b"foo\\bar/baz", b"foo/bar/baz");
|
|
}
|