From 3bdfc48e25f954904f15a164fd0125bc84d20e8b Mon Sep 17 00:00:00 2001 From: jzbor Date: Fri, 19 Jul 2024 15:01:14 +0200 Subject: [PATCH] Register templates on compilation --- src/error.rs | 5 ++++- src/job.rs | 51 ++++++++++++++++++++++++++++++++------------------- src/main.rs | 30 +++++++++++++++++++++++++++++- src/worker.rs | 1 + 4 files changed, 66 insertions(+), 21 deletions(-) diff --git a/src/error.rs b/src/error.rs index 10b3d2c..1924609 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,7 +28,10 @@ pub enum ZinnError { MissingArgument(String), #[error("Template rendering failed - ({0})")] - TemplateError(#[from] handlebars::RenderError), + RenderError(#[from] handlebars::RenderError), + + #[error("Template rendering failed - ({0})")] + TemplateError(#[from] handlebars::TemplateError), #[error("Missing input file \"{0}\"")] InputFileError(String), diff --git a/src/job.rs b/src/job.rs index 9b5216d..7dbc64b 100644 --- a/src/job.rs +++ b/src/job.rs @@ -7,12 +7,14 @@ use std::io::{BufRead, BufReader}; use std::sync::Arc; use handlebars::Handlebars; +use handlebars::Renderable; use serde::{Deserialize, Serialize}; use crate::barkeeper::ThreadStateTracker; use crate::error::*; use crate::queue::JobState; use crate::Options; +use crate::TemplateType; /// Template for a job as described in the Zinnfile @@ -98,15 +100,12 @@ pub type JobRealization = Arc; impl JobDescription { /// Resolve templates and dependencies - pub fn realize(&self, name: &str, job_descriptions: &HashMap, handlebars: &Handlebars, constants: &HashMap, parameters: &HashMap) -> ZinnResult { + pub fn realize(&self, name: &str, job_descriptions: &HashMap, handlebars: &mut Handlebars, constants: &HashMap, parameters: &HashMap) -> ZinnResult { let mut dependencies = Vec::new(); let mut param_values = Vec::new(); let name = name.to_owned(); - let mut combined_vars = HashMap::new(); - for (name, value) in constants { - combined_vars.insert(name.clone(), handlebars.render_template(value, &())?); - } + let mut combined_vars = constants.clone(); for arg in &self.args { match parameters.get(arg).or(self.defaults.get(arg)) { @@ -121,33 +120,35 @@ impl JobDescription { // render input files let mut inputs = Vec::new(); if let Some(input_str) = &self.inputs { - let rendered_input_str = handlebars.render_template(input_str, &combined_vars)?; + let rendered_input_str = Self::render_component(TemplateType::Inputs, &["", "", &name], input_str, handlebars, &combined_vars)?; let additional_inputs = rendered_input_str.split(char::is_whitespace) .filter(|v| !v.is_empty()) .map(|s| s.to_owned()); inputs.extend(additional_inputs) } for input in &self.input_list { - inputs.push(handlebars.render_template(input, &combined_vars)?); + let rendered = Self::render_component(TemplateType::InputListElem, &[input, "", &name], input, handlebars, &combined_vars)?; + inputs.push(rendered); } // render output files let mut outputs = Vec::new(); if let Some(output_str) = &self.outputs { - let rendered_output_str = handlebars.render_template(output_str, &combined_vars)?; + let rendered_output_str = Self::render_component(TemplateType::Outputs, &["", "", &name], output_str, handlebars, &combined_vars)?; let additional_outputs = rendered_output_str .split(char::is_whitespace) .filter(|v| !v.is_empty()) .map(|s| s.to_owned()); outputs.extend(additional_outputs) } - for output in &self.output_list { - outputs.push(handlebars.render_template(output, &combined_vars)?); + for (i, output) in self.output_list.iter().enumerate() { + let rendered = Self::render_component(TemplateType::OutputListElem, &[&i.to_string(), "", &name], output, handlebars, &combined_vars)?; + outputs.push(rendered); } - for dep in &self.requires { - let mut realized_dep_desc = dep.with.clone(); - for val in realized_dep_desc.values_mut() { - *val = handlebars.render_template(val, &combined_vars)?; + for (i, dep) in self.requires.iter().enumerate() { + let mut realized_dep_params = dep.with.clone(); + for (key, val) in &mut realized_dep_params { + *val = Self::render_component(TemplateType::DependencyParam, &[&i.to_string(), key, &name], val, handlebars, &combined_vars)?; } let dep_desc = match job_descriptions.get(&dep.job) { @@ -156,23 +157,23 @@ impl JobDescription { }; if let Some(with_list) = &dep.foreach { - let inputs = handlebars.render_template(&with_list.r#in, &combined_vars)?; + let inputs = Self::render_component(TemplateType::ForEach, &[&i.to_string(), "", &name], &with_list.r#in, handlebars, &combined_vars)?; let val_list = inputs.split(char::is_whitespace) .filter(|v| !v.is_empty()); for val in val_list { // mutating the environment is fine, as it will be overridden // for every iteration with the proper value. - realized_dep_desc.insert(with_list.var.to_owned(), val.to_owned()); - let dep_realization = dep_desc.realize(&dep.job, job_descriptions, handlebars, constants, &realized_dep_desc)?; + realized_dep_params.insert(with_list.var.to_owned(), val.to_owned()); + let dep_realization = dep_desc.realize(&dep.job, job_descriptions, handlebars, constants, &realized_dep_params)?; dependencies.push(dep_realization); } } else { - let dep_realization = dep_desc.realize(&dep.job, job_descriptions, handlebars, constants, &realized_dep_desc)?; + let dep_realization = dep_desc.realize(&dep.job, job_descriptions, handlebars, constants, &realized_dep_params)?; dependencies.push(dep_realization); } } - let run = handlebars.render_template(&self.run, &combined_vars)?; + let run = Self::render_component(TemplateType::Run, &["", "", &name], &self.run, handlebars, &combined_vars)?; let name = name.replace('\n', ""); let interactive = self.interactive; @@ -184,6 +185,18 @@ impl JobDescription { pub fn args(&self) -> &Vec { &self.args } + + fn render_component(tt: TemplateType, suffix: &[&str; 3], template: &str, handlebars: &mut Handlebars, context: &HashMap) -> ZinnResult { + let template_name = tt.to_name(suffix); + match handlebars.get_template(&template_name) { + Some(_) => Ok(handlebars.render(&template_name, context)?), + None => { + let template = handlebars::Template::compile_with_name(template, template_name.clone())?; + handlebars.register_template(&template_name, template); + Ok(handlebars.render(&template_name, context)?) + }, + } + } } impl InnerJobRealization { diff --git a/src/main.rs b/src/main.rs index 0434722..632a1ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,6 +142,34 @@ impl Args { } } +#[derive(Clone, Copy, Debug)] +enum TemplateType { + Inputs, + InputListElem, + Outputs, + OutputListElem, + DependencyParam, + ForEach, + Run, +} + + +impl TemplateType { + fn to_name(&self, suffix: &[&str; 3]) -> String { + use TemplateType::*; + // prefixes must not be the same or similar + match self { + Inputs => format!("inputs-{}-{}-{}", suffix[0], suffix[1], suffix[2]), + InputListElem => format!("input-file-{}-{}-{}", suffix[0], suffix[1], suffix[2]), + Outputs => format!("outputs-{}-{}-{}", suffix[0], suffix[1], suffix[2]), + OutputListElem => format!("output-file-{}-{}-{}", suffix[0], suffix[1], suffix[2]), + DependencyParam => format!("dependency-param-{}-{}-{}", suffix[0], suffix[1], suffix[2]), + ForEach => format!("dependency-foreach-{}-{}-{}", suffix[0], suffix[1], suffix[2]), + Run => format!("run-{}-{}-{}", suffix[0], suffix[1], suffix[2]), + } + } +} + /// Parse a single key-value pair fn parse_key_val(s: &str) -> Result<(T, U), Box> @@ -278,7 +306,7 @@ fn main() { let parameters = args.param.iter().cloned().collect(); for name in &args.targets { let job = match zinnfile.jobs.get(name) { - Some(job) => resolve(job.realize(name, &zinnfile.jobs, &handlebars, &constants, ¶meters)), + Some(job) => resolve(job.realize(name, &zinnfile.jobs, &mut handlebars, &constants, ¶meters)), None => resolve(Err(ZinnError::JobNotFound(name.to_owned()))), }; for dep in job.transitive_dependencies() { diff --git a/src/worker.rs b/src/worker.rs index de6bba4..9c5e610 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -10,6 +10,7 @@ pub fn run_worker(queue: Queue, mut tracker: impl ThreadStateTracker, options: O if let Some(job) = queue.fetch() { tracker.set_prefix(job.to_string()); let state = job.run(&mut tracker, &options) + .map_err(|e| { eprintln!("{}", e); e }) // TODO: Better error messages .unwrap_or(JobState::Failed); tracker.job_completed(job.clone(), state); queue.finished(job, state);