From a262ead12c13b383159ad5e932f6b26574fd0d21 Mon Sep 17 00:00:00 2001 From: SupperZum Date: Mon, 8 Apr 2024 22:37:05 +0300 Subject: [PATCH 1/3] add join_path function --- module/core/proper_path_tools/src/path.rs | 139 +++++++++- .../core/proper_path_tools/tests/inc/mod.rs | 1 + .../proper_path_tools/tests/inc/path_join.rs | 252 ++++++++++++++++++ 3 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 module/core/proper_path_tools/tests/inc/path_join.rs diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index 35560947f7..f889d51cc3 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -301,11 +301,148 @@ pub( crate ) mod private Ok( format!( "{}_{}_{}_{}", timestamp, pid, tid, count ) ) } + /// Joins a list of file system paths into a single absolute path. + /// + /// This function takes a list of file system paths and joins them into a single path, + /// normalizing and simplifying them as it goes. The result is returned as a PathBuf. + /// + /// Examples: + /// + /// ``` + /// + /// let paths = vec![ "a/b/c", "/d/e", "f/g" ]; + /// let joined = proper_path_tools::path::join_paths( paths.into_iter() ); + /// assert_eq!( joined, std::path::PathBuf::from( "/d/e/f/g" ) ); + /// + /// let paths = vec![]; + /// let joined = proper_path_tools::path::join_paths( paths.into_iter() ); + /// assert_eq!( joined, std::path::PathBuf::from( "" ) ); + /// + /// let paths = vec![ "", "a/b", "", "c", "" ]; + /// let joined = proper_path_tools::path::join_paths( paths.into_iter() ); + /// assert_eq!( joined, std::path::PathBuf::from( "/a/b/c" ) ); + /// + /// ``` + pub fn join_paths< 'a, I >( paths : I ) -> std::path::PathBuf + where + I : Iterator< Item = &'a str >, + { + + let mut result = String::new(); + + for path in paths + { + + let mut path = path.replace( '\\', "/" ); + path = path.replace( ':', "" ); + + let mut added_slah = false; + + // If the path is empty, skip it + if path.is_empty() + { + continue; + } + + // If the path starts with '/', clear the result and set it to '/' + if path.starts_with( '/' ) + { + result.clear(); + result.push( '/' ); + } + // If the result doesn't end with '/', append '/' + else if !result.ends_with( '/' ) + { + added_slah = true; + result.push( '/' ); + } + let components : Vec< &str > = path.split( '/' ).collect(); + // Split the path into components + for ( idx, component ) in components.clone().into_iter().enumerate() + { + match component + { + "." => + { + + if ( result.ends_with( '/' ) && components.len() > idx + 1 && components[ idx + 1 ].is_empty() ) || components.len() == idx + 1 + { + result.pop(); + } + + }, + ".." => + { + + if result != "/" + { + if added_slah + { + result.pop(); + added_slah = false; + } + let mut parts : Vec< _ > = result.split( '/' ).collect(); + parts.pop(); + if let Some( part ) = parts.last() + { + if part.is_empty() + { + parts.push( "" ); + } + + } + result = parts.join( "/" ); + if result.is_empty() + { + result.push( '/' ); + } + } + else + { + result.push_str( &components[ idx.. ].to_vec().join( "/" ) ); + break; + } + } + _ => + { + if !component.is_empty() + { + if result.ends_with( '/' ) + { + result.push_str( component ); + + } + else + { + result.push( '/' ); + result.push_str( component ); + } + } + else if components.len() > idx + 1 && components[ idx + 1 ].is_empty() && path != "/" + { + result.push( '/' ); + } + + } + } + } + + if path.ends_with( '/' ) && result != "/" + { + result.push('/'); + } + } + + result.into() + + } + + } crate::mod_interface! { - + protected use join_paths; 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 cc74b4a975..5706fe775d 100644 --- a/module/core/proper_path_tools/tests/inc/mod.rs +++ b/module/core/proper_path_tools/tests/inc/mod.rs @@ -4,6 +4,7 @@ use super::*; mod path_normalize; mod path_is_glob; mod absolute_path; +mod path_join; #[ cfg( feature = "path_unique_folder_name" ) ] mod path_unique_folder_name; diff --git a/module/core/proper_path_tools/tests/inc/path_join.rs b/module/core/proper_path_tools/tests/inc/path_join.rs new file mode 100644 index 0000000000..4bc1c3db62 --- /dev/null +++ b/module/core/proper_path_tools/tests/inc/path_join.rs @@ -0,0 +1,252 @@ +use super::*; + + +#[ test ] +fn join_empty() +{ + let ( expected, paths ) = ( "", vec![ "" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn join_several_empties() +{ + let ( expected, paths ) = ( "", vec![ "", "" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn root_with_absolute() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/", "/a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn root_with_relative() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/", "a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn dir_with_absolute() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir", "/a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn dir_with_relative() +{ + let ( expected, paths ) = ( "/dir/a/b", vec![ "/dir", "a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_dir_with_absolute() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir/", "/a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn trailed_dir_with_relative() +{ + let ( expected, paths ) = ( "/dir/a/b", vec![ "/dir/", "a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn dir_with_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir", "../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_dir_with_down() +{ + let ( expected, paths ) = ( "/dir/a/b", vec![ "/dir/", "../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn dir_with_several_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir/dir2", "../../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_dir_with_several_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/dir/", "../../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn dir_with_several_down_go_out_of_root() +{ + let ( expected, paths ) = ( "/../a/b", vec![ "/dir", "../../a/b" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn trailed_absolute_with_trailed_down() +{ + let ( expected, paths ) = ( "/a/b/", vec![ "/a/b/", "../" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn absolute_with_trailed_down() +{ + let ( expected, paths ) = ( "/a/", vec![ "/a/b", "../" ]) ; + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_absolute_with_down() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/a/b/", ".." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_absolute_with_trailed_here() +{ + let ( expected, paths ) = ( "/a/b/", vec![ "/a/b/", "./" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn absolute_with_trailed_here() +{ + let ( expected, paths ) = ( "/a/b/", vec![ "/a/b", "./" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn trailed_absolute_with_here() +{ + let ( expected, paths ) = ( "/a/b", vec![ "/a/b/", "." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn join_with_empty() +{ + let ( expected, paths ) = ( "/a/b/c", vec![ "", "a/b", "", "c", "" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn join_windows_os_paths() +{ + let ( expected, paths ) = ( "/c/foo/bar/", vec![ "c:\\", "foo\\", "bar\\" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn join_unix_os_paths() +{ + let ( expected, paths ) = ( "/baz/foo", vec![ "/bar/", "/baz", "foo/", "." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn join_unix_os_paths_2() +{ + let ( expected, paths ) = ( "/baz/foo/z", vec![ "/bar/", "/baz", "foo/", ".", "z" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn more_complicated_cases_1() +{ + let ( expected, paths ) = ( "/aa/bb//cc", vec![ "/aa", "bb//", "cc" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + + +#[ test ] +fn more_complicated_cases_2() +{ + let ( expected, paths ) = ( "/bb/cc", vec![ "/aa", "/bb", "cc" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn more_complicated_cases_3() +{ + let ( expected, paths ) = ( "//aa/bb//cc//", vec![ "//aa", "bb//", "cc//" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + + +#[ test ] +fn more_complicated_cases_4() +{ + let ( expected, paths ) = ( "/aa/bb//cc", vec![ "/aa", "bb//", "cc", "." ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} + +#[ test ] +fn more_complicated_cases_5() +{ + let ( expected, paths ) = ( "//b//d/..e", vec![ "/", "a", "//b//", "././c", "../d", "..e" ] ); + let result = the_module::path::join_paths( paths.clone().into_iter() ); + assert_eq!( result, std::path::PathBuf::from( expected ), "Test failed. Paths: '{:?}', Expected: '{}', Got: '{}'", paths, expected, result.to_string_lossy() ); +} \ No newline at end of file From 6bae1d448fd3a41367766ac32710ce7083d16db5 Mon Sep 17 00:00:00 2001 From: SupperZum Date: Fri, 10 May 2024 13:06:47 +0300 Subject: [PATCH 2/3] upd --- module/core/proper_path_tools/src/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index 3644a0492f..4a6f742ca9 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -468,7 +468,7 @@ pub( crate ) mod private #[ cfg( feature = "no_std" ) ] use alloc::string::ToString; - if let Some( file_name ) = Path::new( path.as_ref() ).file_name() + if let Some( file_name ) = std::path::Path::new( path.as_ref() ).file_name() { if let Some( file_name_str ) = file_name.to_str() { From 97721046a13555f13a0747601daea09fbbd740d1 Mon Sep 17 00:00:00 2001 From: SupperZum Date: Sun, 12 May 2024 23:45:14 +0300 Subject: [PATCH 3/3] fix --- module/core/proper_path_tools/src/path.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index 4a6f742ca9..a48058b513 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -216,6 +216,10 @@ pub( crate ) mod private { #[ cfg( target_os = "windows" ) ] use std::path::PathBuf; + #[ cfg( feature = "no_std" ) ] + extern crate alloc; + #[ cfg( feature = "no_std" ) ] + use alloc::string::ToString; // println!( "a" ); // let path = path.as_ref().canonicalize()?; @@ -331,6 +335,13 @@ pub( crate ) mod private where I : Iterator< Item = &'a str >, { + #[ cfg( feature = "no_std" ) ] + extern crate alloc; + #[ cfg( feature = "no_std" ) ] + use alloc::string::String; + #[ cfg( feature = "no_std" ) ] + use alloc::vec::Vec; + let mut result = String::new(); for path in paths { @@ -679,7 +690,7 @@ pub( crate ) mod private /// /// * `path` - A mutable reference to a string representing the path to be cleaned. /// - fn path_remove_dots( path : &mut String ) + fn path_remove_dots( path : &mut std::string::String ) { let mut cleaned_parts = vec![]; for part in path.split( '/' )