Initial commit
commit
b625498c54
|
@ -0,0 +1 @@
|
||||||
|
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "tmpl"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
argparse = "0.2.2"
|
||||||
|
file-mode = { version = "0.1.2", features = ["serde"] }
|
||||||
|
serde = { version = "1.0.195", features = ["derive"] }
|
||||||
|
serde_yaml = "0.9.30"
|
||||||
|
tera = "1.19.1"
|
||||||
|
thiserror = "1.0.56"
|
||||||
|
tracing = { version = "0.1.40", features = ["log"] }
|
||||||
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
|
@ -0,0 +1,120 @@
|
||||||
|
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(())
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct RenderInstruction {
|
||||||
|
pub template: String,
|
||||||
|
pub dest: PathBuf,
|
||||||
|
pub owner: Option<String>,
|
||||||
|
pub group: Option<String>,
|
||||||
|
pub mode: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Instructions {
|
||||||
|
pub render: Vec<RenderInstruction>,
|
||||||
|
}
|
Loading…
Reference in New Issue