Skip to content

Commit

Permalink
file: Add windows support for non-overwrite
Browse files Browse the repository at this point in the history
  • Loading branch information
cdown committed Nov 4, 2024
1 parent 05ff1b0 commit bb6899f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ rayon = "1.10.0"
libc = "0.2.161"

[target.'cfg(target_family = "windows")'.dependencies]
winapi = { version = "0.3.9", features = ["winerror"] }
winapi = { version = "0.3.9", features = ["errhandlingapi", "winbase", "winerror"] }

[dev-dependencies]
assert_cmd = "2.0.16"
Expand Down
35 changes: 34 additions & 1 deletion src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn rename(from: &Path, to: &Path, overwrite: bool) -> io::Result<()> {
}
}

#[cfg(not(target_os = "linux"))]
#[cfg(not(any(target_os = "linux", target_family = "windows")))]
fn rename(from: &Path, to: &Path, overwrite: bool) -> io::Result<()> {
use crate::util::die;
if !overwrite {
Expand All @@ -47,6 +47,39 @@ fn rename(from: &Path, to: &Path, overwrite: bool) -> io::Result<()> {
fs::rename(from, to)
}

#[cfg(target_family = "windows")]
fn rename(from: &Path, to: &Path, overwrite: bool) -> io::Result<()> {
use std::os::windows::ffi::OsStrExt;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::winbase::{MoveFileExW, MOVEFILE_COPY_ALLOWED, MOVEFILE_REPLACE_EXISTING};

let from_wide: Vec<u16> = from
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect();
let to_wide: Vec<u16> = to
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect();

let mut flags = MOVEFILE_COPY_ALLOWED;
if overwrite {
flags |= MOVEFILE_REPLACE_EXISTING;
}

// SAFETY: Simple FFI
let ret = unsafe { MoveFileExW(from_wide.as_ptr(), to_wide.as_ptr(), flags) };

if ret == 0 {
let err = unsafe { GetLastError() };
Err(io::Error::from_raw_os_error(err as i32))
} else {
Ok(())
}
}

pub fn copy_creating_dirs(from: &Path, to_raw: impl Into<PathBuf>, overwrite: bool) -> Result<()> {
let to = to_raw.into();
let to_parent = to.parent().context("refusing to copy to filesystem root")?;
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::Result;

#[cfg(target_os = "linux")] // Others must use --overwrite
#[cfg(any(target_os = "linux", target_family = "windows"))] // Others must use --overwrite
#[test]
fn test_rename_no_overwrite() -> Result<()> {
// Set up test environment
Expand Down

0 comments on commit bb6899f

Please sign in to comment.