use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
};
#[cfg(feature = "yaml")]
use serde::Deserialize;
#[derive(Debug)]
#[cfg_attr(feature = "yaml", derive(Deserialize))]
pub struct TreeBuilder {
#[cfg_attr(feature = "yaml", serde(default = "crate::tree::temp_dir"))]
pub root: PathBuf,
#[cfg_attr(feature = "yaml", serde(default))]
override_file: bool,
entries: Vec<crate::Entry>,
#[cfg_attr(feature = "yaml", serde(default = "crate::yaml::default_drop"))]
drop: bool,
}
impl TreeBuilder {
#[must_use]
pub fn root_folder<P: AsRef<Path>>(mut self, dir: P) -> Self {
self.root = dir.as_ref().to_path_buf();
self
}
#[must_use]
pub const fn drop(mut self, yes: bool) -> Self {
self.drop = yes;
self
}
#[must_use]
pub const fn override_file(mut self, yes: bool) -> Self {
self.override_file = yes;
self
}
#[must_use]
pub fn add<P: AsRef<Path>>(mut self, path: P, content: &str) -> Self {
self.entries.push(crate::Entry {
path: path.as_ref().to_path_buf(),
kind: crate::Kind::TextFile {
content: content.to_string(),
},
settings: None,
});
self
}
#[must_use]
pub fn add_file<P: AsRef<Path>>(self, path: P, content: &str) -> Self {
self.add(path, content)
}
#[must_use]
pub fn add_empty<P: AsRef<Path>>(self, path: P) -> Self {
self.add_empty_file(path)
}
#[must_use]
pub fn add_empty_file<P: AsRef<Path>>(mut self, path: P) -> Self {
self.entries.push(crate::Entry {
path: path.as_ref().to_path_buf(),
kind: crate::Kind::EmptyFile,
settings: None,
});
self
}
#[must_use]
pub fn add_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
self.entries.push(crate::Entry {
path: path.as_ref().to_path_buf(),
kind: crate::Kind::Directory,
settings: None,
});
self
}
#[must_use]
pub fn add_file_with_settings<P: AsRef<Path>>(
mut self,
path: P,
content: &str,
settings: crate::tree::Settings,
) -> Self {
self.entries.push(crate::Entry {
path: path.as_ref().to_path_buf(),
kind: crate::Kind::TextFile {
content: content.to_string(),
},
settings: Some(settings),
});
self
}
#[must_use]
pub fn add_empty_file_with_settings<P: AsRef<Path>>(
mut self,
path: P,
settings: crate::tree::Settings,
) -> Self {
self.entries.push(crate::Entry {
path: path.as_ref().to_path_buf(),
kind: crate::Kind::EmptyFile,
settings: Some(settings),
});
self
}
#[must_use]
pub fn add_directory_with_settings<P: AsRef<Path>>(
mut self,
path: P,
settings: crate::tree::Settings,
) -> Self {
self.entries.push(crate::Entry {
path: path.as_ref().to_path_buf(),
kind: crate::Kind::Directory,
settings: Some(settings),
});
self
}
#[must_use]
pub fn add_readonly_file<P: AsRef<Path>>(self, path: P, content: &str) -> Self {
self.add_file_with_settings(path, content, crate::tree::Settings::new().readonly(true))
}
#[must_use]
pub fn add_readonly_empty_file<P: AsRef<Path>>(self, path: P) -> Self {
self.add_empty_file_with_settings(path, crate::tree::Settings::new().readonly(true))
}
pub fn create(&self) -> std::io::Result<crate::Tree> {
if !self.root.exists() {
std::fs::create_dir_all(&self.root)?;
}
for entry in &self.entries {
let dest_path = self.root.join(&entry.path);
if !self.override_file && dest_path.exists() {
continue;
}
match &entry.kind {
crate::Kind::Directory => {
std::fs::create_dir_all(&dest_path)?;
}
crate::Kind::EmptyFile => {
if let Some(parent_dir) = Path::new(&dest_path).parent() {
std::fs::create_dir_all(parent_dir)?;
}
File::create(&dest_path)?;
}
crate::Kind::TextFile { content } => {
if let Some(parent_dir) = Path::new(&dest_path).parent() {
std::fs::create_dir_all(parent_dir)?;
}
let mut file = File::create(&dest_path)?;
file.write_all(content.as_bytes())?;
}
}
if let Some(settings) = &entry.settings {
if matches!(entry.kind, crate::Kind::Directory) {
continue;
}
let dest_path_for_perms = self.root.join(&entry.path);
if settings.readonly {
let mut permissions = std::fs::metadata(&dest_path_for_perms)?.permissions();
permissions.set_readonly(true);
std::fs::set_permissions(&dest_path_for_perms, permissions)?;
}
}
}
Ok(crate::Tree {
root: self.root.clone(),
drop: self.drop,
})
}
}
impl Default for TreeBuilder {
fn default() -> Self {
Self {
entries: vec![],
override_file: false,
root: crate::tree::temp_dir(),
drop: true,
}
}
}