use crate::FrenError;
use std::path::{Path, PathBuf};
use uuid::Uuid;
pub fn is_case_only_rename(from: &Path, to: &Path) -> bool {
let from_str = from.to_string_lossy();
let to_str = to.to_string_lossy();
from_str != to_str && from_str.eq_ignore_ascii_case(&to_str)
}
pub fn rename_via_temp(from: &Path, to: &Path) -> Result<(), FrenError> {
let temp = make_temp_path(from);
std::fs::rename(from, &temp).map_err(|source| FrenError::Io {
path: from.to_path_buf(),
source,
})?;
std::fs::rename(&temp, to).map_err(|source| FrenError::Io {
path: temp.clone(),
source,
})?;
Ok(())
}
fn make_temp_path(near: &Path) -> PathBuf {
let uuid = Uuid::now_v7();
let parent = near.parent().unwrap_or_else(|| Path::new("."));
let name = near
.file_name()
.map(|n| n.to_string_lossy().into_owned())
.unwrap_or_default();
parent.join(format!(".fren_tmp_{name}_{uuid}"))
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use std::path::Path;
use tempfile::TempDir;
#[test]
fn is_case_only_rename_detects_case_difference() {
assert!(is_case_only_rename(
Path::new("/dir/Foo.txt"),
Path::new("/dir/foo.txt")
));
assert!(is_case_only_rename(
Path::new("/dir/FOO.TXT"),
Path::new("/dir/foo.txt")
));
}
#[test]
fn is_case_only_rename_false_for_different_names() {
assert!(!is_case_only_rename(
Path::new("/dir/foo.txt"),
Path::new("/dir/bar.txt")
));
}
#[test]
fn is_case_only_rename_false_for_identical_paths() {
assert!(!is_case_only_rename(
Path::new("/dir/foo.txt"),
Path::new("/dir/foo.txt")
));
}
#[test]
fn rename_via_temp_moves_file() {
let tmp = TempDir::new().unwrap();
let from = tmp.path().join("Hello.txt");
let to = tmp.path().join("hello.txt");
std::fs::write(&from, b"data").unwrap();
rename_via_temp(&from, &to).unwrap();
assert!(to.exists() || from.exists(), "file must exist after rename");
}
#[test]
fn make_temp_path_is_in_same_dir() {
let path = Path::new("/some/dir/myfile.txt");
let temp = make_temp_path(path);
assert_eq!(temp.parent().unwrap(), Path::new("/some/dir"));
let name = temp.file_name().unwrap().to_string_lossy();
assert!(name.starts_with(".fren_tmp_myfile.txt_"));
}
}