diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index c096f2d062..150cfe8485 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -358,6 +358,84 @@ pub( crate ) mod private vec![] } + + + + + /// Extracts the parent directory and file stem (without extension) from the given path. + /// + /// This function takes a path and returns an Option containing the modified path without the extension. + /// If the input path is empty or if it doesn't contain a file stem, it returns None. + /// + /// # Arguments + /// + /// * `path` - An object that can be converted into a Path reference, representing the file path. + /// + /// # Returns + /// + /// An Option containing the modified path without the extension, or None if the input path is empty or lacks a file stem. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// use proper_path_tools::path::without_ext; + /// + /// let path = "/path/to/file.txt"; + /// let modified_path = without_ext(path); + /// assert_eq!(modified_path, Some(PathBuf::from("/path/to/file"))); + /// ``` + /// + /// ``` + /// use std::path::PathBuf; + /// use proper_path_tools::path::without_ext; + /// + /// let empty_path = ""; + /// let modified_path = without_ext(empty_path); + /// assert_eq!(modified_path, None); + /// ``` + /// + pub fn without_ext( path : impl AsRef< std::path::Path > ) -> Option< std::path::PathBuf > + { + use std::path::Path; + use std::path::PathBuf; + + if path.as_ref().to_string_lossy().is_empty() + { + return None; + } + + let path_buf = Path::new( path.as_ref() ); + + let parent = match path_buf.parent() + { + Some( parent ) => parent, + None => return None, + }; + let file_stem = match path_buf.file_stem() + { + Some( name ) => + { + let ends = format!( "{}/", name.to_string_lossy() ); + if path.as_ref().to_string_lossy().ends_with( &ends ) + { + ends + } + else + { + String::from( name.to_string_lossy() ) + } + + } + None => return None, + }; + + let mut full_path = parent.to_path_buf(); + full_path.push( file_stem ); + + Some( PathBuf::from( full_path.to_string_lossy().replace( "\\", "/" ) ) ) + } + /// Finds the common directory path among a collection of paths. /// /// Given an iterator of path strings, this function determines the common directory @@ -744,6 +822,7 @@ crate::mod_interface! protected use exts; protected use path_relative; protected use rebase; protected use path_common; + protected use without_ext; protected use is_glob; protected use normalize; protected use canonicalize; diff --git a/module/core/proper_path_tools/tests/inc/mod.rs b/module/core/proper_path_tools/tests/inc/mod.rs index 71c039c7a4..ddab6de7c0 100644 --- a/module/core/proper_path_tools/tests/inc/mod.rs +++ b/module/core/proper_path_tools/tests/inc/mod.rs @@ -4,7 +4,9 @@ use super::*; mod path_normalize; mod path_is_glob; mod absolute_path; -mod path_exts;mod path_common; +mod path_exts; +mod without_ext; +mod path_common; mod rebase_path; mod path_relative; diff --git a/module/core/proper_path_tools/tests/inc/without_ext.rs b/module/core/proper_path_tools/tests/inc/without_ext.rs new file mode 100644 index 0000000000..fa1c5bf11e --- /dev/null +++ b/module/core/proper_path_tools/tests/inc/without_ext.rs @@ -0,0 +1,114 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn empty_path() +{ + let path = ""; + let expected = None; + assert_eq!( the_module::path::without_ext( path ), expected ); +} + +#[ test ] +fn txt_extension() +{ + let path = "some.txt"; + let expected = "some"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn path_with_non_empty_dir_name() +{ + let path = "/foo/bar/baz.asdf"; + let expected = "/foo/bar/baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn hidden_file() +{ + let path = "/foo/bar/.baz"; + let expected = "/foo/bar/.baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn file_with_composite_file_name() +{ + let path = "/foo.coffee.md"; + let expected = "/foo.coffee"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn path_without_extension() +{ + let path = "/foo/bar/baz"; + let expected = "/foo/bar/baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_1() +{ + let path = "./foo/.baz"; + let expected = "./foo/.baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_2() +{ + let path = "./.baz"; + let expected = "./.baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_3() +{ + let path = ".baz.txt"; + let expected = ".baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_4() +{ + let path = "./baz.txt"; + let expected = "./baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_5() +{ + let path = "./foo/baz.txt"; + let expected = "./foo/baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_6() +{ + let path = "./foo/"; + let expected = "./foo/"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_7() +{ + let path = "baz"; + let expected = "baz"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} + +#[ test ] +fn relative_path_8() +{ + let path = "baz.a.b"; + let expected = "baz.a"; + assert_eq!( the_module::path::without_ext( path ).unwrap().to_string_lossy(), expected ); +} \ No newline at end of file