From 3e2206a29a2f8b93932a56768516845ec4379bb4 Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Tue, 11 May 2021 10:29:57 +1000 Subject: [PATCH 1/7] add file_prefix method --- library/std/src/path.rs | 53 ++++++++++- library/std/src/path/tests.rs | 174 +++++++++++++++++++++++++++------- 2 files changed, 191 insertions(+), 36 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index ed0987064e8f7..cec8010fe24be 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -315,7 +315,7 @@ fn has_physical_root(s: &[u8], prefix: Option>) -> bool { } // basic workhorse for splitting stem and extension -fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { +fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { if os_str_as_u8_slice(file) == b".." { return (Some(file), None); } @@ -334,6 +334,29 @@ fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { } } +fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { + let slice = os_str_as_u8_slice(file); + if slice == b".." { + return (Some(file), None); + } + + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let i = match slice[1..].iter().position(|b| *b == b'.') { + Some(i) => i + 1, + None => slice.len(), + }; + if i == slice.len() { + (Some(file), None) + } else { + let before = Some(&slice[..i]); + let after = Some(&slice[i + 1..]); + unsafe { (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s))) } + } +} + //////////////////////////////////////////////////////////////////////////////// // The core iterators //////////////////////////////////////////////////////////////////////////////// @@ -2158,6 +2181,32 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn file_stem(&self) -> Option<&OsStr> { + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.or(after)) + } + + /// Extracts the prefix (non-extension(s)) portion of [`self.file_name`]. This is a "left" + /// variant of `file_stem` - meaning it takes the portion of the file name before the *first* `.` + /// + /// [`self.file_name`]: Path::file_name + /// + /// The prefix is: + /// + /// * [`None`], if there is no file name; + /// * The entire file name if there is no embedded `.`; + /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * Otherwise, the portion of the file name before the first `.` + /// + /// # Examples + /// + /// ``` + /// # #![feature(path_file_prefix)] + /// use std::path::Path; + /// + /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); + /// assert_eq!("foo", Path::new("foo.tar.gz").file_prefix().unwrap()); + /// ``` + #[unstable(feature = "path_file_prefix", issue = "none")] + pub fn file_prefix(&self) -> Option<&OsStr> { self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after)) } @@ -2182,7 +2231,7 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn extension(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after)) + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after)) } /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 896d6c2a64c60..14dcab450acbe 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -3,7 +3,7 @@ use super::*; use crate::rc::Rc; use crate::sync::Arc; -macro_rules! t( +macro_rules! t ( ($path:expr, iter: $iter:expr) => ( { let path = Path::new($path); @@ -73,15 +73,33 @@ macro_rules! t( } ); + ($path:expr, file_prefix: $file_prefix:expr, extension: $extension:expr) => ( + { + let path = Path::new($path); + + let prefix = path.file_prefix().map(|p| p.to_str().unwrap()); + let exp_prefix: Option<&str> = $file_prefix; + assert!(prefix == exp_prefix, "file_prefix: Expected {:?}, found {:?}", + exp_prefix, prefix); + + let ext = path.extension().map(|p| p.to_str().unwrap()); + let exp_ext: Option<&str> = $extension; + assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", + exp_ext, ext); + } + ); + ($path:expr, iter: $iter:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr, parent: $parent:expr, file_name: $file:expr, - file_stem: $file_stem:expr, extension: $extension:expr) => ( + file_stem: $file_stem:expr, extension: $extension:expr, + file_prefix: $file_prefix:expr) => ( { t!($path, iter: $iter); t!($path, has_root: $has_root, is_absolute: $is_absolute); t!($path, parent: $parent, file_name: $file); t!($path, file_stem: $file_stem, extension: $extension); + t!($path, file_prefix: $file_prefix, extension: $extension); } ); ); @@ -116,7 +134,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -126,7 +145,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/", @@ -136,7 +156,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -146,7 +167,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/", @@ -156,7 +178,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/foo/", @@ -166,7 +189,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/bar", @@ -176,7 +200,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("/foo/bar", @@ -186,7 +211,8 @@ pub fn test_decompositions_unix() { parent: Some("/foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("///foo///", @@ -196,7 +222,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("///foo///bar", @@ -206,7 +233,8 @@ pub fn test_decompositions_unix() { parent: Some("///foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./.", @@ -216,7 +244,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -226,7 +255,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -236,7 +266,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -246,7 +277,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/..", @@ -256,7 +288,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -266,7 +299,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/./bar", @@ -276,7 +310,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("foo/../", @@ -286,7 +321,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/../bar", @@ -296,7 +332,8 @@ pub fn test_decompositions_unix() { parent: Some("foo/.."), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./a", @@ -306,7 +343,8 @@ pub fn test_decompositions_unix() { parent: Some("."), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!(".", @@ -316,7 +354,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -326,7 +365,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -336,7 +376,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a//b", @@ -346,7 +387,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/./b", @@ -356,7 +398,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/b/c", @@ -366,7 +409,8 @@ pub fn test_decompositions_unix() { parent: Some("a/b"), file_name: Some("c"), file_stem: Some("c"), - extension: None + extension: None, + file_prefix: Some("c") ); t!(".foo", @@ -376,7 +420,30 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some(".foo"), file_stem: Some(".foo"), - extension: None + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.foo", + iter: ["a", ".foo"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.rustfmt.toml", + iter: ["a", ".rustfmt.toml"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".rustfmt.toml"), + file_stem: Some(".rustfmt"), + extension: Some("toml"), + file_prefix: Some(".rustfmt") ); } @@ -952,9 +1019,48 @@ pub fn test_stem_ext() { t!("", file_stem: None, extension: None); } +#[test] +pub fn test_prefix_ext() { + t!("foo", + file_prefix: Some("foo"), + extension: None + ); + + t!("foo.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".foo", + file_prefix: Some(".foo"), + extension: None + ); + + t!("foo.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".", file_prefix: None, extension: None); + + t!("..", file_prefix: None, extension: None); + + t!("", file_prefix: None, extension: None); +} + #[test] pub fn test_push() { - macro_rules! tp( + macro_rules! tp ( ($path:expr, $push:expr, $expected:expr) => ( { let mut actual = PathBuf::from($path); actual.push($push); @@ -1042,7 +1148,7 @@ pub fn test_push() { #[test] pub fn test_pop() { - macro_rules! tp( + macro_rules! tp ( ($path:expr, $expected:expr, $output:expr) => ( { let mut actual = PathBuf::from($path); let output = actual.pop(); @@ -1096,7 +1202,7 @@ pub fn test_pop() { #[test] pub fn test_set_file_name() { - macro_rules! tfn( + macro_rules! tfn ( ($path:expr, $file:expr, $expected:expr) => ( { let mut p = PathBuf::from($path); p.set_file_name($file); @@ -1130,7 +1236,7 @@ pub fn test_set_file_name() { #[test] pub fn test_set_extension() { - macro_rules! tfe( + macro_rules! tfe ( ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { let mut p = PathBuf::from($path); let output = p.set_extension($ext); @@ -1192,7 +1298,7 @@ pub fn test_compare() { s.finish() } - macro_rules! tc( + macro_rules! tc ( ($path1:expr, $path2:expr, eq: $eq:expr, starts_with: $starts_with:expr, ends_with: $ends_with:expr, relative_from: $relative_from:expr) => ({ From b1d84f721e3e423e4159e7f63729bd492d2cb77a Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Tue, 11 May 2021 11:41:37 +1000 Subject: [PATCH 2/7] add missing windows tests --- library/std/src/path/tests.rs | 193 ++++++++++++++++++++++++---------- 1 file changed, 140 insertions(+), 53 deletions(-) diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 14dcab450acbe..7ffc787b0a2b4 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -457,7 +457,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -467,7 +468,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/", @@ -477,7 +479,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\", @@ -487,7 +490,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:", @@ -497,7 +501,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:\\", @@ -507,7 +512,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:/", @@ -517,7 +523,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -527,7 +534,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/", @@ -537,7 +545,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/foo/", @@ -547,7 +556,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/bar", @@ -557,7 +567,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("/foo/bar", @@ -567,7 +578,8 @@ pub fn test_decompositions_windows() { parent: Some("/foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("///foo///", @@ -577,7 +589,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("///foo///bar", @@ -587,7 +600,8 @@ pub fn test_decompositions_windows() { parent: Some("///foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./.", @@ -597,7 +611,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -607,7 +622,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -617,7 +633,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -627,7 +644,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/..", @@ -637,7 +655,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -647,7 +666,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/./bar", @@ -657,7 +677,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("foo/../", @@ -667,7 +688,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/../bar", @@ -677,7 +699,8 @@ pub fn test_decompositions_windows() { parent: Some("foo/.."), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./a", @@ -687,7 +710,8 @@ pub fn test_decompositions_windows() { parent: Some("."), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!(".", @@ -697,7 +721,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -707,7 +732,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -717,7 +743,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a//b", @@ -727,7 +754,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/./b", @@ -737,7 +765,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/b/c", @@ -747,7 +776,9 @@ pub fn test_decompositions_windows() { parent: Some("a/b"), file_name: Some("c"), file_stem: Some("c"), - extension: None); + extension: None, + file_prefix: Some("c") + ); t!("a\\b\\c", iter: ["a", "b", "c"], @@ -756,7 +787,8 @@ pub fn test_decompositions_windows() { parent: Some("a\\b"), file_name: Some("c"), file_stem: Some("c"), - extension: None + extension: None, + file_prefix: Some("c") ); t!("\\a", @@ -766,7 +798,8 @@ pub fn test_decompositions_windows() { parent: Some("\\"), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!("c:\\foo.txt", @@ -776,7 +809,8 @@ pub fn test_decompositions_windows() { parent: Some("c:\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\server\\share\\foo.txt", @@ -786,7 +820,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\server\\share\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\server\\share", @@ -796,7 +831,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\server", @@ -806,7 +842,8 @@ pub fn test_decompositions_windows() { parent: Some("\\"), file_name: Some("server"), file_stem: Some("server"), - extension: None + extension: None, + file_prefix: Some("server") ); t!("\\\\?\\bar\\foo.txt", @@ -816,7 +853,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\bar\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\bar", @@ -826,7 +864,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\", @@ -836,7 +875,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\UNC\\server\\share\\foo.txt", @@ -846,7 +886,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\UNC\\server\\share\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\UNC\\server", @@ -856,7 +897,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\UNC\\", @@ -866,7 +908,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:\\foo.txt", @@ -876,7 +919,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\C:\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\C:\\", @@ -886,7 +930,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:", @@ -896,7 +941,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\foo/bar", @@ -906,7 +952,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:/foo", @@ -916,7 +963,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo\\bar", @@ -926,7 +974,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo\\"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("\\\\.\\foo", @@ -936,7 +985,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo/bar", @@ -946,7 +996,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo/"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("\\\\.\\foo\\bar/baz", @@ -956,7 +1007,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo\\bar"), file_name: Some("baz"), file_stem: Some("baz"), - extension: None + extension: None, + file_prefix: Some("baz") ); t!("\\\\.\\", @@ -966,7 +1018,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\a\\b\\", @@ -976,7 +1029,41 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\a\\"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") + ); + + t!("\\\\?\\C:\\foo.txt.zip", + iter: ["\\\\?\\C:", "\\", "foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt.zip"), + file_stem: Some("foo.txt"), + extension: Some("zip"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\C:\\.foo.txt.zip", + iter: ["\\\\?\\C:", "\\", ".foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo.txt.zip"), + file_stem: Some(".foo.txt"), + extension: Some("zip"), + file_prefix: Some(".foo") + ); + + t!("\\\\?\\C:\\.foo", + iter: ["\\\\?\\C:", "\\", ".foo"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") ); } From 7465192c7de96ac46a9c5b0200a34a6d010068ac Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Tue, 15 Jun 2021 14:00:22 +1000 Subject: [PATCH 3/7] simplify logic for split_file_at_dot --- library/std/src/path.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index cec8010fe24be..17e47b76cad73 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -346,15 +346,11 @@ fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { // only from ASCII-bounded slices of existing &OsStr values. let i = match slice[1..].iter().position(|b| *b == b'.') { Some(i) => i + 1, - None => slice.len(), + None => return (Some(file), None), }; - if i == slice.len() { - (Some(file), None) - } else { - let before = Some(&slice[..i]); - let after = Some(&slice[i + 1..]); - unsafe { (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s))) } - } + let before = &slice[..i]; + let after = &slice[i + 1..]; + unsafe { (Some(u8_slice_as_os_str(before)), Some(u8_slice_as_os_str(after))) } } //////////////////////////////////////////////////////////////////////////////// From a889529e984f1d477153e60c16fa4066d8b6c9d7 Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Tue, 15 Jun 2021 14:47:23 +1000 Subject: [PATCH 4/7] add explicit hidden file name tests --- library/std/src/path/tests.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 7ffc787b0a2b4..d7ba478939169 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -445,6 +445,17 @@ pub fn test_decompositions_unix() { extension: Some("toml"), file_prefix: Some(".rustfmt") ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") + ); } #[test] @@ -1065,6 +1076,17 @@ pub fn test_decompositions_windows() { extension: None, file_prefix: Some(".foo") ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") + ); } #[test] @@ -1103,6 +1125,8 @@ pub fn test_stem_ext() { t!("..", file_stem: None, extension: None); + t!(".x.y.z", file_stem: Some(".x.y"), extension: Some("z")); + t!("", file_stem: None, extension: None); } @@ -1142,6 +1166,8 @@ pub fn test_prefix_ext() { t!("..", file_prefix: None, extension: None); + t!(".x.y.z", file_prefix: Some(".x"), extension: Some("z")); + t!("", file_prefix: None, extension: None); } From fcb1ebf1945c616365c74e95666dcdcfdc8bbd70 Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Thu, 24 Jun 2021 13:45:56 +1000 Subject: [PATCH 5/7] change return signature for split_file_at_dot --- library/std/src/path.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 17e47b76cad73..1cb596d691c41 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -334,10 +334,10 @@ fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { } } -fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { +fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { let slice = os_str_as_u8_slice(file); if slice == b".." { - return (Some(file), None); + return (file, None); } // The unsafety here stems from converting between &OsStr and &[u8] @@ -346,11 +346,11 @@ fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { // only from ASCII-bounded slices of existing &OsStr values. let i = match slice[1..].iter().position(|b| *b == b'.') { Some(i) => i + 1, - None => return (Some(file), None), + None => return (file, None), }; let before = &slice[..i]; let after = &slice[i + 1..]; - unsafe { (Some(u8_slice_as_os_str(before)), Some(u8_slice_as_os_str(after))) } + unsafe { (u8_slice_as_os_str(before), Some(u8_slice_as_os_str(after))) } } //////////////////////////////////////////////////////////////////////////////// @@ -2201,9 +2201,11 @@ impl Path { /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); /// assert_eq!("foo", Path::new("foo.tar.gz").file_prefix().unwrap()); /// ``` - #[unstable(feature = "path_file_prefix", issue = "none")] + #[unstable(feature = "path_file_prefix", issue = "86319")] pub fn file_prefix(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after)) + self.file_name() + .map(split_file_at_dot) + .and_then(|(before, after)| if before.is_empty() { after } else { Some(before) }) } /// Extracts the extension of [`self.file_name`], if possible. From 1e759bef919e3600481839324ba02c637ec13ee1 Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Thu, 24 Jun 2021 14:26:10 +1000 Subject: [PATCH 6/7] make docs clearer about how hidden files are dealt with --- library/std/src/path.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 1cb596d691c41..9ab2f045d697e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2175,22 +2175,29 @@ impl Path { /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap()); /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap()); /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_prefix`], which extracts the portion of the file name + /// before the *first* `.` + /// + /// [`Path::file_prefix`]: Path::file_prefix + /// #[stable(feature = "rust1", since = "1.0.0")] pub fn file_stem(&self) -> Option<&OsStr> { self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.or(after)) } - /// Extracts the prefix (non-extension(s)) portion of [`self.file_name`]. This is a "left" - /// variant of `file_stem` - meaning it takes the portion of the file name before the *first* `.` - /// - /// [`self.file_name`]: Path::file_name + /// Extracts the prefix of [`self.file_name`]. /// /// The prefix is: /// /// * [`None`], if there is no file name; /// * The entire file name if there is no embedded `.`; + /// * The portion of the file name before the first non-beginning `.`; /// * The entire file name if the file name begins with `.` and has no other `.`s within; - /// * Otherwise, the portion of the file name before the first `.` + /// * The portion of the file name before the second `.` if the file name begins with `.` + /// + /// [`self.file_name`]: Path::file_name /// /// # Examples /// @@ -2201,6 +2208,13 @@ impl Path { /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); /// assert_eq!("foo", Path::new("foo.tar.gz").file_prefix().unwrap()); /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_stem`], which extracts the portion of the file name + /// before the *last* `.` + /// + /// [`Path::file_stem`]: Path::file_stem + /// #[unstable(feature = "path_file_prefix", issue = "86319")] pub fn file_prefix(&self) -> Option<&OsStr> { self.file_name() From 51cf318dbcdd38d6a7478413513a72097d9622c3 Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Tue, 17 Aug 2021 12:24:28 +1000 Subject: [PATCH 7/7] remove unnecessary empty check --- library/std/src/path.rs | 4 +--- library/std/src/path/tests.rs | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 9ab2f045d697e..8a2450fcb887c 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2217,9 +2217,7 @@ impl Path { /// #[unstable(feature = "path_file_prefix", issue = "86319")] pub fn file_prefix(&self) -> Option<&OsStr> { - self.file_name() - .map(split_file_at_dot) - .and_then(|(before, after)| if before.is_empty() { after } else { Some(before) }) + self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) } /// Extracts the extension of [`self.file_name`], if possible. diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index d7ba478939169..e8b960e6d7370 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1127,6 +1127,8 @@ pub fn test_stem_ext() { t!(".x.y.z", file_stem: Some(".x.y"), extension: Some("z")); + t!("..x.y.z", file_stem: Some("..x.y"), extension: Some("z")); + t!("", file_stem: None, extension: None); } @@ -1168,6 +1170,8 @@ pub fn test_prefix_ext() { t!(".x.y.z", file_prefix: Some(".x"), extension: Some("z")); + t!("..x.y.z", file_prefix: Some("."), extension: Some("z")); + t!("", file_prefix: None, extension: None); }