diff --git a/src/tools/session_send.rs b/src/tools/session_send.rs index 1cfdf8a..e16fbeb 100644 --- a/src/tools/session_send.rs +++ b/src/tools/session_send.rs @@ -180,25 +180,87 @@ fn validate_context(context: &ToolContext) -> anyhow::Result<()> { fn resolve_attachment_path(raw_path: &str) -> PathBuf { let path = Path::new(raw_path); + tracing::debug!( + raw_path = %raw_path, + raw_path_bytes = ?raw_path.as_bytes(), + "resolve_attachment_path: attempting to resolve path" + ); + // 1. 先按原样(UTF-8)尝试 if path.exists() { + tracing::debug!( + raw_path = %raw_path, + "resolve_attachment_path: path exists as-is (UTF-8)" + ); return path.to_path_buf(); } + tracing::debug!( + raw_path = %raw_path, + "resolve_attachment_path: path not found as UTF-8, trying directory scan" + ); + // 2. 提取父目录和文件名,列出目录逐项比对 if let (Some(parent), Some(target_filename)) = (path.parent(), path.file_name()) { let target_str = target_filename.to_string_lossy(); - if let Ok(entries) = std::fs::read_dir(parent) { - for entry in entries.flatten() { - let entry_name = entry.file_name(); - if filename_matches_target(&entry_name, &target_str) { - return entry.path(); + tracing::debug!( + parent = %parent.display(), + target_filename = %target_str, + target_filename_bytes = ?target_filename.as_encoded_bytes(), + "resolve_attachment_path: scanning parent directory" + ); + + match std::fs::read_dir(parent) { + Ok(entries) => { + let mut entry_count = 0; + for entry in entries.flatten() { + entry_count += 1; + let entry_name = entry.file_name(); + let is_match = filename_matches_target(&entry_name, &target_str); + tracing::trace!( + entry_path = %entry.path().display(), + entry_name_lossy = %entry_name.to_string_lossy(), + entry_name_bytes = ?entry_name.as_encoded_bytes(), + is_match = is_match, + "resolve_attachment_path: checking entry" + ); + if is_match { + tracing::debug!( + entry_path = %entry.path().display(), + entry_name_lossy = %entry_name.to_string_lossy(), + "resolve_attachment_path: MATCH FOUND via encoding fallback" + ); + return entry.path(); + } } + tracing::debug!( + parent = %parent.display(), + entry_count = entry_count, + "resolve_attachment_path: directory scan complete, no match" + ); + } + Err(e) => { + tracing::warn!( + parent = %parent.display(), + error = %e, + "resolve_attachment_path: failed to read directory" + ); } } + } else { + tracing::debug!( + raw_path = %raw_path, + has_parent = path.parent().is_some(), + has_filename = path.file_name().is_some(), + "resolve_attachment_path: cannot extract parent or filename" + ); } // 回退失败,返回原路径(让调用方报错) + tracing::warn!( + raw_path = %raw_path, + "resolve_attachment_path: all resolution attempts failed, returning original path" + ); path.to_path_buf() } @@ -210,25 +272,87 @@ fn filename_matches_target(on_disk_name: &std::ffi::OsStr, target: &str) -> bool use std::os::unix::ffi::OsStrExt; let bytes = on_disk_name.as_bytes(); - // 直接 UTF-8 匹配(最快) - if let Ok(decoded) = std::str::from_utf8(bytes) { - return decoded == target; + tracing::trace!( + on_disk_bytes = ?bytes, + on_disk_bytes_hex = %format_bytes_hex(bytes), + target = %target, + "filename_matches_target: comparing" + ); + + // 直接 UTF-8 匹配 + match std::str::from_utf8(bytes) { + Ok(decoded) => { + let matches = decoded == target; + tracing::trace!( + decoded_utf8 = %decoded, + matches = matches, + "filename_matches_target: UTF-8 decode result" + ); + return matches; + } + Err(e) => { + tracing::trace!( + utf8_error = %e, + "filename_matches_target: not valid UTF-8, trying GBK" + ); + } } - // 尝试 GBK/GB18030 解码 + // 尝试 GBK 解码 let (gbk_decoded, _, had_errors) = GBK.decode(bytes); - if !had_errors && gbk_decoded == target { - return true; + if !had_errors { + let matches = gbk_decoded == target; + tracing::debug!( + gbk_decoded = %gbk_decoded, + target = %target, + matches = matches, + gbk_decoded_bytes = ?gbk_decoded.as_bytes(), + target_bytes = ?target.as_bytes(), + "filename_matches_target: GBK decode result" + ); + if matches { + return true; + } + } else { + tracing::debug!( + gbk_decoded_lossy = %gbk_decoded, + had_errors = had_errors, + "filename_matches_target: GBK decode had errors" + ); } - // 回退:lossy 转换比对(处理非 GBK 编码) + // 回退:lossy 转换比对 let lossy = String::from_utf8_lossy(bytes); - lossy == target + let matches = lossy == target; + tracing::debug!( + lossy = %lossy, + target = %target, + matches = matches, + "filename_matches_target: lossy fallback result" + ); + matches +} + +/// 将字节切片格式化为十六进制字符串,用于调试日志。 +#[cfg(unix)] +fn format_bytes_hex(bytes: &[u8]) -> String { + bytes + .iter() + .map(|b| format!("{:02x}", b)) + .collect::>() + .join(" ") } #[cfg(not(unix))] fn filename_matches_target(on_disk_name: &std::ffi::OsStr, target: &str) -> bool { - on_disk_name.to_string_lossy() == target + let matches = on_disk_name.to_string_lossy() == target; + tracing::trace!( + on_disk_lossy = %on_disk_name.to_string_lossy(), + target = %target, + matches = matches, + "filename_matches_target (non-unix): comparing" + ); + matches } fn parse_attachments(value: &serde_json::Value) -> anyhow::Result> {