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.exists() {
return Ok(());
}
fs::create_dir(path)?;
apply_dir_hints(path)?;
Ok(())
}
pub fn create_dir_all(path: &Path) -> Result<(), XzipError> {
if path.exists() {
return Ok(());
}
let mut to_create = Vec::new();
let mut cursor = Some(path);
while let Some(dir) = cursor {
if dir.exists() {
break;
}
to_create.push(dir.to_path_buf());
cursor = dir.parent();
}
for dir in to_create.into_iter().rev() {
fs::create_dir(&dir)?;
apply_dir_hints(&dir)?;
}
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) -> Result<(), XzipError> {
use std::os::windows::ffi::OsStrExt;
const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: u32 = 0x0000_2000;
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 Err(XzipError::Io(std::io::Error::last_os_error()));
}
let updated = attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
if unsafe {
windows_sys::Win32::Storage::FileSystem::SetFileAttributesW(wide.as_ptr(), updated)
} == 0
{
return Err(XzipError::Io(std::io::Error::last_os_error()));
}
Ok(())
}
#[cfg(not(windows))]
fn apply_dir_hints(_path: &Path) -> Result<(), XzipError> {
Ok(())
}