panres/src/main.rs

125 lines
3.6 KiB
Rust
Raw Normal View History

2019-05-06 03:27:01 +00:00
#![forbid(unsafe_code)]
2022-06-03 07:11:54 +00:00
use clap::{crate_authors, crate_version, Parser};
use crossbeam::channel::unbounded;
2020-07-24 02:04:24 +00:00
use json5::from_str;
2019-05-11 15:44:26 +00:00
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
2020-07-24 02:04:24 +00:00
use serde_json::Value;
2020-07-23 23:19:38 +00:00
use std::env;
use std::error::Error;
use std::fs::{create_dir_all, read_dir, read_to_string, write};
2021-09-29 05:01:17 +00:00
use std::path::{Path, PathBuf};
2020-07-23 23:19:38 +00:00
use std::process;
2019-08-30 21:50:10 +00:00
use tera::{Context, Error as TeraError, Tera};
2019-05-06 03:27:01 +00:00
2019-12-04 00:35:36 +00:00
const DEFAULT_TEMPLATE_DIR: &str = "templates";
const DEFAULT_OUTPUT_DIR: &str = "output";
2019-05-06 03:27:01 +00:00
2019-08-30 21:50:10 +00:00
fn main() -> Result<(), Box<dyn Error>> {
process::exit(match run() {
Ok(_) => 0,
Err(e) => {
eprintln!("{}", e);
1
}
})
}
fn env_or_default(env_name: &str, default: &str) -> String {
env::var(env_name).unwrap_or_else(|_| String::from(default))
}
2022-06-03 07:11:54 +00:00
/// Universal resume formatter
#[derive(Parser)]
#[clap(version = crate_version!(), author = crate_authors!())]
struct Args {
/// Watch for changes
#[clap(short)]
watch: bool,
/// Specifies which output format you want
#[clap(short, long, multiple_occurrences = true)]
output: Vec<String>,
}
2019-08-30 21:50:10 +00:00
fn run() -> Result<(), Box<dyn Error>> {
2020-07-24 02:04:24 +00:00
let config: Value = from_str(&read_to_string("config.json5")?)?;
2020-07-23 23:19:38 +00:00
let template_dir = env_or_default("PANRES_TEMPLATE_DIR", DEFAULT_TEMPLATE_DIR);
let output_dir = env_or_default("PANRES_OUTPUT_DIR", DEFAULT_OUTPUT_DIR);
2019-08-30 21:50:10 +00:00
2022-06-03 07:11:54 +00:00
let args = Args::parse();
2019-08-30 21:50:10 +00:00
2022-06-03 07:11:54 +00:00
let tera = Tera::new(&format!("{template_dir}/**/*"))?;
2020-07-23 23:19:38 +00:00
let mut context = Context::new();
2019-08-30 21:50:10 +00:00
context.insert("config", &config);
2021-09-29 05:01:17 +00:00
let template_dir = PathBuf::from(template_dir);
2022-06-03 07:11:54 +00:00
output(&tera, &context, &output_dir, &args.output, &template_dir)?;
2020-07-23 23:19:38 +00:00
2022-06-03 07:11:54 +00:00
if args.watch {
watch_mode(tera, context, output_dir, args.output, &template_dir)?;
2019-08-30 21:50:10 +00:00
}
2020-07-23 23:19:38 +00:00
Ok(())
2019-08-30 21:50:10 +00:00
}
2019-05-06 03:27:01 +00:00
2019-08-30 21:50:10 +00:00
/// Usually never returns, unless there was an error initializing the watcher.
/// Handles watching for file changes, and reloads tera if there's a change.
2021-09-29 05:01:17 +00:00
fn watch_mode(
2020-07-23 23:19:38 +00:00
mut engine: Tera,
context: Context,
dir: String,
outputs: Vec<String>,
2021-09-29 05:01:17 +00:00
template_dir: &Path,
2019-08-30 21:50:10 +00:00
) -> Result<(), Box<dyn Error>> {
let (tx, rx) = unbounded();
2021-09-29 05:01:17 +00:00
let mut watcher: RecommendedWatcher = Watcher::new(move |res| tx.send(res).unwrap())?;
watcher.watch(template_dir, RecursiveMode::Recursive)?;
2019-05-11 15:44:26 +00:00
2020-07-23 23:19:38 +00:00
for res in rx {
match res {
2019-08-30 21:50:10 +00:00
Err(e) => println!("{}", e),
Ok(event) => {
println!("got event {:?}", event);
2020-07-23 23:19:38 +00:00
engine.full_reload().expect("Failed to perform full reload");
2021-09-29 05:01:17 +00:00
output(&engine, &context, &dir, &outputs, template_dir)
2020-07-23 23:19:38 +00:00
.expect("Failed to call output");
2019-05-11 15:44:26 +00:00
}
}
}
2020-07-23 23:19:38 +00:00
Ok(())
2019-05-11 15:44:26 +00:00
}
2019-05-06 03:27:01 +00:00
2019-08-30 21:50:10 +00:00
/// Parses the output values and generates a file for each format specified or
/// found, if told to generate all outputs.
2021-09-29 05:01:17 +00:00
fn output(
2019-05-11 15:44:26 +00:00
engine: &Tera,
context: &Context,
2019-05-11 15:48:47 +00:00
dir: &str,
2019-12-04 00:35:36 +00:00
outputs: &[String],
2021-09-29 05:01:17 +00:00
template_dir: &Path,
2019-08-30 21:50:10 +00:00
) -> Result<(), Box<dyn Error>> {
if outputs.contains(&String::from("all")) {
2019-05-11 15:44:26 +00:00
for output in read_dir(template_dir)? {
2019-08-30 21:50:10 +00:00
write_file(engine, context, dir, &output?.file_name().to_str().unwrap())?;
2019-05-06 03:27:01 +00:00
}
} else {
for output in outputs {
2019-08-30 21:50:10 +00:00
write_file(engine, context, dir, &output)?;
2019-05-06 03:27:01 +00:00
}
}
Ok(())
}
2019-08-30 21:50:10 +00:00
/// Write out the post-template file to the output dir.
fn write_file(engine: &Tera, context: &Context, dir: &str, format: &str) -> Result<(), TeraError> {
2019-05-11 15:48:47 +00:00
create_dir_all(dir).expect("Could not create output dir");
2019-05-11 15:44:26 +00:00
write(
2019-05-11 15:48:47 +00:00
format!("{}/{}", dir, format),
2020-07-23 23:19:38 +00:00
engine.render(format, context)?,
2019-05-11 15:44:26 +00:00
)
.expect("to be able to write to output folder");
2019-05-06 03:27:01 +00:00
Ok(())
}