use crate::Error;
use git2::Repository;
use handlebars::Handlebars;
use serde::Serialize;
use std::collections::HashMap;
use std::fs::{create_dir_all, read_dir, File};
use std::io::prelude::*;
use std::path::Path;
use tempfile::tempdir;
pub enum Template {
Git(String),
Folder(String),
Map(HashMap<String, String>),
}
impl Template {
pub fn from_string(s: &str) -> Self {
match s {
s if s.starts_with("git:") => Self::Git(s[4..].to_string()),
s => Self::Folder(s.to_string()),
}
}
pub fn from_map(map: HashMap<String, String>) -> Self {
Self::Map(map)
}
pub fn render<T>(&self, path: &Path, values: &T) -> Result<(), Error>
where
T: Serialize,
{
let reg = Handlebars::new();
match self {
Self::Git(link) => {
let temp_dir = tempdir()?;
let folder_path = temp_dir.path();
Repository::clone(link, folder_path)?;
self.visit_path(folder_path, folder_path, ®, path, values)?;
Ok(())
}
Self::Folder(folder) => {
let folder_path = Path::new(folder);
if !folder_path.is_dir() {
return Err(Error::RenderTemplateError(
"template folder does not exist",
folder.clone(),
));
}
self.visit_path(folder_path, folder_path, ®, path, values)?;
Ok(())
}
Self::Map(files) => {
for (name, template) in files {
let file_path = path.join(name);
self.render_file(®, file_path.as_path(), template, values)?;
}
Ok(())
}
}
}
fn render_file<T>(
&self,
reg: &Handlebars,
path: &Path,
template: &str,
values: &T,
) -> Result<(), Error>
where
T: Serialize,
{
if let Some(folder) = path.parent() {
if !folder.is_dir() {
create_dir_all(folder)?;
}
}
let file = File::create(path)?;
reg.render_template_to_write(template, values, file)?;
Ok(())
}
fn visit_path<T>(
&self,
root: &Path,
dest_path: &Path,
reg: &Handlebars,
output_path: &Path,
values: &T,
) -> Result<(), Error>
where
T: Serialize,
{
if dest_path.is_dir() {
for entry in read_dir(dest_path)? {
let entry = entry?;
let entry_path = entry.path();
let relative_path = dest_path.strip_prefix(root)?;
let entry_output_path = output_path.join(relative_path);
create_dir_all(entry_output_path)?;
self.visit_path(root, &entry_path, reg, output_path, values)?;
}
} else {
let mut file = File::open(&dest_path)?;
let mut data = String::new();
file.read_to_string(&mut data)?;
let relative_path = dest_path.strip_prefix(root)?;
let entry_output_path = output_path.join(relative_path);
self.render_file(reg, &entry_output_path, &data, values)?;
}
Ok(())
}
}