use std::{
fs::File,
io::{Read, Write},
};
use tectonic_errors::prelude::*;
use tectonic_status_base::tt_warning;
use crate::Common;
#[derive(Debug)]
pub(crate) struct Templating {
tera: tera::Tera,
context: tera::Context,
next_template_path: String,
next_output_path: String,
}
impl Templating {
pub(crate) fn new(
tera: tera::Tera,
context: tera::Context,
next_template_path: String,
next_output_path: String,
) -> Self {
Templating {
tera,
context,
next_template_path,
next_output_path,
}
}
pub(crate) fn handle_set_template<S: ToString>(&mut self, arg: S) {
self.next_template_path = arg.to_string();
}
pub(crate) fn handle_set_output_path<S: ToString>(&mut self, arg: S) {
self.next_output_path = arg.to_string();
}
pub(crate) fn handle_set_template_variable(
&mut self,
remainder: &str,
common: &mut Common,
) -> Result<()> {
if let Some((varname, varval)) = remainder.split_once(' ') {
self.set_variable(varname, varval);
} else {
tt_warning!(
common.status,
"ignoring malformatted tdux:setTemplateVariable special `{}`",
remainder
);
}
Ok(())
}
pub(crate) fn set_variable<S: AsRef<str>>(&mut self, name: &str, value: S) {
self.context.insert(name, value.as_ref());
}
pub(crate) fn ready_to_output(&self) -> bool {
!self.next_template_path.is_empty() && !self.next_output_path.is_empty()
}
pub(crate) fn emit(&mut self, common: &mut Common) -> Result<()> {
if self.next_template_path.is_empty() {
bail!("need to emit HTML content but no template has been specified; is your document HTML-compatible?");
}
if self.next_output_path.is_empty() {
bail!("need to emit HTML content but no output path has been specified; is your document HTML-compatible?");
}
let (out_path, n_levels) =
crate::assets::create_output_path(&self.next_output_path, common)?;
if n_levels < 2 {
self.context.insert("tduxRelTop", "");
} else {
let mut rel_top = String::default();
for _ in 0..(n_levels - 1) {
rel_top.push_str("../");
}
self.context.insert("tduxRelTop", &rel_top);
}
let mut ih = atry!(
common.hooks.io().input_open_name(&self.next_template_path, common.status).must_exist();
["unable to open input HTML template `{}`", &self.next_template_path]
);
let mut template = String::new();
atry!(
ih.read_to_string(&mut template);
["unable to read input HTML template `{}`", &self.next_template_path]
);
let (name, digest_opt) = ih.into_name_digest();
common
.hooks
.event_input_closed(name, digest_opt, common.status);
let rendered = atry!(
self.tera.render_str(&template, &self.context);
["failed to render HTML template `{}` while creating `{}`", &self.next_template_path, &self.next_output_path]
);
if let Some(out_path) = out_path {
let mut out_file = atry!(
File::create(&out_path);
["cannot open output file `{}`", out_path.display()]
);
atry!(
out_file.write_all(rendered.as_bytes());
["cannot write output file `{}`", out_path.display()]
);
}
self.next_output_path.clear();
Ok(())
}
}