From 13bf12fe4d3584a55b250459d0e54a813599cb67 Mon Sep 17 00:00:00 2001 From: SupperZum Date: Wed, 10 Apr 2024 17:01:57 +0300 Subject: [PATCH 1/3] add common --- module/core/proper_path_tools/src/path.rs | 77 ++- .../core/proper_path_tools/tests/inc/mod.rs | 1 + .../tests/inc/path_common.rs | 490 ++++++++++++++++++ 3 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 module/core/proper_path_tools/tests/inc/path_common.rs diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index 35560947f7..ce54b3f98d 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -301,11 +301,86 @@ pub( crate ) mod private Ok( format!( "{}_{}_{}_{}", timestamp, pid, tid, count ) ) } + /// Finds the common directory path among a collection of paths. + /// + /// Given an iterator of path strings, this function determines the common directory + /// path shared by all paths. If no common directory path exists, it returns `None`. + /// + /// # Arguments + /// + /// * `paths` - An iterator of path strings (`&str`). + /// + /// # Returns + /// + /// * `Option` - The common directory path shared by all paths, if it exists. + /// If no common directory path exists, returns `None`. + /// + /// # Examples + /// + /// ``` + /// use proper_path_tools::path::::path_common; + /// + /// let paths = vec![ "/a/b/c", "/a/b/d", "/a/b/e" ]; + /// let common_path = path_common( paths.into_iter() ); + /// assert_eq!( common_path, Some( "/a/b/".to_string() ) ); + /// ``` + /// + pub fn path_common< 'a, I >( paths : I ) -> Option< String > + where + I : Iterator< Item = &'a str >, + { + use std::collections::HashMap; + + let paths : Vec< String > = paths.map( | path | path.to_string() ).collect(); + + if paths.is_empty() + { + return None; + } + + // Create a map to store directory frequencies + let mut dir_freqs : HashMap< String, usize > = HashMap::new(); + + // Iterate over paths to count directory frequencies + for path in paths.iter() + { + // Split path into directories + let dirs : Vec< &str > = path.split( '/' ).collect(); + + // Iterate over directories + for i in 0..dirs.len() + { + // Construct directory path + let dir_path = dirs[ 0..i + 1 ].join( "/" ); + if dir_path.is_empty() + { + continue; + } + // Increment frequency count + *dir_freqs.entry( dir_path ).or_insert( 0 ) += 1; + } + } + + // Find the directory with the highest frequency + let common_dir = dir_freqs + .into_iter() + .filter( | ( _, freq ) | *freq == paths.len() ) + .map( | ( dir, _ ) | dir ) + .max_by_key( | dir | dir.len() ) + .unwrap_or_default(); + + let mut result = common_dir.to_string(); + //result.push( '/' ); + Some( result ) + + + } + } crate::mod_interface! { - + protected use path_common; 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..c8565805a2 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_common; #[ cfg( feature = "path_unique_folder_name" ) ] mod path_unique_folder_name; diff --git a/module/core/proper_path_tools/tests/inc/path_common.rs b/module/core/proper_path_tools/tests/inc/path_common.rs new file mode 100644 index 0000000000..2f9dbbd395 --- /dev/null +++ b/module/core/proper_path_tools/tests/inc/path_common.rs @@ -0,0 +1,490 @@ +#[ allow( unused_imports ) ] +use super::*; + + +#[ test ] +fn test_with_empty_array() +{ + let paths : Vec< &str > = vec![]; + let got = the_module::path::path_common( paths.into_iter() ); + assert_eq!( got, None ); +} + +#[ test ] +fn test_array() +{ + let paths = vec![ "/a1/b2", "/a1/b" ]; + let got = the_module::path::path_common( paths.into_iter() ).unwrap(); + assert_eq!( got, "/a1/" ); +} + +#[ test ] +fn test_absolute_absolute_have_common_dir() +{ + let got = the_module::path::path_common( vec![ "/a1/b2", "/a1/a" ].into_iter() ).unwrap(); + assert_eq!( got, "/a1/" ); +} + +#[ test ] +fn test_absolute_absolute_have_common_dir_2() +{ + let got = the_module::path::path_common( vec![ "/a1/b1/c", "/a1/b1/d", "/a1/b2" ].into_iter() ).unwrap(); + assert_eq!( got, "/a1/" ); +} + +#[ test ] +fn test_absolute_absolute_have_common_dir_and_part_of_name() +{ + let got = the_module::path::path_common( vec![ "/a1/b2", "/a1/b1" ].into_iter() ).unwrap(); + assert_eq!( got, "/a1/" ); +} + +#[ test ] +fn test_absolute_absolute_one_path_has_dots_identical_paths() +{ + let got = the_module::path::path_common( vec![ "/a1/x/../b1", "/a1/b1" ].into_iter() ).unwrap(); + assert_eq!( got, "/a1/b1" ); +} + +#[ test ] +fn test_absolute_absolute_more_than_one_dir_in_common_path() +{ + let got = the_module::path::path_common( vec![ "/a1/b1/c1", "/a1/b1/c" ].into_iter() ).unwrap(); + assert_eq!( got, "/a1/b1/" ); +} + +#[ test ] +fn test_absolute_absolute_one_path_have_dots_no_common_dirs() +{ + let got = the_module::path::path_common( vec![ "/a1/../../b1/c1", "/a1/b1/c1" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_absolute_dir_name_is_part_of_another_dir_name() +{ + let got = the_module::path::path_common( vec![ "/abcd", "/ab" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_absolute_dir_names_has_dots_have_common_path() +{ + let got = the_module::path::path_common( vec![ "/.a./.b./.c.", "/.a./.b./.c" ].into_iter() ).unwrap(); + assert_eq!( got, "/.a./.b./" ); +} + +#[ test ] +fn test_absolute_absolute_one_path_has_several_slashes_the_other_has_not_not_identical() +{ + let got = the_module::path::path_common( vec![ "//a//b//c", "/a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_absolute_identical_paths_with_several_slashes() +{ + let got = the_module::path::path_common( vec![ "/a//b", "/a//b" ].into_iter() ).unwrap(); + assert_eq!( got, "/a//b" ); +} + +#[ test ] +fn test_absolute_absolute_identical_paths_with_several_slashes_2() +{ + let got = the_module::path::path_common( vec![ "/a//", "/a//" ].into_iter() ).unwrap(); + assert_eq!( got, "/a//" ); +} + +#[ test ] +fn test_absolute_absolute_one_path_has_here_token_dirs_identical_paths() +{ + let got = the_module::path::path_common( vec![ "/./a/./b/./c", "/a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "/a/b" ); +} + +#[ test ] +fn test_absolute_absolute_different_case_in_path_name_not_identical() +{ + let got = the_module::path::path_common( vec![ "/A/b/c", "/a/b/c" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_absolute_one_path_is_root_directory_common_root_directory() +{ + let got = the_module::path::path_common( vec![ "/", "/x" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_absolute_different_paths_in_root_directory_common_root_directory() +{ + let got = the_module::path::path_common( vec![ "/a", "/x" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_absolute_more_than_2_path_in_arguments() +{ + let got = the_module::path::path_common( vec![ "/a/b/c", "/a/b/c", "/a/b/c", "/a/b/c" ].into_iter() ).unwrap(); + assert_eq!( got, "/a/b/c" ); +} + +#[ test ] +fn test_absolute_absolute_more_than_2_path_in_arguments_variant2() +{ + let got = the_module::path::path_common( vec![ "/a/b/c", "/a/b/c", "/a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "/a/b" ); +} + +#[ test ] +fn test_absolute_absolute_more_than_2_path_in_arguments_variant3() +{ + let got = the_module::path::path_common( vec![ "/a/b/c", "/a/b/c", "/a/b1" ].into_iter() ).unwrap(); + assert_eq!( got, "/a/" ); +} + +#[ test ] +fn test_absolute_absolute_more_than_2_path_in_arguments_variant4() +{ + let got = the_module::path::path_common( vec![ "/a/b/c", "/a/b/c", "/a" ].into_iter() ).unwrap(); + assert_eq!( got, "/a" ); +} + +#[ test ] +fn test_absolute_absolute_more_than_2_path_in_arguments_variant5() +{ + let got = the_module::path::path_common( vec![ "/a/b/c", "/a/b/c", "/x" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_absolute_more_than_2_path_in_arguments_variant6() +{ + let got = the_module::path::path_common( vec![ "/a/b/c", "/a/b/c", "/" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + + + + + + + + + +// absolute-relative + +#[ test ] +fn test_absolute_relative_root_and_down_token() +{ + let got = the_module::path::path_common( vec![ "/", ".." ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_relative_root_and_here_token() +{ + let got = the_module::path::path_common( vec![ "/", "." ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_relative_root_and_some_relative_directory() +{ + let got = the_module::path::path_common( vec![ "/", "x" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_relative_root_and_double_down_token_in_path() +{ + let got = the_module::path::path_common( vec![ "/", "../.." ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_relative_root_with_here_token_and_down_token() +{ + let got = the_module::path::path_common( vec![ "/.", ".." ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_relative_root_with_here_token_and_here_token() +{ + let got = the_module::path::path_common( vec![ "/.", "." ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_relative_root_with_here_token_and_some_relative_directory() +{ + let got = the_module::path::path_common( vec![ "/.", "x" ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + +#[ test ] +fn test_absolute_relative_root_with_here_token_and_double_down_token_in_path() +{ + let got = the_module::path::path_common( vec![ "/.", "../.." ].into_iter() ).unwrap(); + assert_eq!( got, "/" ); +} + + + + + + + +// relative - relative +#[ test ] +fn test_relative_relative_common_dir() +{ + let got = the_module::path::path_common( vec![ "a1/b2", "a1/a" ].into_iter() ).unwrap(); + assert_eq!( got, "a1/" ); +} + +#[ test ] +fn test_relative_relative_common_dir_and_part_of_dir_names() +{ + let got = the_module::path::path_common( vec![ "a1/b2", "a1/b1" ].into_iter() ).unwrap(); + assert_eq!( got, "a1/" ); +} + +#[ test ] +fn test_relative_relative_one_path_with_down_token_dir_identical_paths() +{ + let got = the_module::path::path_common( vec![ "a1/x/../b1", "a1/b1" ].into_iter() ).unwrap(); + assert_eq!( got, "a1/b1" ); +} + +#[ test ] +fn test_relative_relative_paths_begins_with_here_token_directory_dots_identical_paths() +{ + let got = the_module::path::path_common( vec![ "./a1/x/../b1", "./a1/b1" ].into_iter() ).unwrap(); + assert_eq!( got, "a1/b1" ); +} + +#[ test ] +fn test_relative_relative_one_path_begins_with_here_token_dir_another_down_token() +{ + let got = the_module::path::path_common( vec![ "./a1/x/../b1", "../a1/b1" ].into_iter() ).unwrap(); + assert_eq!( got, ".." ); +} + +#[ test ] +fn test_relative_relative_here_token_and_down_token() +{ + let got = the_module::path::path_common( vec![ ".", ".." ].into_iter() ).unwrap(); + assert_eq!( got, ".." ); +} + +#[ test ] +fn test_relative_relative_different_paths_start_with_here_token_dir() +{ + let got = the_module::path::path_common( vec![ "./b/c", "./x" ].into_iter() ).unwrap(); + assert_eq!( got, "." ); +} + + + + +//combinations of paths with dots + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots() +{ + let got = the_module::path::path_common( vec![ "./././a", "./a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "a" ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant2() +{ + let got = the_module::path::path_common( vec![ "./a/./b", "./a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "a/b" ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant3() +{ + let got = the_module::path::path_common( vec![ "./a/./b", "./a/c/../b" ].into_iter() ).unwrap(); + assert_eq!( got, "a/b" ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant4() +{ + let got = the_module::path::path_common( vec![ "../b/c", "./x" ].into_iter() ).unwrap(); + assert_eq!( got, ".." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant5() +{ + let got = the_module::path::path_common( vec![ "../../b/c", "../b" ].into_iter() ).unwrap(); + assert_eq!( got, "../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant6() +{ + let got = the_module::path::path_common( vec![ "../../b/c", "../../../x" ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant7() +{ + let got = the_module::path::path_common( vec![ "../../b/c/../../x", "../../../x" ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant8() +{ + let got = the_module::path::path_common( vec![ "./../../b/c/../../x", "./../../../x" ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant9() +{ + let got = the_module::path::path_common( vec![ "../../..", "./../../.." ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant10() +{ + let got = the_module::path::path_common( vec![ "./../../..", "./../../.." ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant11() +{ + let got = the_module::path::path_common( vec![ "../../..", "../../.." ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant12() +{ + let got = the_module::path::path_common( vec![ "../b", "../b" ].into_iter() ).unwrap(); + assert_eq!( got, "../b" ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant13() +{ + let got = the_module::path::path_common( vec![ "../b", "./../b" ].into_iter() ).unwrap(); + assert_eq!( got, "../b" ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths() +{ + let got = the_module::path::path_common( vec![ "a/b/c", "a/b/c", "a/b/c" ].into_iter() ).unwrap(); + assert_eq!( got, "a/b/c" ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths_variant2() +{ + let got = the_module::path::path_common( vec![ "a/b/c", "a/b/c", "a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "a/b" ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths_variant3() +{ + let got = the_module::path::path_common( vec![ "a/b/c", "a/b/c", "a/b1" ].into_iter() ).unwrap(); + assert_eq!( got, "a/" ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths_variant4() +{ + let got = the_module::path::path_common( vec![ "a/b/c", "a/b/c", "." ].into_iter() ).unwrap(); + assert_eq!( got, "." ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths_variant5() +{ + let got = the_module::path::path_common( vec![ "a/b/c", "a/b/c", "x" ].into_iter() ).unwrap(); + assert_eq!( got, "." ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths_variant6() +{ + let got = the_module::path::path_common( vec![ "a/b/c", "a/b/c", "./" ].into_iter() ).unwrap(); + assert_eq!( got, "." ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths_variant7() +{ + let got = the_module::path::path_common( vec![ "../a/b/c", "a/../b/c", "a/b/../c" ].into_iter() ).unwrap(); + assert_eq!( got, ".." ); +} + +#[ test ] +fn test_relative_relative_several_relative_paths_variant8() +{ + let got = the_module::path::path_common( vec![ "./a/b/c", "../../a/b/c", "../../../a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_dot_and_double_up_and_down_tokens() +{ + let got = the_module::path::path_common( vec![ ".", "./", ".." ].into_iter() ).unwrap(); + assert_eq!( got, ".." ); +} + +#[ test ] +fn test_relative_relative_dot_and_double_up_and_down_tokens_variant2() +{ + let got = the_module::path::path_common( vec![ ".", "./../..", ".." ].into_iter() ).unwrap(); + assert_eq!( got, "../.." ); +} + +/* +#[ test ] +#[ should_panic ] +fn test_first_path_is_absolute_another_is_dots() +{ + the_module::path::path_common( vec![ "/a", ".."]); +} + +#[ test ] +#[ should_panic ] +fn test_first_path_is_dots_and_absolute_path() +{ + the_module::path::path_common( vec![ "..", "../../b/c", "/a"]); +} + +#[ test ] +#[ should_panic ] +fn test_first_path_is_dots_and_absolute_path_variant2() +{ + the_module::path::path_common( vec![ "../..", "../../b/c", "/a"]); +} + +#[ test ] +#[ should_panic ] +fn test_unknown_path() +{ + the_module::path::path_common( vec![ "/a", "x"]); +} + +#[ test ] +#[ should_panic ] +fn test_unknown_path_variant2() +{ + the_module::path::path_common( vec![ "x", "/a/b/c", "/a"]); +} */ \ No newline at end of file From 3187eaa1adf5d6a196135ee8c59359f2ddbcf396 Mon Sep 17 00:00:00 2001 From: SupperZum Date: Thu, 11 Apr 2024 16:46:32 +0300 Subject: [PATCH 2/3] upd --- module/core/proper_path_tools/src/path.rs | 206 ++++++++++++++++-- .../core/proper_path_tools/tests/inc/mod.rs | 1 + .../tests/inc/path_common.rs | 94 ++++---- .../tests/inc/rebase_path.rs | 57 +++++ 4 files changed, 301 insertions(+), 57 deletions(-) create mode 100644 module/core/proper_path_tools/tests/inc/rebase_path.rs diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index ce54b3f98d..b74076426f 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -318,7 +318,7 @@ pub( crate ) mod private /// # Examples /// /// ``` - /// use proper_path_tools::path::::path_common; + /// use proper_path_tools::path::path_common; /// /// let paths = vec![ "/a/b/c", "/a/b/d", "/a/b/e" ]; /// let common_path = path_common( paths.into_iter() ); @@ -330,37 +330,44 @@ pub( crate ) mod private I : Iterator< Item = &'a str >, { use std::collections::HashMap; - - let paths : Vec< String > = paths.map( | path | path.to_string() ).collect(); - - if paths.is_empty() + let orig_paths : Vec< String > = paths.map( | path | path.to_string() ).collect(); + + if orig_paths.is_empty() { return None; } - + // Create a map to store directory frequencies let mut dir_freqs : HashMap< String, usize > = HashMap::new(); - + + let mut paths = orig_paths.clone(); // Iterate over paths to count directory frequencies - for path in paths.iter() + for path in paths.iter_mut() { + path_remove_dots( path ); + path_remove_double_dots( path ); // Split path into directories let dirs : Vec< &str > = path.split( '/' ).collect(); - + // Iterate over directories - for i in 0..dirs.len() + for i in 0..dirs.len() { + // Construct directory path - let dir_path = dirs[ 0..i + 1 ].join( "/" ); - if dir_path.is_empty() + let mut dir_path = dirs[ 0..i + 1 ].join( "/" ); + + + // Increment frequency count + *dir_freqs.entry( dir_path.clone() ).or_insert( 0 ) += 1; + + if i != dirs.len() - 1 && !dirs[ i + 1 ].is_empty() { - continue; + dir_path.push( '/' ); + *dir_freqs.entry( dir_path ).or_insert( 0 ) += 1; } - // Increment frequency count - *dir_freqs.entry( dir_path ).or_insert( 0 ) += 1; } } - + // Find the directory with the highest frequency let common_dir = dir_freqs .into_iter() @@ -368,18 +375,181 @@ pub( crate ) mod private .map( | ( dir, _ ) | dir ) .max_by_key( | dir | dir.len() ) .unwrap_or_default(); - + let mut result = common_dir.to_string(); - //result.push( '/' ); + + if result.is_empty() + { + if orig_paths.iter().any( | path | path.starts_with( '/' ) ) + { + result.push( '/' ); + } + else if orig_paths.iter().any( | path | path.starts_with( ".." ) ) + { + result.push_str( ".." ); + } + else + { + result.push( '.' ); + } + + } + Some( result ) + + + } + + /// Removes dot segments (".") from the given path string. + /// + /// Dot segments in a path represent the current directory and can be safely removed + /// without changing the meaning of the path. + /// + /// # Arguments + /// + /// * `path` - A mutable reference to a string representing the path to be cleaned. + /// + fn path_remove_dots( path : &mut String ) + { + let mut cleaned_parts = vec![]; + + for part in path.split( '/' ) + { + if part == "." + { + continue; + } + + cleaned_parts.push( part ); + + } + + *path = cleaned_parts.join( "/" ); + + } + + /// Removes dot-dot segments ("..") from the given path string. + /// + /// Dot-dot segments in a path represent the parent directory and can be safely resolved + /// to simplify the path. + /// + /// # Arguments + /// + /// * `path` - A mutable reference to a string representing the path to be cleaned. + /// + fn path_remove_double_dots( path : &mut String ) + { + + let mut cleaned_parts: Vec< &str > = Vec::new(); + let mut delete_empty_part = false; + + for part in path.split( '/' ) + { + if part == ".." + { + if let Some( pop ) = cleaned_parts.pop() + { + if pop.is_empty() + { + delete_empty_part = true; + } + + if pop == ".." + { + cleaned_parts.push(".."); + cleaned_parts.push(".."); + } + } + else + { + cleaned_parts.push( ".." ); + } + } + else + { + cleaned_parts.push( part ); + } + } + if delete_empty_part + { + *path = format!( "/{}", cleaned_parts.join( "/" ) ); + } + else + { + *path = cleaned_parts.join( "/" ); + } + + } + + + /// Rebase the file path relative to a new base path, optionally removing a common prefix. + /// + /// # Arguments + /// + /// * `file_path` - The original file path to rebase. + /// * `new_path` - The new base path to which the file path will be rebased. + /// * `old_path` - An optional common prefix to remove from the file path before rebasing. + /// + /// # Returns + /// + /// Returns the rebased file path if successful, or None if any error occurs. + /// + /// # Examples + /// + /// Rebase a file path to a new base path without removing any common prefix: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let file_path = "/home/user/documents/file.txt"; + /// let new_path = "/mnt/storage"; + /// let rebased_path = proper_path_tools::path::rebase( file_path, new_path, None ).unwrap(); + /// assert_eq!( rebased_path, PathBuf::from( "/mnt/storage/home/user/documents/file.txt" ) ); + /// ``` + /// + /// Rebase a file path to a new base path after removing a common prefix: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let file_path = "/home/user/documents/file.txt"; + /// let new_path = "/mnt/storage"; + /// let old_path = "/home/user"; + /// let rebased_path = proper_path_tools::path::rebase( file_path, new_path, Some( old_path ) ).unwrap(); + /// assert_eq!( rebased_path, PathBuf::from( "/mnt/storage/documents/file.txt" ) ); + /// ``` + /// + pub fn rebase< T : AsRef< std::path::Path > >( file_path : T, new_path : T, old_path : Option< T > ) -> Option< std::path::PathBuf > + { + use std::path::Path; + use std::path::PathBuf; + + let new_path = Path::new( new_path.as_ref() ); + let mut main_file_path = Path::new( file_path.as_ref() ); + + if old_path.is_some() + { + let common = path_common( vec![ file_path.as_ref().to_str().unwrap(), old_path.unwrap().as_ref().to_str().unwrap() ].into_iter() )?; + + main_file_path = match main_file_path.strip_prefix( common ) + { + Ok( rel ) => rel, + Err( _ ) => return None, + }; + } + let mut rebased_path = PathBuf::new(); + rebased_path.push( new_path ); + rebased_path.push( main_file_path.strip_prefix( "/" ).unwrap_or( main_file_path ) ); + Some( normalize( rebased_path ) ) } } crate::mod_interface! { + protected use rebase; protected use path_common; protected use is_glob; protected use normalize; diff --git a/module/core/proper_path_tools/tests/inc/mod.rs b/module/core/proper_path_tools/tests/inc/mod.rs index c8565805a2..0eea9b62b6 100644 --- a/module/core/proper_path_tools/tests/inc/mod.rs +++ b/module/core/proper_path_tools/tests/inc/mod.rs @@ -5,6 +5,7 @@ mod path_normalize; mod path_is_glob; mod absolute_path; mod path_common; +mod rebase_path; #[ cfg( feature = "path_unique_folder_name" ) ] mod path_unique_folder_name; diff --git a/module/core/proper_path_tools/tests/inc/path_common.rs b/module/core/proper_path_tools/tests/inc/path_common.rs index 2f9dbbd395..b491d2106c 100644 --- a/module/core/proper_path_tools/tests/inc/path_common.rs +++ b/module/core/proper_path_tools/tests/inc/path_common.rs @@ -10,13 +10,7 @@ fn test_with_empty_array() assert_eq!( got, None ); } -#[ test ] -fn test_array() -{ - let paths = vec![ "/a1/b2", "/a1/b" ]; - let got = the_module::path::path_common( paths.into_iter() ).unwrap(); - assert_eq!( got, "/a1/" ); -} +// absolute-absolute #[ test ] fn test_absolute_absolute_have_common_dir() @@ -123,6 +117,9 @@ fn test_absolute_absolute_different_paths_in_root_directory_common_root_director assert_eq!( got, "/" ); } + +// more than 2 path in arguments + #[ test ] fn test_absolute_absolute_more_than_2_path_in_arguments() { @@ -320,33 +317,7 @@ fn test_relative_relative_combinations_of_paths_with_dots_variant4() assert_eq!( got, ".." ); } -#[ test ] -fn test_relative_relative_combinations_of_paths_with_dots_variant5() -{ - let got = the_module::path::path_common( vec![ "../../b/c", "../b" ].into_iter() ).unwrap(); - assert_eq!( got, "../.." ); -} - -#[ test ] -fn test_relative_relative_combinations_of_paths_with_dots_variant6() -{ - let got = the_module::path::path_common( vec![ "../../b/c", "../../../x" ].into_iter() ).unwrap(); - assert_eq!( got, "../../.." ); -} - -#[ test ] -fn test_relative_relative_combinations_of_paths_with_dots_variant7() -{ - let got = the_module::path::path_common( vec![ "../../b/c/../../x", "../../../x" ].into_iter() ).unwrap(); - assert_eq!( got, "../../.." ); -} -#[ test ] -fn test_relative_relative_combinations_of_paths_with_dots_variant8() -{ - let got = the_module::path::path_common( vec![ "./../../b/c/../../x", "./../../../x" ].into_iter() ).unwrap(); - assert_eq!( got, "../../.." ); -} #[ test ] fn test_relative_relative_combinations_of_paths_with_dots_variant9() @@ -383,6 +354,9 @@ fn test_relative_relative_combinations_of_paths_with_dots_variant13() assert_eq!( got, "../b" ); } + +// several relative paths + #[ test ] fn test_relative_relative_several_relative_paths() { @@ -432,20 +406,48 @@ fn test_relative_relative_several_relative_paths_variant7() assert_eq!( got, ".." ); } + + #[ test ] -fn test_relative_relative_several_relative_paths_variant8() +fn test_relative_relative_dot_and_double_up_and_down_tokens() { - let got = the_module::path::path_common( vec![ "./a/b/c", "../../a/b/c", "../../../a/b" ].into_iter() ).unwrap(); + let got = the_module::path::path_common( vec![ ".", "./", ".." ].into_iter() ).unwrap(); + assert_eq!( got, ".." ); +} + + + +/* + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant5() +{ + let got = the_module::path::path_common( vec![ "../../b/c", "../b" ].into_iter() ).unwrap(); + assert_eq!( got, "../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant6() +{ + let got = the_module::path::path_common( vec![ "../../b/c", "../../../x" ].into_iter() ).unwrap(); assert_eq!( got, "../../.." ); } #[ test ] -fn test_relative_relative_dot_and_double_up_and_down_tokens() +fn test_relative_relative_combinations_of_paths_with_dots_variant7() { - let got = the_module::path::path_common( vec![ ".", "./", ".." ].into_iter() ).unwrap(); - assert_eq!( got, ".." ); + let got = the_module::path::path_common( vec![ "../../b/c/../../x", "../../../x" ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + +#[ test ] +fn test_relative_relative_combinations_of_paths_with_dots_variant8() +{ + let got = the_module::path::path_common( vec![ "./../../b/c/../../x", "./../../../x" ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); } + #[ test ] fn test_relative_relative_dot_and_double_up_and_down_tokens_variant2() { @@ -453,7 +455,21 @@ fn test_relative_relative_dot_and_double_up_and_down_tokens_variant2() assert_eq!( got, "../.." ); } -/* +#[ test ] +fn test_relative_relative_several_relative_paths_variant8() +{ + let got = the_module::path::path_common( vec![ "./a/b/c", "../../a/b/c", "../../../a/b" ].into_iter() ).unwrap(); + assert_eq!( got, "../../.." ); +} + + + + + + + + + #[ test ] #[ should_panic ] fn test_first_path_is_absolute_another_is_dots() diff --git a/module/core/proper_path_tools/tests/inc/rebase_path.rs b/module/core/proper_path_tools/tests/inc/rebase_path.rs new file mode 100644 index 0000000000..7c8db4350c --- /dev/null +++ b/module/core/proper_path_tools/tests/inc/rebase_path.rs @@ -0,0 +1,57 @@ +#[ allow( unused_imports ) ] +use super::*; +use std::path::PathBuf; + +#[ test ] +fn test_rebase_without_old_path() +{ + let file_path = "/home/user/documents/file.txt"; + let new_path = "/mnt/storage"; + let rebased_path = the_module::path::rebase( &file_path, &new_path, None ).unwrap(); + assert_eq! + ( + rebased_path, + PathBuf::from( "/mnt/storage/home/user/documents/file.txt" ) + ); +} + +#[ test ] +fn test_rebase_with_old_path() +{ + let file_path = "/home/user/documents/file.txt"; + let new_path = "/mnt/storage"; + let old_path = "/home/user"; + let rebased_path = the_module::path::rebase( &file_path, &new_path, Some( &old_path ) ).unwrap(); + assert_eq! + ( + rebased_path, + PathBuf::from( "/mnt/storage/documents/file.txt" ) + ); +} + +#[ test ] +fn test_rebase_invalid_old_path() +{ + let file_path = "/home/user/documents/file.txt"; + let new_path = "/mnt/storage"; + let old_path = "/tmp"; + let rebased_path = the_module::path::rebase( &file_path, &new_path, Some( &old_path ) ).unwrap(); + assert_eq! + ( + rebased_path, + PathBuf::from( "/mnt/storage/home/user/documents/file.txt" ) + ); +} + +#[ test ] +fn test_rebase_non_ascii_paths() +{ + let file_path = "/home/пользователь/documents/файл.txt"; // Non-ASCII file path + let new_path = "/mnt/存储"; // Non-ASCII new base path + let rebased_path = the_module::path::rebase( &file_path, &new_path, None ).unwrap(); + assert_eq! + ( + rebased_path, + PathBuf::from( "/mnt/存储/home/пользователь/documents/файл.txt" ) + ); +} \ No newline at end of file From 20a6e1ddadb4ed516bc6798613ffd64c3c55107e Mon Sep 17 00:00:00 2001 From: SupperZum Date: Sat, 13 Apr 2024 13:25:58 +0300 Subject: [PATCH 3/3] add path_relative --- module/core/proper_path_tools/src/path.rs | 136 ++++++ .../core/proper_path_tools/tests/inc/mod.rs | 1 + .../tests/inc/path_relative.rs | 403 ++++++++++++++++++ 3 files changed, 540 insertions(+) create mode 100644 module/core/proper_path_tools/tests/inc/path_relative.rs diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index b74076426f..f76bb41aa7 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -545,10 +545,146 @@ pub( crate ) mod private Some( normalize( rebased_path ) ) } + + /// Computes the relative path from one path to another. + /// + /// This function takes two paths and returns a relative path from the `from` path to the `to` path. + /// If the paths have different roots, the function returns the `to` path. + /// + /// # Arguments + /// + /// * `from` - The starting path. + /// * `to` - The target path. + /// + /// # Returns + /// + /// A `std::path::PathBuf` representing the relative path from `from` to `to`. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let from = "/a/b"; + /// let to = "/a/c/d"; + /// let relative_path = proper_path_tools::path::path_relative( from, to ); + /// assert_eq!( relative_path, PathBuf::from( "../c/d" ) ); + /// ``` + pub fn path_relative< T : AsRef< std::path::Path > >( from : T, to : T ) -> std::path::PathBuf + { + use std::path::PathBuf; + + let mut from = from.as_ref().to_string_lossy().to_string(); + let mut to = to.as_ref().to_string_lossy().to_string(); + + from = from.replace( ':', "" ); + to = to.replace( ':', "" ); + + + if from == "./" + { + from.push_str( &to ); + return PathBuf::from( from ) + } + + if from == "." + { + return PathBuf::from( to ) + } + + path_remove_double_dots( &mut from ); + path_remove_double_dots( &mut to ); + path_remove_dots( &mut from ); + path_remove_dots( &mut to ); + + let mut from_parts: Vec< &str > = from.split( '/' ).collect(); + let mut to_parts: Vec< &str > = to.split( '/' ).collect(); + + + if from_parts.len() == 1 && from_parts[ 0 ].is_empty() + { + from_parts.pop(); + } + + if to_parts.len() == 1 && to_parts[ 0 ].is_empty() + { + to_parts.pop(); + } + + let mut common_prefix = 0; + for ( idx, ( f, t ) ) in from_parts.iter().zip( to_parts.iter() ).enumerate() + { + if f != t + { + break; + } + common_prefix = idx + 1; + } + + let mut result = Vec::new(); + + // Add ".." for each directory not in common + for i in common_prefix..from_parts.len() + { + if from_parts[ common_prefix ].is_empty() || + ( + i == from_parts.len() - 1 + && from_parts[ i ].is_empty() + && !to_parts.last().unwrap_or( &"" ).is_empty() + ) + { + continue; + } + + result.push( ".." ); + } + + // Add the remaining directories from 'to' + for part in to_parts.iter().skip( common_prefix ) + { + result.push( *part ); + } + + // Join the parts into a string + let mut relative_path = result.join( "/" ); + + + + // If the relative path is empty or the 'to' path is the same as the 'from' path, + // set the relative path to "." + if relative_path.is_empty() || from == to + { + relative_path = ".".to_string(); + } + + + if to.ends_with( '/' ) && !relative_path.ends_with( '/' ) && to != "/" + { + relative_path.push( '/' ); + } + + + if from.ends_with( '/' ) && to.starts_with( '/' ) && relative_path.starts_with( ".." ) && relative_path != ".." + { + relative_path.replace_range( ..2 , "." ); + } + + if from.ends_with( '/' ) && to.starts_with( '/' ) && relative_path == ".." + { + relative_path = "./..".to_string(); + } + + PathBuf::from( relative_path ) + } + + + + } crate::mod_interface! { + protected use path_relative; protected use rebase; protected use path_common; protected use is_glob; diff --git a/module/core/proper_path_tools/tests/inc/mod.rs b/module/core/proper_path_tools/tests/inc/mod.rs index 0eea9b62b6..051ddf9c27 100644 --- a/module/core/proper_path_tools/tests/inc/mod.rs +++ b/module/core/proper_path_tools/tests/inc/mod.rs @@ -6,6 +6,7 @@ mod path_is_glob; mod absolute_path; mod path_common; mod rebase_path; +mod path_relative; #[ cfg( feature = "path_unique_folder_name" ) ] mod path_unique_folder_name; diff --git a/module/core/proper_path_tools/tests/inc/path_relative.rs b/module/core/proper_path_tools/tests/inc/path_relative.rs new file mode 100644 index 0000000000..7c9f6bfbed --- /dev/null +++ b/module/core/proper_path_tools/tests/inc/path_relative.rs @@ -0,0 +1,403 @@ +#[ allow( unused_imports ) ] +use super::*; +use std::path::PathBuf; + + +// absolute path relative + +#[ test ] +fn test_absolute_a_minus_b() +{ + let from = "/a"; + let to = "/b"; + let expected = "../b"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( PathBuf::from( expected ) ) ); +} + +#[ test ] +fn test_absolute_root_minus_b() +{ + let from = "/"; + let to = "/b"; + let expected = "b"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_same_path() +{ + let from = "/aa/bb/cc"; + let to = "/aa/bb/cc"; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_same_path_with_trail() +{ + let from = "/aa/bb/cc"; + let to = "/aa/bb/cc/"; + let expected = "./"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_two_trailed_absolute_paths() +{ + let from = "/a/b/"; + let to = "/a/b/"; + let expected = "./"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_two_absolute_paths_with_trail() +{ + let from = "/a/b"; + let to = "/a/b/"; + let expected = "./"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_two_absolute_paths() +{ + let from = "/a/b/"; + let to = "/a/b"; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_same_path_trail_to_not() +{ + let from = "/aa/bb/cc/"; + let to = "/aa/bb/cc"; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_a_to_double_slash_b() +{ + let from = "/a"; + let to = "//b"; + let expected = "..//b"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + + +#[ test ] +fn test_absolute_relative_to_nested() +{ + let from = "/foo/bar/baz/asdf/quux"; + let to = "/foo/bar/baz/asdf/quux/new1"; + let expected = "new1"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_out_of_relative_dir() +{ + let from = "/abc"; + let to = "/a/b/z"; + let expected = "../a/b/z"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_relative_root() +{ + let from = "/"; + let to = "/a/b/z"; + let expected = "a/b/z"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + + +#[ test ] +fn test_long_not_direct() +{ + let from = "/a/b/xx/yy/zz"; + let to = "/a/b/files/x/y/z.txt"; + let expected = "../../../files/x/y/z.txt"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_relative_to_parent_directory() +{ + let from = "/aa/bb/cc"; + let to = "/aa/bb"; + let expected = ".."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_relative_to_parent_directory_file_trailed() +{ + let from = "/aa/bb/cc"; + let to = "/aa/bb/"; + let expected = "../"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_relative_root_to_root() +{ + let from = "/"; + let to = "/"; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_windows_disks() +{ + let from = "d:/"; + let to = "c:/x/y"; + let expected = "../c/x/y"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + + +#[ test ] +fn test_absolute_relative_to_parent_directory_both_trailed() +{ + let from = "/aa/bb/cc/"; + let to = "/aa/bb/"; + let expected = "./../"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + + +#[ test ] +fn test_absolute_a_with_trail_to_double_slash_b_with_trail() +{ + let from = "/a/"; + let to = "//b/"; + let expected = "./..//b/"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_4_down() +{ + let from = "/aa//bb/cc/"; + let to = "//xx/yy/zz/"; + let expected = "./../../../..//xx/yy/zz/"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_same_length_both_trailed() +{ + let from = "/aa//bb/cc/"; + let to = "//xx/yy/zz/"; + let expected = "./../../../..//xx/yy/zz/"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_absolute_relative_to_parent_directory_base_trailed() +{ + let from = "/aa/bb/cc/"; + let to = "/aa/bb"; + let expected = "./.."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + + + + + +// relative_path_relative + +#[ test ] +fn test_relative_dot_to_dot() +{ + let from = "."; + let to = "."; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_to_b() +{ + let from = "a"; + let to = "b"; + let expected = "../b"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_b_to_b_c() +{ + let from = "a/b"; + let to = "b/c"; + let expected = "../../b/c"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_b_to_a_b_c() +{ + let from = "a/b"; + let to = "a/b/c"; + let expected = "c"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_b_c_to_a_b() +{ + let from = "a/b/c"; + let to = "a/b"; + let expected = ".."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_b_c_d_to_a_b_d_c() +{ + let from = "a/b/c/d"; + let to = "a/b/d/c"; + let expected = "../../d/c"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_to_dot_dot_a() +{ + let from = "a"; + let to = "../a"; + let expected = "../../a"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_slash_slash_b_to_a_slash_slash_c() +{ + let from = "a//b"; + let to = "a//c"; + let expected = "../c"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_dot_slash_b_to_a_dot_slash_c() +{ + let from = "a/./b"; + let to = "a/./c"; + let expected = "../c"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_dot_dot_slash_b_to_b() +{ + let from = "a/../b"; + let to = "b"; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_b_to_b_dot_dot_slash_b() +{ + let from = "b"; + let to = "b/../b"; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_to_dot_dot() +{ + let from = "."; + let to = ".."; + let expected = ".."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_to_dot_dot_dot() +{ + let from = "."; + let to = "../.."; + let expected = "../.."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_dot_to_dot_dot() +{ + let from = ".."; + let to = "../.."; + let expected = ".."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_dot_to_dot_dot_dot() +{ + let from = ".."; + let to = ".."; + let expected = "."; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_dot_a_b_to_dot_dot_c_d() +{ + let from = "../a/b"; + let to = "../c/d"; + let expected = "../../c/d"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_to_b() +{ + let from = "."; + let to = "b"; + let expected = "b"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_slash_to_b() +{ + let from = "./"; + let to = "b"; + let expected = "./b"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_to_b_slash() +{ + let from = "."; + let to = "b/"; + let expected = "b/"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_dot_slash_to_b_slash() +{ + let from = "./"; + let to = "b/"; + let expected = "./b/"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} + +#[ test ] +fn test_relative_a_dot_dot_to_b_dot_dot() +{ + let from = "a/../b/.."; + let to = "b"; + let expected = "b"; + assert_eq!( the_module::path::path_relative( from, to ), PathBuf::from( expected ) ); +} \ No newline at end of file