use std::fs;
use std::io::ErrorKind;
use std::path::Path;
use super::error::StoreError;
#[derive(Clone, Copy, Debug)]
pub enum ConstructMode {
CreateOrOpenFenced,
CreateOrOpenUnfenced,
OpenExistingUnfenced,
}
pub fn ensure_directory(path: &Path, mode: ConstructMode) -> Result<(), StoreError> {
let existed = match fs::metadata(path) {
Ok(metadata) if metadata.is_dir() => true,
Ok(_metadata) => {
return Err(StoreError::NotADirectory {
path: path.to_path_buf(),
});
}
Err(error) if error.kind() == ErrorKind::NotFound => false,
Err(error) => return Err(directory_error(error, path)),
};
match mode {
ConstructMode::OpenExistingUnfenced => {
if !existed {
return Err(StoreError::DirectoryNotFound);
}
Ok(())
}
ConstructMode::CreateOrOpenUnfenced => {
if !existed {
create_dir_single(path, Journalled::No)?;
}
Ok(())
}
ConstructMode::CreateOrOpenFenced => {
if !existed {
create_dir_single(path, Journalled::Yes)?;
}
fence_store_root_entry(path)
}
}
}
#[derive(Clone, Copy)]
enum Journalled {
Yes,
No,
}
fn create_dir_single(path: &Path, journalled: Journalled) -> Result<(), StoreError> {
#[cfg(test)]
let reservation = match journalled {
Journalled::Yes => Some(crate::fence::journal::reserve_create_dir(path)),
Journalled::No => None,
};
#[cfg(not(test))]
let _ = journalled;
let result = fs::create_dir(path);
match result {
Ok(()) => {
#[cfg(test)]
if let Some(reservation) = reservation {
reservation.commit();
}
Ok(())
}
Err(error) if error.kind() == ErrorKind::AlreadyExists => {
#[cfg(test)]
if let Some(reservation) = reservation {
reservation.cancel();
}
Ok(())
}
Err(error) => {
#[cfg(test)]
if let Some(reservation) = reservation {
reservation.cancel();
}
Err(directory_error(error, path))
}
}
}
fn fence_store_root_entry(path: &Path) -> Result<(), StoreError> {
let Some(parent) = path.parent() else {
return Ok(());
};
crate::fence::sync_dir_entry(parent).map_err(StoreError::Io)
}
pub fn directory_error(error: std::io::Error, path: &Path) -> StoreError {
match error.kind() {
ErrorKind::NotFound => StoreError::DirectoryNotFound,
ErrorKind::NotADirectory | ErrorKind::AlreadyExists => StoreError::NotADirectory {
path: path.to_path_buf(),
},
_ => StoreError::Io(error),
}
}