Skip to content

Commit

Permalink
Register templates on compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
jzbor committed Jul 19, 2024
1 parent 1e4bf33 commit 3bdfc48
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 21 deletions.
5 changes: 4 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
51 changes: 32 additions & 19 deletions src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -98,15 +100,12 @@ pub type JobRealization = Arc<InnerJobRealization>;

impl JobDescription {
/// Resolve templates and dependencies
pub fn realize(&self, name: &str, job_descriptions: &HashMap<String, JobDescription>, handlebars: &Handlebars, constants: &HashMap<String, String>, parameters: &HashMap<String, String>) -> ZinnResult<JobRealization> {
pub fn realize(&self, name: &str, job_descriptions: &HashMap<String, JobDescription>, handlebars: &mut Handlebars, constants: &HashMap<String, String>, parameters: &HashMap<String, String>) -> ZinnResult<JobRealization> {
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)) {
Expand All @@ -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) {
Expand All @@ -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;

Expand All @@ -184,6 +185,18 @@ impl JobDescription {
pub fn args(&self) -> &Vec<String> {
&self.args
}

fn render_component(tt: TemplateType, suffix: &[&str; 3], template: &str, handlebars: &mut Handlebars, context: &HashMap<String, String>) -> ZinnResult<String> {
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 {
Expand Down
30 changes: 29 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
Expand Down Expand Up @@ -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, &parameters)),
Some(job) => resolve(job.realize(name, &zinnfile.jobs, &mut handlebars, &constants, &parameters)),
None => resolve(Err(ZinnError::JobNotFound(name.to_owned()))),
};
for dep in job.transitive_dependencies() {
Expand Down
1 change: 1 addition & 0 deletions src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 3bdfc48

Please sign in to comment.