pub mod catalog_io;
pub mod content_block_io;
pub mod custom_attribute_io;
pub mod email_template_io;
pub mod frontmatter;
pub mod tag_io;
use crate::error::{Error, Result};
use std::path::{Path, PathBuf};
pub(crate) fn try_read_resource_dir(
root: &Path,
kind_label: &str,
) -> Result<Option<std::fs::ReadDir>> {
match std::fs::read_dir(root) {
Ok(rd) => Ok(Some(rd)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(e) => {
if root.is_file() {
return Err(Error::InvalidFormat {
path: root.to_path_buf(),
message: format!("expected a directory for the {kind_label} root"),
});
}
Err(e.into())
}
}
}
pub(crate) fn validate_resource_name(kind_label: &str, name: &str) -> Result<()> {
let bad = name.is_empty()
|| name == "."
|| name == ".."
|| name.contains('/')
|| name.contains('\\')
|| name.contains('\0');
if bad {
return Err(Error::InvalidFormat {
path: PathBuf::from(name),
message: format!("{kind_label} name '{name}' contains invalid characters"),
});
}
Ok(())
}
pub(crate) fn write_atomic(path: &Path, contents: &[u8]) -> Result<()> {
use std::io::Write;
let parent = path.parent().unwrap_or_else(|| Path::new("."));
std::fs::create_dir_all(parent)?;
let file_name = path.file_name().ok_or_else(|| Error::InvalidFormat {
path: path.to_path_buf(),
message: "atomic write target has no file name".into(),
})?;
let mut tmp_name = file_name.to_os_string();
tmp_name.push(format!(".{}.tmp", std::process::id()));
let tmp_path = parent.join(tmp_name);
let mut file = std::fs::File::create(&tmp_path)?;
if let Err(e) = file.write_all(contents).and_then(|_| file.sync_all()) {
drop(file);
let _ = std::fs::remove_file(&tmp_path);
return Err(e.into());
}
drop(file);
if let Err(e) = std::fs::rename(&tmp_path, path) {
let _ = std::fs::remove_file(&tmp_path);
return Err(e.into());
}
Ok(())
}