use quote::quote;
use syn::Ident;
use crate::codegen::build_walk_fns;
use crate::codegen::get_serde_derives;
use crate::codegen::get_serde_derives_transparent;
#[expect(clippy::too_many_lines)]
pub fn generate_file_type(name: &Ident) -> proc_macro2::TokenStream {
let serde_derives = get_serde_derives_transparent();
if name == "GenericFile" {
quote! {
#serde_derives
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct #name(std::path::PathBuf);
impl #name {
pub fn new(path: impl Into<std::path::PathBuf>) -> std::io::Result<Self> {
let path_buf = path.into();
let file_name = path_buf.file_name();
if file_name.is_none() {
return Err(std::io::Error::from(std::io::ErrorKind::InvalidFilename));
}
Ok(Self(path_buf))
}
pub fn as_path(&self) -> &std::path::Path {
&self.0
}
pub fn exists(&self) -> bool {
self.0.exists()
}
pub fn as_generic(&self) -> #name {
self.clone()
}
pub fn read(&self) -> std::io::Result<Vec<u8>> {
::tree_type::fs::read(&self.0)
}
pub fn read_to_string(&self) -> std::io::Result<String> {
::tree_type::fs::read_to_string(&self.0)
}
pub fn write<C: AsRef<[u8]>>(&self, contents: C) -> std::io::Result<()> {
if let Some(parent) = self.0.parent() {
if !parent.exists() {
::tree_type::fs::create_dir_all(parent)?;
}
}
::tree_type::fs::write(&self.0, contents)
}
pub fn create(&self) -> std::io::Result<()> {
if let Some(parent) = self.0.parent() {
if !parent.exists() {
::tree_type::fs::create_dir_all(parent)?;
}
}
std::fs::File::create(&self.0)?;
Ok(())
}
pub fn remove(&self) -> std::io::Result<()> {
::tree_type::fs::remove_file(&self.0)
}
pub fn fs_metadata(&self) -> std::io::Result<::tree_type::fs::Metadata> {
::tree_type::fs::metadata(&self.0)
}
pub fn file_name(&self) -> String {
self.0.file_name()
.expect("validated in new")
.to_string_lossy()
.to_string()
}
pub fn parent(&self) -> Option<::tree_type::GenericDir> {
self.0.parent().and_then(|parent_path| {
::tree_type::GenericDir::new(parent_path).ok()
})
}
}
impl AsRef<std::path::Path> for #name {
fn as_ref(&self) -> &std::path::Path {
&self.0
}
}
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.display())
}
}
}
} else {
quote! {
#serde_derives
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct #name {
path: std::path::PathBuf,
}
impl #name {
pub fn new(path: impl Into<std::path::PathBuf>) -> std::io::Result<Self> {
let path_buf = path.into();
let file_name = path_buf.file_name();
if file_name.is_none() {
return Err(std::io::Error::from(std::io::ErrorKind::InvalidFilename));
}
Ok(Self { path: path_buf })
}
pub fn as_path(&self) -> &std::path::Path {
&self.path
}
pub fn exists(&self) -> bool {
self.path.exists()
}
pub fn as_generic(&self) -> ::tree_type::GenericFile {
::tree_type::GenericFile::new(self.path.clone()).expect("Path validation already performed")
}
pub fn read(&self) -> std::io::Result<Vec<u8>> {
::tree_type::fs::read(&self.path)
}
pub fn read_to_string(&self) -> std::io::Result<String> {
::tree_type::fs::read_to_string(&self.path)
}
pub fn write<C: AsRef<[u8]>>(&self, contents: C) -> std::io::Result<()> {
if let Some(parent) = self.path.parent() {
if !parent.exists() {
::tree_type::fs::create_dir_all(parent)?;
}
}
::tree_type::fs::write(&self.path, contents)
}
pub fn create(&self) -> std::io::Result<()> {
if let Some(parent) = self.path.parent() {
if !parent.exists() {
::tree_type::fs::create_dir_all(parent)?;
}
}
std::fs::File::create(&self.path)?;
Ok(())
}
pub fn remove(&self) -> std::io::Result<()> {
::tree_type::fs::remove_file(&self.path)
}
pub fn fs_metadata(&self) -> std::io::Result<::tree_type::fs::Metadata> {
::tree_type::fs::metadata(&self.path)
}
pub fn from_generic(file: ::tree_type::GenericFile) -> Self {
Self { path: file.as_path().to_path_buf() }
}
pub fn file_name(&self) -> String {
self.path.file_name()
.expect("validated in new")
.to_string_lossy()
.to_string()
}
#[cfg(unix)]
pub fn secure(&self) -> std::io::Result<()> {
self.as_generic().secure()
}
pub fn parent(&self) -> Option<::tree_type::GenericDir> {
self.path.parent().and_then(|parent_path| {
::tree_type::GenericDir::new(parent_path).ok()
})
}
}
impl AsRef<std::path::Path> for #name {
fn as_ref(&self) -> &std::path::Path {
&self.path
}
}
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.path.display())
}
}
impl From<#name> for ::tree_type::GenericFile {
fn from(file: #name) -> Self {
Self::new(file.path).expect("Path validation already performed")
}
}
}
}
}
#[expect(clippy::too_many_lines)]
pub fn generate_dir_type(name: &Ident) -> proc_macro2::TokenStream {
let serde_derives = get_serde_derives();
let walk_fns = build_walk_fns();
quote! {
#serde_derives
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct #name {
path: std::path::PathBuf,
}
impl #name {
pub fn new(path: impl Into<std::path::PathBuf>) -> std::io::Result<Self> {
let path_buf = path.into();
if path_buf.as_os_str().is_empty() {
return Err(std::io::Error::from(std::io::ErrorKind::InvalidFilename));
}
Ok(Self { path: path_buf })
}
pub fn as_path(&self) -> &std::path::Path {
&self.path
}
pub fn exists(&self) -> bool {
self.path.exists()
}
pub fn as_generic(&self) -> ::tree_type::GenericDir {
::tree_type::GenericDir::new(self.path.clone()).expect("Path validation already performed")
}
pub fn create(&self) -> std::io::Result<()> {
::tree_type::fs::create_dir(&self.path)
}
pub fn create_all(&self) -> std::io::Result<()> {
::tree_type::fs::create_dir_all(&self.path)
}
pub fn remove(&self) -> std::io::Result<()> {
::tree_type::fs::remove_dir(&self.path)
}
pub fn remove_all(&self) -> std::io::Result<()> {
::tree_type::fs::remove_dir_all(&self.path)
}
pub fn read_dir(&self) -> std::io::Result<impl Iterator<Item = std::io::Result<::tree_type::GenericPath>>> {
::tree_type::fs::read_dir(&self.path)
.map(|read_dir| read_dir.map(|result| result.and_then(::tree_type::GenericPath::try_from)))
}
pub fn fs_metadata(&self) -> std::io::Result<::tree_type::fs::Metadata> {
::tree_type::fs::metadata(&self.path)
}
#walk_fns
pub fn validate(&self) -> ::tree_type::ValidationReport {
let mut report = ::tree_type::ValidationReport::new();
if !self.exists() {
report.errors.push(tree_type::ValidationError {
path: self.path.clone(),
message: "Directory does not exist".to_string(),
});
}
report
}
pub fn setup(&self) -> std::result::Result<Vec<::tree_type::BuildError>, Vec<::tree_type::BuildError>> {
let errors = Vec::new();
if !self.path.exists() {
let create_result = ::tree_type::fs::create_dir_all(&self.path);
if let Err(e) = create_result {
return Err(vec![tree_type::BuildError::Directory(
self.path.clone(),
e
)]);
}
}
Ok(errors)
}
pub fn ensure(&self) -> std::result::Result<::tree_type::ValidationReport, Vec<::tree_type::BuildError>> {
self.setup()?;
Ok(self.validate())
}
pub fn from_generic(dir: ::tree_type::GenericDir) -> Self {
Self { path: dir.as_path().to_path_buf() }
}
pub fn file_name(&self) -> String {
self.path.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or_default()
}
#[cfg(unix)]
pub fn secure(&self) -> std::io::Result<()> {
self.as_generic().secure()
}
pub fn parent(&self) -> Option<::tree_type::GenericDir> {
self.path.parent().and_then(|parent_path| {
::tree_type::GenericDir::new(parent_path).ok()
})
}
}
impl AsRef<std::path::Path> for #name {
fn as_ref(&self) -> &std::path::Path {
&self.path
}
}
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.path.display())
}
}
impl From<#name> for ::tree_type::GenericDir {
fn from(dir: #name) -> Self {
Self::new(dir.path).expect("Path validation already performed")
}
}
}
}