chore: add code
This commit is contained in:
parent
b3a992cb20
commit
8119bf8c23
33
Cargo.toml
Normal file
33
Cargo.toml
Normal file
@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "rtplfile"
|
||||
version = "0.1.0"
|
||||
authors = ["JesusPerez <jpl@jesusperez.pro>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.0.10", features = ["derive"] }
|
||||
stderrlog = "0.5"
|
||||
structopt = "0.3"
|
||||
anyhow = "1.0.38"
|
||||
faccess = "0.2.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.8.16"
|
||||
|
||||
regex = "1.4.3"
|
||||
toml = "0.5"
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
|
||||
[dependencies.tera]
|
||||
version = "1"
|
||||
default-features = true
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = "z"
|
||||
|
||||
# Uncomment to sacrifice Drop-on-panic cleanup and std::panic::catch_unwind
|
||||
# for ~24K space saving
|
||||
#panic = 'abort'
|
36
rustfmt.toml
Normal file
36
rustfmt.toml
Normal file
@ -0,0 +1,36 @@
|
||||
# Version 0.1
|
||||
|
||||
# Always nice to have a way to gather TODOs and FIXMEs quickly
|
||||
report_todo = "Always"
|
||||
report_fixme = "Always"
|
||||
|
||||
# I was one of the proponents in the RFC discussion
|
||||
use_try_shorthand = true
|
||||
|
||||
# Try to wrestle rustfmt as close as possible to the style I habitually use
|
||||
# and rigorously enforce by using `git gui` to only commit rustfmt changes
|
||||
# I approve of.
|
||||
brace_style = "PreferSameLine"
|
||||
comment_width = 100
|
||||
enum_discrim_align_threshold = 10
|
||||
format_strings = true
|
||||
fn_args_density = "Compressed"
|
||||
fn_single_line = true
|
||||
imports_indent = "Visual"
|
||||
match_block_trailing_comma = true
|
||||
normalize_doc_attributes = true
|
||||
overflow_delimited_expr = true
|
||||
reorder_impl_items = true
|
||||
struct_field_align_threshold = 10
|
||||
use_field_init_shorthand = true
|
||||
use_small_heuristics = "Max"
|
||||
where_single_line = true
|
||||
wrap_comments = false
|
||||
|
||||
# I happen to like /* this */ for multi-line comments, thank you very much
|
||||
normalize_comments = false
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Used for debugging rustfmt configurations
|
||||
#write_mode = "Diff"
|
35
src/main.rs
Normal file
35
src/main.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use clap::Parser;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod tera_lib;
|
||||
use crate::tera_lib::{hash_from_data, data_templated};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Data file
|
||||
#[clap(short, long)]
|
||||
data: String,
|
||||
|
||||
/// Template file
|
||||
#[clap(short, long)]
|
||||
tpl: String,
|
||||
|
||||
/// Number of times to greet
|
||||
#[clap(short, long, default_value_t = 1)]
|
||||
count: u8,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let mut data_hash: HashMap<String, String> = HashMap::new();
|
||||
let mut tpl_context = tera::Context::new();
|
||||
match hash_from_data(&args.data, &mut tpl_context, &mut data_hash, true) {
|
||||
Ok(_) =>
|
||||
match data_templated(&"".to_string(),&args.tpl,&mut tpl_context, &data_hash) {
|
||||
Ok(result) => println!("{}",result),
|
||||
Err(e) => println!("Error with {} data in {} template: {}", args.data, args.tpl, e),
|
||||
},
|
||||
Err(e) => println!("Error with {} data in {} template: {}", args.data, args.tpl, e),
|
||||
}
|
||||
}
|
217
src/tera_lib.rs
Normal file
217
src/tera_lib.rs
Normal file
@ -0,0 +1,217 @@
|
||||
/*! 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<String, String>,
|
||||
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<String, String> = 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<String, String>, load_extend: bool) -> anyhow::Result<()> {
|
||||
// {HashMap<String, String>> {
|
||||
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<String, String> = HashMap::new();
|
||||
if let Some(extension) = path.split('.').last() {
|
||||
//println!("File {} extension {}", &path, &extension);
|
||||
match extension {
|
||||
"json" => {
|
||||
let data: HashMap<String, serde_json::Value> = 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::<toml::Value>()?;
|
||||
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<String, serde_yaml::Value> = 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<String, String>,
|
||||
) -> anyhow::Result<String> {
|
||||
// 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)
|
||||
}
|
||||
/// 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<String> {
|
||||
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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user