use std::path::Path;
#[cfg(not(windows))]
use std::fs;
use crate::{error::io_path, Error, Result};
#[cfg(windows)]
use std::{ffi::OsStr, os::windows::ffi::OsStrExt};
#[cfg(windows)]
use windows_sys::Win32::Storage::FileSystem::{
MoveFileExW, MOVEFILE_REPLACE_EXISTING, MOVEFILE_WRITE_THROUGH,
};
pub(crate) fn replace_file(source: &Path, destination: &Path) -> Result<()> {
#[cfg(windows)]
{
let source_wide = wide_null(source.as_os_str()).map_err(|error| io_path(source, error))?;
let destination_wide =
wide_null(destination.as_os_str()).map_err(|error| io_path(destination, error))?;
let result = unsafe {
MoveFileExW(
source_wide.as_ptr(),
destination_wide.as_ptr(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH,
)
};
if result == 0 {
return Err(io_path(destination, std::io::Error::last_os_error()));
}
Ok(())
}
#[cfg(not(windows))]
{
fs::rename(source, destination).map_err(|error| io_path(destination, error))
}
}
pub fn ensure_available_space(path: &Path, required: u64) -> Result<u64> {
let available = fs2::available_space(path).map_err(|error| io_path(path, error))?;
if available < required {
return Err(Error::InsufficientDiskSpace {
path: path.to_path_buf(),
required,
available,
});
}
Ok(available)
}
#[cfg(windows)]
fn wide_null(value: &OsStr) -> std::io::Result<Vec<u16>> {
let mut wide = Vec::new();
for unit in value.encode_wide() {
if unit == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"path contains an embedded null character",
));
}
wide.push(unit);
}
wide.push(0);
Ok(wide)
}
#[cfg(test)]
mod tests {
use tempfile::tempdir;
use super::ensure_available_space;
use crate::Error;
#[test]
fn disk_preflight_rejects_impossible_requirements() {
let dir = tempdir().unwrap();
let error = ensure_available_space(dir.path(), u64::MAX).unwrap_err();
assert!(matches!(error, Error::InsufficientDiskSpace { .. }));
}
}