use crate::FrenError;
use std::path::Path;
pub fn rename(from: &Path, to: &Path) -> Result<(), FrenError> {
#[cfg(target_os = "linux")]
{
rename_linux(from, to)
}
#[cfg(not(target_os = "linux"))]
{
rename_fallback(from, to)
}
}
#[cfg(target_os = "linux")]
fn rename_linux(from: &Path, to: &Path) -> Result<(), FrenError> {
use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;
const RENAME_NOREPLACE: libc::c_uint = 1;
const AT_FDCWD: libc::c_int = -100;
let from_c = CString::new(from.as_os_str().as_bytes()).map_err(|_| {
FrenError::InvalidInput(format!("path contains null byte: {}", from.display()))
})?;
let to_c = CString::new(to.as_os_str().as_bytes()).map_err(|_| {
FrenError::InvalidInput(format!("path contains null byte: {}", to.display()))
})?;
let ret = unsafe {
libc::syscall(
libc::SYS_renameat2,
AT_FDCWD,
from_c.as_ptr(),
AT_FDCWD,
to_c.as_ptr(),
RENAME_NOREPLACE,
)
};
if ret == 0 {
return Ok(());
}
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ENOSYS) {
return rename_fallback(from, to);
}
if err.raw_os_error() == Some(libc::EEXIST) {
return Err(FrenError::TargetExists(to.to_path_buf()));
}
Err(FrenError::Io {
path: from.to_path_buf(),
source: err,
})
}
fn rename_fallback(from: &Path, to: &Path) -> Result<(), FrenError> {
if to.exists() {
return Err(FrenError::TargetExists(to.to_path_buf()));
}
std::fs::rename(from, to).map_err(|source| FrenError::Io {
path: from.to_path_buf(),
source,
})
}