fix: ensure NFC normalization is applied consistently
- api/files.rs: NFC-normalize filename before sending to server in upload_file(). On macOS, local filenames may be NFD-encoded, which would cause the server to store them as NFD instead of NFC. - commands/download.rs: replace direct to_lowercase() subfolder comparison with find_subfolder_by_name() helper, which already applies NFC normalization on both sides. - commands/cp.rs, mv.rs: apply nfc() to s_basename (source path component from user input) for consistency with d_basename, so the no-op identity check and find_*() calls use normalized strings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+4
-2
@@ -1,5 +1,6 @@
|
||||
use crate::connection::MDRSConnection;
|
||||
pub use crate::models::file::File;
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct FileListResponse {
|
||||
@@ -40,11 +41,12 @@ impl MDRSConnection {
|
||||
file_path: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use reqwest::multipart;
|
||||
let file_name = std::path::Path::new(file_path)
|
||||
let file_name: String = std::path::Path::new(file_path)
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
.nfc()
|
||||
.collect();
|
||||
let file_bytes = tokio::fs::read(file_path).await?;
|
||||
let part = multipart::Part::bytes(file_bytes).file_name(file_name.clone());
|
||||
let form = multipart::Form::new()
|
||||
|
||||
+2
-1
@@ -25,7 +25,8 @@ pub async fn cp(
|
||||
let lab_id = lab.id;
|
||||
|
||||
// Split source path into parent directory and target name
|
||||
let (s_dirname, s_basename) = split_path(&s_path);
|
||||
let (s_dirname, s_basename_raw) = split_path(&s_path);
|
||||
let s_basename = nfc(&s_basename_raw);
|
||||
|
||||
// If dest ends with '/', treat it as a directory and preserve src basename
|
||||
let (d_dirname, d_basename_raw) = if dest_ends_with_slash {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::commands::shared::{
|
||||
create_authenticated_conn, find_file_by_name, find_folder, find_lab_in_cache, load_cache_with_token_refresh,
|
||||
parse_remote_path,
|
||||
create_authenticated_conn, find_file_by_name, find_folder, find_lab_in_cache,
|
||||
find_subfolder_by_name, load_cache_with_token_refresh, parse_remote_path,
|
||||
};
|
||||
use crate::connection::MDRSConnection;
|
||||
use futures::stream::{FuturesUnordered, StreamExt};
|
||||
@@ -66,10 +66,7 @@ pub async fn download(
|
||||
}
|
||||
|
||||
// Case 2: basename matches a sub-folder.
|
||||
let subfolder = parent_folder
|
||||
.sub_folders
|
||||
.iter()
|
||||
.find(|f| f.name.to_lowercase() == basename.to_lowercase());
|
||||
let subfolder = find_subfolder_by_name(&parent_folder.sub_folders, &basename);
|
||||
if let Some(sub) = subfolder {
|
||||
if !recursive {
|
||||
return Err(format!("Cannot download `{}`: Is a folder.", r_path_clean).into());
|
||||
|
||||
+2
-1
@@ -21,7 +21,8 @@ pub async fn mv(src_path: &str, dest_path: &str) -> Result<(), Box<dyn std::erro
|
||||
let lab_id = lab.id;
|
||||
|
||||
// Split source path into parent directory and target name
|
||||
let (s_dirname, s_basename) = split_path(&s_path);
|
||||
let (s_dirname, s_basename_raw) = split_path(&s_path);
|
||||
let s_basename = nfc(&s_basename_raw);
|
||||
|
||||
// If dest ends with '/', treat it as a directory and preserve src basename
|
||||
let (d_dirname, d_basename_raw) = if dest_ends_with_slash {
|
||||
|
||||
Reference in New Issue
Block a user