use std::fs::{self, File, OpenOptions};
use std::path::Path;
use crate::error::XzipError;
pub fn open_zip_file(path: &Path) -> Result<File, XzipError> {
let mut options = OpenOptions::new();
options.read(true);
#[cfg(windows)]
{
use std::os::windows::fs::OpenOptionsExt;
const FILE_SHARE_READ: u32 = 0x0000_0001;
const FILE_SHARE_DELETE: u32 = 0x0000_0004;
const FILE_FLAG_SEQUENTIAL_SCAN: u32 = 0x0800_0000;
options.share_mode(FILE_SHARE_READ | FILE_SHARE_DELETE);
options.custom_flags(FILE_FLAG_SEQUENTIAL_SCAN);
}
options.open(path).map_err(XzipError::from)
}
pub fn create_dir(path: &Path) -> Result<(), XzipError> {
if path.as_os_str().is_empty() {
return Err(XzipError::InvalidInput(path.to_path_buf()));
}
if path.is_dir() {
return Ok(());
}
fs::create_dir(path)?;
apply_dir_hints(path);
Ok(())
}
pub fn create_dir_all(path: &Path) -> Result<(), XzipError> {
if path.as_os_str().is_empty() {
return Err(XzipError::InvalidInput(path.to_path_buf()));
}
if path.is_dir() {
return Ok(());
}
fs::create_dir_all(path)?;
apply_dir_hints(path);
Ok(())
}
pub fn create_output_file(path: &Path) -> Result<File, XzipError> {
if let Some(parent) = path.parent() {
create_dir_all(parent)?;
}
File::create(path).map_err(XzipError::from)
}
#[cfg(windows)]
fn apply_dir_hints(path: &Path) {
use std::os::windows::ffi::OsStrExt;
const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: u32 = 0x0000_2000;
let Ok(path) = path.canonicalize() else {
return;
};
let wide: Vec<u16> = path
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect();
let attrs =
unsafe { windows_sys::Win32::Storage::FileSystem::GetFileAttributesW(wide.as_ptr()) };
if attrs == u32::MAX {
return;
}
let updated = attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
let _ = unsafe {
windows_sys::Win32::Storage::FileSystem::SetFileAttributesW(wide.as_ptr(), updated)
};
}
#[cfg(not(windows))]
fn apply_dir_hints(_path: &Path) {}
#[cfg(test)]
mod tests {
use std::fs;
use tempfile::tempdir;
use super::{create_dir, create_dir_all};
#[test]
fn create_dir_all_makes_nested_tree() {
let temp = tempdir().expect("temp dir");
let nested = temp.path().join("a/b/c");
create_dir_all(&nested).expect("create nested dir");
assert!(nested.is_dir());
}
#[test]
fn create_dir_skips_existing_directory() {
let temp = tempdir().expect("temp dir");
let dir = temp.path().join("exists");
fs::create_dir(&dir).expect("create dir");
create_dir(&dir).expect("existing dir should be ok");
}
}