1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
use tera::Tera;
use serde::Deserialize;
use std::fmt;
use crate::HumusProtoEngine;
use crate::read_toml_from_file;
use crate::TomlError;
/// Helps loading Templates from disk along with a handy configuration file.
///
/// Templates should be placed in a flat file structure in one directory.
/// They may be accompanied by an `extra.toml` file.
/// (by default, the location is configurable)
///
/// It also helps out a bit with deriving template realted configuration
/// for the rest of whatever you are building.
///
/// Example:
/// ```
/// let template_loader = TemplateEngineLoader::new(
/// config.template.template_location.clone(),
/// config.template.extra_config.clone()
/// )
/// .cli_template_location(cli_args.template_location)
/// .cli_extra_config_location(cli_args.extra_config);
///
///
/// let templating_engine = match template_loader.load_templates() {
/// Ok(t) => t.into(),
/// Err(e) => {
/// println!("{e}");
/// ::std::process::exit(1);
/// }
/// };
/// ```
///
#[derive(Deserialize, Clone)]
pub struct TemplateEngineLoader {
/// The path to the directory where the templates are.
pub template_location: String,
/// The path to the extra configuration
/// (relative to the current pwd, not to the templates)
pub extra_config_location: Option<String>,
}
impl TemplateEngineLoader {
/// Creates a new `TemplateEngineLoader` with minimal typing.
pub fn new(
template_location: String,
extra_config_location: Option<String>
) -> Self {
Self {
template_location: template_location,
extra_config_location: extra_config_location,
}
}
/// Overrides the template location with a new location if it is set.
///
/// Intended for processing cli-options.
pub fn cli_template_location(mut self, location: Option<String>) -> Self {
if let Some(location) = location {
self.template_location = location;
}
self
}
/// Overrides the extra configuration location with a new location if it is set.
///
/// Intended for processing cli-options.
pub fn cli_extra_config_location(mut self, location: Option<String>) -> Self {
if let Some(location) = location {
self.extra_config_location = Some(location);
}
self
}
/// Returns the template base directory.
///
/// Contructed by ensuring the template_location ends in a "/".
pub fn base_dir(&self) -> String {
if self.template_location.ends_with("/") {
self.template_location.clone()
} else {
self.template_location.clone()+"/"
}
}
/// Initialize a [HumusProtoEngine] with the given templates and extra configuration.
///
/// Failure Modes:
/// * The `extra.toml` was not found and the path was explicitly set.
/// * The `extra.toml` was found and is not valid toml.
/// * The `extra.toml` passes, but tera finds an error in the templates.
///
/// If `extra_config_location` is `None` no error is returned if the `extra.toml`
/// was not found as the template might not require one.
///
/// [HumusProtoEngine]: ./struct.HumusProtoEngine.html
pub fn load_templates(
&self
) -> Result<HumusProtoEngine,TemplateEngineLoaderError> {
let template_base_dir = self.base_dir();
let template_extra_config_res = match &self.extra_config_location {
Some(path) => read_toml_from_file(path),
None => {
read_toml_from_file(&(template_base_dir.clone()+"extra.toml"))
}
};
let template_extra_config = match template_extra_config_res {
Ok(c) => Some(c),
Err(e) => match &e {
TomlError::FileError{..} => {
// Only fatal if the file was explicitly requested.
// An implicit request could also mean that
// the template doesn't need a config file.
if self.extra_config_location.is_some() {
return Err(TemplateEngineLoaderError::TomlError(e));
}
None
},
TomlError::ParseError{..} => {
return Err(TemplateEngineLoaderError::TomlError(e));
}
},
};
let template_glob = template_base_dir.clone()+"*";
println!("Parsing Templates from '{}' ...", &template_glob);
let res = Tera::new((template_glob).as_str());
let tera = match res {
Ok(t) => t,
Err(e) => {
return Err(TemplateEngineLoaderError::TemplateParseError{
path: template_glob,
tera_error: e,
});
}
};
Ok(HumusProtoEngine {
tera: tera,
template_config: template_extra_config,
})
}
}
/// Returned when loading a template using the [TemplateEngineLoader] fails.
///
/// [TemplateEngineLoader]: ./struct.TemplateEngineLoader.html
pub enum TemplateEngineLoaderError {
/// An error occourred while loading the template configuration file.
TomlError(TomlError),
/// An error occourred while parsing the templates.
TemplateParseError{
/// Path to the template directory
path: String,
/// what went wrong
tera_error: tera::Error
},
}
impl fmt::Display for TemplateEngineLoaderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TomlError(e) =>
write!(f,"Error with template extra configuration:\n{e}"),
Self::TemplateParseError{path, tera_error} =>
write!(f,"Error parsing template '{path}':\n{tera_error}"),
}
}
}