From 072a098e9c121773bb6567e9c96a3831184e73c9 Mon Sep 17 00:00:00 2001 From: Kingsword Date: Sun, 27 Apr 2025 11:31:16 +0800 Subject: [PATCH 1/2] fix path joining behavior where path.join('', 'a') incorrectly returns "/a" instead of "a" (#13313) --- .changes/fix-path-join-error.md | 6 +++ crates/tauri/src/path/plugin.rs | 67 ++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 .changes/fix-path-join-error.md diff --git a/.changes/fix-path-join-error.md b/.changes/fix-path-join-error.md new file mode 100644 index 000000000000..6360f75517c0 --- /dev/null +++ b/.changes/fix-path-join-error.md @@ -0,0 +1,6 @@ +--- +"tauri": "minor:bug" +"@tauri-apps/api": "minor:bug" +--- + +Fixed path joining behavior where `path.join('', 'a')` incorrectly returns "/a" instead of "a". diff --git a/crates/tauri/src/path/plugin.rs b/crates/tauri/src/path/plugin.rs index 0cfdba2c52cb..bb801b3fdf86 100644 --- a/crates/tauri/src/path/plugin.rs +++ b/crates/tauri/src/path/plugin.rs @@ -141,7 +141,9 @@ pub fn join(mut paths: Vec) -> String { // Doing this to ensure that the vector elements are separated in // the resulting string so path.components() can work correctly when called // in `normalize_path_no_absolute()` later on. - if !p.ends_with('/') && !p.ends_with('\\') { + // Issue #13313 - path.join('', 'a') returns "/a" instead of "a" + // https://github.com/tauri-apps/tauri/issues/13313 + if !p.is_empty() && !p.ends_with('/') && !p.ends_with('\\') { p.push(MAIN_SEPARATOR); } p.to_string() @@ -287,4 +289,67 @@ mod tests { "some-json-file.json.html" ); } + + #[test] + fn join() { + #[cfg(not(target_os = "windows"))] + { + let paths = vec!["".to_string()]; + assert_eq!(super::join(paths), "."); + + let paths = vec!["".to_string(), "".to_string()]; + assert_eq!(super::join(paths), "."); + + let paths = vec!["a".to_string()]; + assert_eq!(super::join(paths), "a"); + + let paths = vec!["".to_string(), "a".to_string()]; + assert_eq!(super::join(paths), "a"); + + let paths = vec!["a".to_string(), "b".to_string()]; + assert_eq!(super::join(paths), "a/b"); + + let paths = vec!["a".to_string(), "".to_string(), "b".to_string()]; + assert_eq!(super::join(paths), "a/b"); + + let paths = vec!["a".to_string(), "/b".to_string(), "c".to_string()]; + assert_eq!(super::join(paths), "a/b/c"); + + let paths = vec!["a".to_string(), "b/c".to_string(), "d".to_string()]; + assert_eq!(super::join(paths), "a/b/c/d"); + + let paths = vec!["a/".to_string(), "b".to_string()]; + assert_eq!(super::join(paths), "a/b"); + } + + #[cfg(target_os = "windows")] + { + let paths = vec!["".to_string()]; + assert_eq!(super::join(paths), "."); + + let paths = vec!["".to_string(), "".to_string()]; + assert_eq!(super::join(paths), "."); + + let paths = vec!["a".to_string()]; + assert_eq!(super::join(paths), "a"); + + let paths = vec!["".to_string(), "a".to_string()]; + assert_eq!(super::join(paths), "a"); + + let paths = vec!["a".to_string(), "b".to_string()]; + assert_eq!(super::join(paths), "a\\b"); + + let paths = vec!["a".to_string(), "".to_string(), "b".to_string()]; + assert_eq!(super::join(paths), "a\\b"); + + let paths = vec!["a".to_string(), "\\b".to_string(), "c".to_string()]; + assert_eq!(super::join(paths), "a\\b\\c"); + + let paths = vec!["a".to_string(), "b\\c".to_string(), "d".to_string()]; + assert_eq!(super::join(paths), "a\\b\\c\\d"); + + let paths = vec!["a\\".to_string(), "b".to_string()]; + assert_eq!(super::join(paths), "a\\b"); + } + } } From a9fb1bc9a2ba7b396f48e34b8d9ece76db514ee5 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 27 Apr 2025 14:10:54 +0800 Subject: [PATCH 2/2] Clean up --- crates/tauri/src/path/plugin.rs | 86 +++++++++------------------------ 1 file changed, 22 insertions(+), 64 deletions(-) diff --git a/crates/tauri/src/path/plugin.rs b/crates/tauri/src/path/plugin.rs index bb801b3fdf86..4368531b1da7 100644 --- a/crates/tauri/src/path/plugin.rs +++ b/crates/tauri/src/path/plugin.rs @@ -132,21 +132,19 @@ pub fn normalize(path: String) -> String { } #[command(root = "crate")] -pub fn join(mut paths: Vec) -> String { +pub fn join(paths: Vec) -> String { let path = PathBuf::from( paths - .iter_mut() - .map(|p| { - // Add a `MAIN_SEPARATOR` if it doesn't already have one. + .into_iter() + .map(|mut p| { + // Add a `MAIN_SEPARATOR` if it doesn't already have one and is not an empty string. // Doing this to ensure that the vector elements are separated in // the resulting string so path.components() can work correctly when called // in `normalize_path_no_absolute()` later on. - // Issue #13313 - path.join('', 'a') returns "/a" instead of "a" - // https://github.com/tauri-apps/tauri/issues/13313 if !p.is_empty() && !p.ends_with('/') && !p.ends_with('\\') { p.push(MAIN_SEPARATOR); } - p.to_string() + p }) .collect::(), ); @@ -292,64 +290,24 @@ mod tests { #[test] fn join() { - #[cfg(not(target_os = "windows"))] - { - let paths = vec!["".to_string()]; - assert_eq!(super::join(paths), "."); - - let paths = vec!["".to_string(), "".to_string()]; - assert_eq!(super::join(paths), "."); - - let paths = vec!["a".to_string()]; - assert_eq!(super::join(paths), "a"); - - let paths = vec!["".to_string(), "a".to_string()]; - assert_eq!(super::join(paths), "a"); - - let paths = vec!["a".to_string(), "b".to_string()]; - assert_eq!(super::join(paths), "a/b"); - - let paths = vec!["a".to_string(), "".to_string(), "b".to_string()]; - assert_eq!(super::join(paths), "a/b"); - - let paths = vec!["a".to_string(), "/b".to_string(), "c".to_string()]; - assert_eq!(super::join(paths), "a/b/c"); - - let paths = vec!["a".to_string(), "b/c".to_string(), "d".to_string()]; - assert_eq!(super::join(paths), "a/b/c/d"); - - let paths = vec!["a/".to_string(), "b".to_string()]; - assert_eq!(super::join(paths), "a/b"); + fn check(paths: Vec<&str>, expected_unix: &str, expected_windows: &str) { + let expected = if cfg!(windows) { + expected_windows + } else { + expected_unix + }; + let paths = paths.into_iter().map(String::from).collect(); + assert_eq!(super::join(paths), expected); } - #[cfg(target_os = "windows")] - { - let paths = vec!["".to_string()]; - assert_eq!(super::join(paths), "."); - - let paths = vec!["".to_string(), "".to_string()]; - assert_eq!(super::join(paths), "."); - - let paths = vec!["a".to_string()]; - assert_eq!(super::join(paths), "a"); - - let paths = vec!["".to_string(), "a".to_string()]; - assert_eq!(super::join(paths), "a"); - - let paths = vec!["a".to_string(), "b".to_string()]; - assert_eq!(super::join(paths), "a\\b"); - - let paths = vec!["a".to_string(), "".to_string(), "b".to_string()]; - assert_eq!(super::join(paths), "a\\b"); - - let paths = vec!["a".to_string(), "\\b".to_string(), "c".to_string()]; - assert_eq!(super::join(paths), "a\\b\\c"); - - let paths = vec!["a".to_string(), "b\\c".to_string(), "d".to_string()]; - assert_eq!(super::join(paths), "a\\b\\c\\d"); - - let paths = vec!["a\\".to_string(), "b".to_string()]; - assert_eq!(super::join(paths), "a\\b"); - } + check(vec![""], ".", "."); + check(vec!["", ""], ".", "."); + check(vec!["a"], "a", "a"); + check(vec!["", "a"], "a", "a"); + check(vec!["a", "b"], "a/b", r"a\b"); + check(vec!["a", "", "b"], "a/b", r"a\b"); + check(vec!["a", "/b", "c"], "a/b/c", r"a\b\c"); + check(vec!["a", "b/c", "d"], "a/b/c/d", r"a\b\c\d"); + check(vec!["a/", "b"], "a/b", r"a\b"); } }