Made template loading logic reuseable

This commit is contained in:
Slatian 2023-10-29 20:52:32 +01:00
parent 5adca4fb80
commit 912a119361
5 changed files with 205 additions and 56 deletions

View File

@ -19,7 +19,6 @@ use clap::Parser;
use lazy_static::lazy_static;
use regex::Regex;
use serde::{Deserialize,Serialize};
use tera::Tera;
use tower::ServiceBuilder;
use tower_http::services::ServeDir;
use trust_dns_resolver::Name;
@ -32,7 +31,6 @@ use tokio::signal::unix::{
use tokio::task;
use std::collections::HashMap;
use std::fs;
use std::net::IpAddr;
use std::sync::Arc;
@ -54,6 +52,8 @@ use crate::geoip::{
};
use crate::idna::IdnaName;
use crate::mycelium::MycEngine;
use crate::mycelium::TemplateEngineLoader;
use crate::mycelium::read_toml_from_file;
use crate::simple_dns::DnsLookupResult;
use crate::settings::*;
use crate::view::View;
@ -137,23 +137,6 @@ fn match_domain_hidden_list(domain: &String, hidden_list: &Vec<String>) -> bool
return false;
}
fn read_toml_from_file<T: for<'de> serde::Deserialize<'de>>(path: &String) -> Option<T> {
let text = match fs::read_to_string(path) {
Ok(t) => t,
Err(e) => {
println!("Error while reading file '{path}': {e}");
return None;
}
};
match toml::from_str(&text) {
Ok(t) => Some(t),
Err(e) => {
println!("Unable to parse file '{path}':\n{e}");
return None;
}
}
}
#[tokio::main]
async fn main() {
// Parse Command line arguments
@ -163,9 +146,11 @@ async fn main() {
let config: config::EchoIpServiceConfig = match cli_args.config {
Some(config_path) => {
match read_toml_from_file::<config::EchoIpServiceConfig>(&config_path) {
Some(c) => c,
None => {
println!("Could not read confuration file, exiting.");
Ok(c) => c,
Err(e) => {
println!("Could not read confuration file!");
println!("{e}");
println!("Exiting ...");
::std::process::exit(1);
}
}
@ -174,47 +159,25 @@ async fn main() {
};
// Initalize Tera templates
let mut template_base_dir = match cli_args.template_location {
Some(template_base_dir) => template_base_dir,
None => (&config.template.template_location).to_owned(),
};
if !template_base_dir.ends_with("/") {
template_base_dir = template_base_dir + "/";
}
let template_extra_config = match &cli_args.extra_config {
Some(path) => read_toml_from_file(path),
None => match &config.template.extra_config {
Some(path) => read_toml_from_file(path),
None => {
println!("Trying to read default template configuration ...");
println!("(If this fails that may be ok, depending on your template)");
read_toml_from_file(&(template_base_dir.clone()+"extra.toml"))
},
},
};
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,
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!("Template parsing error(s): {}", e);
println!("{e}");
::std::process::exit(1);
}
};
let templating_engine = TemplatingEngine::new(
tera,
template_extra_config,
);
// Static file directory
let static_file_directory = cli_args.static_location.unwrap_or(
config.server.static_location.clone().unwrap_or(
template_base_dir+"/static"
)
);
let static_file_directory = template_loader.base_dir()+"/static";
println!("Static files will be served from: {static_file_directory}");

View File

@ -20,6 +20,14 @@ use crate::mycelium::MycQuerySettings;
/* The engine itself */
/// A constructable version of MycEngine without the types attached.
///
/// Can be converted to a MycEngine using `into()`.
pub struct MycProtoEngine {
pub tera: Tera,
pub template_config: Option<Table>,
}
#[derive(Clone)]
pub struct MycEngine<V, S, F>
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
@ -120,3 +128,19 @@ where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
}
}
impl<V, S, F> From<MycProtoEngine> for MycEngine<V, S, F>
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
fn from(e: MycProtoEngine) -> Self {
Self::new(e.tera, e.template_config)
}
}
impl<V, S, F> From<MycEngine<V, S, F>> for MycProtoEngine
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
fn from(e: MycEngine<V, S, F>) -> Self {
Self {
tera: e.tera,
template_config: e.template_config,
}
}
}

View File

@ -1,11 +1,17 @@
mod engine;
mod format;
mod query_settings;
mod template_loader;
mod toml_helper;
mod view;
pub use self::engine::MycEngine;
pub use self::engine::MycProtoEngine;
pub use self::format::HtmlTextJsonFormat;
pub use self::format::MycFormat;
pub use self::format::MycFormatFamily;
pub use self::query_settings::MycQuerySettings;
pub use self::template_loader::TemplateEngineLoader;
pub use self::toml_helper::read_toml_from_file;
pub use self::toml_helper::TomlError;
pub use self::view::MycView;

View File

@ -0,0 +1,114 @@
use tera::Tera;
use serde::Deserialize;
use std::fmt;
use crate::mycelium::MycProtoEngine;
use crate::mycelium::read_toml_from_file;
use crate::mycelium::TomlError;
#[derive(Deserialize, Clone)]
pub struct TemplateEngineLoader {
pub template_location: String,
pub extra_config_location: Option<String>,
}
impl TemplateEngineLoader {
pub fn new(
template_location: String,
extra_config_location: Option<String>
) -> Self {
Self {
template_location: template_location,
extra_config_location: extra_config_location,
}
}
pub fn cli_template_location(mut self, location: Option<String>) -> Self {
if let Some(location) = location {
self.template_location = location;
}
self
}
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()+"/"
}
}
pub fn load_templates(
&self
) -> Result<MycProtoEngine,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(MycProtoEngine {
tera: tera,
template_config: template_extra_config,
})
}
}
pub enum TemplateEngineLoaderError {
TomlError(TomlError),
TemplateParseError{path: String, 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(Depending on you template the file not being found might be okay)\n{e}"),
Self::TemplateParseError{path, tera_error} =>
write!(f,"Error parsing template '{path}':\n{tera_error}"),
}
}
}

View File

@ -0,0 +1,42 @@
use serde::Deserialize;
use std::fmt;
use std::fs;
pub fn read_toml_from_file<T>(path: &String) -> Result<T,TomlError>
where T: for<'de> Deserialize<'de> {
let text = match fs::read_to_string(path) {
Ok(t) => t,
Err(e) => {
return Err(TomlError::FileError{
path: path.clone(),
io_error: e
});
}
};
match toml::from_str(&text) {
Ok(t) => Ok(t),
Err(e) => {
return Err(TomlError::ParseError{
path: path.clone(),
toml_error: e
});
}
}
}
pub enum TomlError {
FileError{ path: String, io_error: std::io::Error },
ParseError{ path: String, toml_error: toml::de::Error },
}
impl fmt::Display for TomlError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::FileError{path, io_error} =>
write!(f,"Error while reading file '{path}': {io_error}"),
Self::ParseError{path, toml_error} =>
write!(f,"Unable to parse file as toml '{path}':\n{toml_error}"),
}
}
}