/*! Utility functions for variety of tasks */ // // Copyright 2020, Jesús Pérez Lorenzo use std::collections::HashMap; use std::fs; use std::path::Path; use tera::{Tera}; /// make content from template path: from `root_path` + extension search for ".toml" or ".json" /// template should be `root_path`.html and lang should be included in `root_path` // #[must_use] #[allow(clippy::implicit_hasher)] #[allow(dead_code)] pub fn make_template_content( root_path: &str, ctx: &mut tera::Context, data_hash: &mut HashMap, load_extend: bool, ) -> String { let mut data_path = format!("{}.toml", &root_path); if !Path::new(&data_path).exists() { data_path = format!("{}.json", &root_path); } match hash_from_data(&data_path,ctx,data_hash,load_extend) { Ok(_) => match data_templated( "", format!("{}.html", &root_path).as_str(), ctx, data_hash, ) { Ok(templated_file) => return templated_file, Err(e) => println!( "Error generating template {}.html: {}", &root_path, e ), }, Err(e) => println!( "Error with data template {} - {} : {}", &root_path, &data_path, e ), } "".to_string() } /// `get_json_val` is a simple match Option to remove quotes in json values and returned as a String #[must_use] pub fn get_json_val(json_value: &serde_json::Value, dflt_val: String) -> String { match json_value.as_str() { Some(val) => val.to_string(), None => dflt_val, } } /// `get_toml_val` is a simple match Option to remove quotes in json values and returned as a String #[must_use] pub fn get_toml_val(toml_value: &toml::Value, dflt_val: String) -> String { match toml_value.as_str() { Some(val) => val.to_string(), None => dflt_val, } } /// `get_toml_val` is a simple match Option to remove quotes in json values and returned as a String #[must_use] pub fn get_yaml_val(yaml_value: &serde_yaml::Value, dflt_val: String) -> String { match yaml_value.as_str() { Some(val) => val.to_string(), None => dflt_val, } } /// Load a data file, fill a receive HASH, load template file and applied with Tera /// By file extension is able to load data from JSON or TOML (as a table Value) /// /// Unwrap lines in the following example has to be changed to "matched" or ? with -> Result<> ///```rust /// let mut data_hash: HashMap = HashMap::new(); /// let path = String::from("resources/mail/test.json".to_string()); /// let res = hash_from_data(&data_path, &mut data_hash).unwrap(); /// let root_path = String::from("resources/mail"); /// let tpl_result = data_templated(&root_path,"test.html",&res).unwrap(); /// ``` #[allow(clippy::missing_errors_doc,clippy::implicit_hasher)] // pub fn hash_from_data(path: &str, ctx: &mut tera::Context, data_hash: &mut HashMap, load_extend: bool) -> anyhow::Result<()> { // {HashMap> { let mut check_value = | key: String, v: String | { if let Some(first_char) = v.chars().next() { if first_char == '@' { let field_file = &v[1..]; let _ = hash_from_data(field_file, ctx, data_hash, load_extend); // dbg!("{}",&data_hash); return; } } data_hash.insert(key, v); }; let vars_content = fs::read_to_string(&path)?; //let mut data_hash: HashMap = HashMap::new(); if let Some(extension) = path.split('.').last() { //println!("File {} extension {}", &path, &extension); match extension { "json" => { let data: HashMap = serde_json::from_str(&vars_content)?; if load_extend { match tera::Context::from_serialize(data) { Ok(res) => { ctx.extend(res); }, Err(e) => { println!("Error loading {}: {}",&path, e); }, } } else { for (key, value) in data { let val = get_json_val(&value, "".into()); check_value(key,val); } } } "toml" => { // As a table content let data: toml::Value = toml::from_str(&vars_content)?; // let data = &vars_content.parse::()?; if load_extend { match tera::Context::from_serialize(data) { Ok(res) => { ctx.extend(res); }, Err(e) => { println!("Error loading {}: {}",&path, e); }, } } else if let Some(table_data) = data.as_table() { for (key, value) in table_data { // println!("-{}-{}",&key,&value); let val = get_toml_val(&value, "".into()); check_value(key.into(),val); } } } "yaml" => { let data: HashMap = serde_yaml::from_str(&vars_content)?; if load_extend { match tera::Context::from_serialize(data) { Ok(res) => { ctx.extend(res); }, Err(e) => { println!("Error loading {}: {}",&path, e); }, } } else { for (key, value) in data { let val = get_yaml_val(&value, "".into()); check_value(key,val); } } } _ => { println!( "File {} format extension {} not defined ", &path, &extension ); } } } else { println!("File {} unknown extension", &path); } // dbg!(&data_hash); Ok(()) // data_hash) } /// Data tempalted create a Tera context form hashmap and reads a file template from path/template_name /// Finally render to a String #[allow(clippy::missing_errors_doc, clippy::implicit_hasher)] pub fn data_templated( path: &str, template_name: &str, tpl_context: &mut tera::Context, content: &HashMap, ) -> anyhow::Result { // let mut tpl_context = Context::new(); for (key, value) in content.iter() { tpl_context.insert(key, value); } let mut tera = Tera::default(); let template_head = "header.html"; let tpl = read_path_file(&path, &template_name, "content")?; let mut all_tpls = vec![("data-template.html",tpl)]; match read_path_file(&path, &template_head, "content") { Ok(tpl_head) => all_tpls.push((&template_head,tpl_head)), Err(_) => {} // ignore if no header } tera.add_raw_templates(all_tpls)?; let res = tera.render("data-template.html", &tpl_context)?; Ok(res.replace("/","/")) } /// Read a `file_name` from a path (root / `file_name`) ( "" `file_name_full_path` ) /// format of reading can be matched with `file_type` /// TODO implement other types to read #[allow(clippy::missing_errors_doc)] pub fn read_path_file(path: &str, file_name: &str, file_type: &str) -> anyhow::Result { let mut result = String::from(""); let mut root_path = format!("{}/", path); if root_path.as_str() == "/" { root_path = String::from(""); } //println!("{}",format!("{}{}", &root_path.as_str(), &file_name)); match file_type { "content" => { result = fs::read_to_string(format!("{}{}", &root_path.as_str(), &file_name).as_str())? } _ => println!( "File {}{} type {} not defined ", &root_path, &file_name, &file_type ), } Ok(result) }