106 lines
3.3 KiB
Rust
106 lines
3.3 KiB
Rust
#![forbid(unsafe_code)]
|
|
|
|
#[macro_use]
|
|
extern crate clap;
|
|
extern crate notify;
|
|
extern crate serde;
|
|
extern crate serde_json;
|
|
extern crate tera;
|
|
|
|
use clap::{App, AppSettings, Arg, Values};
|
|
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
|
use serde_json::{from_str, Value};
|
|
use std::{
|
|
env,
|
|
error::Error,
|
|
fs::{create_dir_all, read_dir, read_to_string, write},
|
|
sync::mpsc::channel,
|
|
time::Duration,
|
|
};
|
|
use tera::{Context, Tera};
|
|
|
|
const DEFAULT_TEMPLATE_DIR: &'static str = "templates";
|
|
const DEFAULT_OUTPUT_DIR: &'static str = "output";
|
|
|
|
fn main() -> Result<(), Box<Error>> {
|
|
let config: Value = from_str(&read_to_string("config.json")?)?;
|
|
let template_dir =
|
|
&env::var("PANRES_TEMPLATE_DIR").unwrap_or_else(|_| String::from(DEFAULT_TEMPLATE_DIR));
|
|
|
|
let matches = App::new("panres")
|
|
.version(crate_version!())
|
|
.author(crate_authors!())
|
|
.about("Universal resume formatter")
|
|
.arg(Arg::with_name("watch").short("w").help("Watch for changes"))
|
|
.arg(
|
|
Arg::with_name("output-format")
|
|
.help("Specifies which output format you want")
|
|
.required(true)
|
|
.multiple(true),
|
|
)
|
|
.settings(&[AppSettings::ArgRequiredElseHelp])
|
|
.get_matches();
|
|
|
|
let tera = &mut Tera::new(&format!("{}/**/*", template_dir))?;
|
|
let context = &mut Context::new();
|
|
context.insert("config", &config);
|
|
let outputs = matches.values_of("output-format").unwrap_or_default();
|
|
|
|
if matches.is_present("watch") {
|
|
let (tx, rx) = channel();
|
|
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
|
watcher.watch(template_dir, RecursiveMode::Recursive)?;
|
|
|
|
loop {
|
|
match rx.recv() {
|
|
Err(e) => println!("{}", e),
|
|
Ok(notify::DebouncedEvent::NoticeWrite(msg))
|
|
| Ok(notify::DebouncedEvent::NoticeRemove(msg))
|
|
| Ok(notify::DebouncedEvent::Chmod(msg))
|
|
| Ok(notify::DebouncedEvent::Remove(msg))
|
|
| Ok(notify::DebouncedEvent::Rename(_, msg)) => {
|
|
println!("Reloading: {:?}", msg);
|
|
tera.full_reload()?;
|
|
output(tera, context, outputs.clone(), template_dir)?;
|
|
},
|
|
Ok(notify::DebouncedEvent::Rescan) => {
|
|
println!("notify emitted rescan event!");
|
|
},
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
output(tera, context, outputs, template_dir)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn output<'a>(
|
|
engine: &Tera,
|
|
context: &Context,
|
|
outputs: Values,
|
|
template_dir: &'a str,
|
|
) -> Result<(), Box<Error>> {
|
|
if outputs.clone().any(|e| e == "all") {
|
|
for output in read_dir(template_dir)? {
|
|
generate_output(engine, context, &output?.file_name().to_str().unwrap())?;
|
|
}
|
|
} else {
|
|
for output in outputs {
|
|
generate_output(engine, context, &output)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_output(engine: &Tera, context: &Context, format: &str) -> Result<(), Box<Error>> {
|
|
create_dir_all(DEFAULT_OUTPUT_DIR).expect("Could not create output dir");
|
|
write(
|
|
format!("{}/{}", DEFAULT_OUTPUT_DIR, format),
|
|
engine.render(format, context.clone())?,
|
|
)
|
|
.expect("to be able to write to output folder");
|
|
Ok(())
|
|
}
|