use serde::Serialize;
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::Path;
use tinytemplate::TinyTemplate;
use crate::spec::Spec;
pub enum SourceFileContents {
Template(&'static str),
Copy(&'static [u8]),
}
pub struct SourceFile {
name: String,
contents: SourceFileContents,
}
pub struct SourceTree {
files: Vec<SourceFile>,
}
#[derive(Serialize)]
pub struct Context<'a> {
spec: &'a Spec,
}
pub struct SourceTreeIter<'a> {
files: &'a [SourceFile],
current: usize,
context: Context<'a>,
destination_path: &'a Path,
}
#[derive(Debug)]
pub enum Error {
Template(tinytemplate::error::Error),
IO(std::io::Error),
}
impl<'a> Iterator for SourceTreeIter<'a> {
type Item = Result<(), Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.current == self.files.len() {
return None;
}
let file = &self.files[self.current];
self.current += 1;
let mut buf = self.destination_path.to_path_buf();
buf.push(file.name.to_owned());
{
let mut buf = buf.clone();
buf.pop();
let _ = create_dir_all(buf);
}
match file.contents {
SourceFileContents::Copy(contents) => {
let mut file = match File::create(buf) {
Ok(file) => file,
Err(error) => return Some(Err(Error::IO(error))),
};
if let Err(error) = file.write_all(contents) {
return Some(Err(Error::IO(error)));
}
}
SourceFileContents::Template(template) => {
let mut engine = TinyTemplate::new();
if let Err(error) = engine.add_template("template", template) {
return Some(Err(Error::Template(error)));
}
let contents = match engine.render("template", &self.context) {
Ok(contents) => contents,
Err(error) => return Some(Err(Error::Template(error))),
};
let mut file = match File::create(buf) {
Ok(file) => file,
Err(error) => return Some(Err(Error::IO(error))),
};
if let Err(error) = file.write_all(contents.as_bytes()) {
return Some(Err(Error::IO(error)));
}
}
}
Some(Ok(()))
}
}
impl SourceTree {
pub fn new() -> SourceTree {
SourceTree { files: vec![] }
}
pub fn copy(&mut self, name: &str, contents: &'static [u8]) {
self.files.push(SourceFile {
name: name.to_owned(),
contents: SourceFileContents::Copy(contents),
})
}
pub fn template(&mut self, name: &str, contents: &'static str) {
self.files.push(SourceFile {
name: name.to_owned(),
contents: SourceFileContents::Template(contents),
});
}
pub fn len(&self) -> usize {
self.files.len()
}
pub fn generate<'a>(
&'a self,
spec: &'a Spec,
destination_path: &'a Path,
) -> SourceTreeIter<'a> {
SourceTreeIter {
files: &self.files,
current: 0,
context: Context { spec },
destination_path,
}
}
}