tmpl/src/main.rs

121 lines
3.1 KiB
Rust

mod model;
use std::path::PathBuf;
use std::io::Read;
use argparse::{ArgumentParser, Store, StoreOption};
use serde::de::DeserializeOwned;
use serde_yaml::Value;
use tera::{Context, Tera};
use tracing::error;
use model::Instructions;
#[derive(Debug, thiserror::Error)]
enum LoadError {
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Yaml(#[from] serde_yaml::Error),
}
#[derive(Debug, thiserror::Error)]
enum ProcessInstructionsError {
#[error("Error loading templates: {0}")]
Tera(#[from] tera::Error),
}
#[derive(Default)]
struct Args {
instructions: String,
values: String,
templates: Option<String>,
destdir: Option<PathBuf>,
}
fn parse_args(args: &mut Args) {
let mut parser = ArgumentParser::new();
parser
.refer(&mut args.instructions)
.required()
.add_argument("instructions", Store, "Instructions");
parser
.refer(&mut args.values)
.required()
.add_argument("values", Store, "Values");
parser.refer(&mut args.templates).add_option(
&["--templates", "-t"],
StoreOption,
"Templates directory",
);
parser.refer(&mut args.destdir).add_option(
&["--destdir", "-d"],
StoreOption,
"Destination directory",
);
parser.parse_args_or_exit();
}
fn main() {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.init();
let mut args: Args = Default::default();
parse_args(&mut args);
let instructions = match load_yaml(&args.instructions) {
Ok(i) => i,
Err(e) => {
error!(
"Could not load instructions from {}: {}",
args.instructions, e
);
std::process::exit(1);
}
};
let values = match load_yaml(&args.values) {
Ok(i) => i,
Err(e) => {
error!("Could not load values from {}: {}", args.values, e);
std::process::exit(1);
}
};
let templates = args.templates.as_deref().unwrap_or("templates");
if let Err(e) = process_instructions(templates, instructions, values) {
error!("Failed to process instructions: {}", e);
std::process::exit(1);
}
}
fn load_yaml<T: DeserializeOwned>(path: &str) -> Result<T, LoadError> {
let mut yaml = vec![];
if path == "-" {
std::io::stdin().read_to_end(&mut yaml)?;
} else {
let mut f = std::fs::File::open(path)?;
f.read_to_end(&mut yaml)?;
}
Ok(serde_yaml::from_slice(&yaml)?)
}
fn process_instructions(
templates: &str,
instructions: Instructions,
values: Value,
) -> Result<(), ProcessInstructionsError> {
let tera = Tera::new(&format!("{}/**", templates))?;
let ctx = Context::from_serialize(&values)?;
for i in instructions.render {
match tera.render(&i.template, &ctx) {
Ok(o) => {
print!("{}", o);
}
Err(e) => {
error!("Failed to render template {}: {}", i.template, e);
}
}
}
Ok(())
}