aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assets.rs2
-rw-r--r--src/controller/balance.rs21
-rw-r--r--src/controller/categories.rs40
-rw-r--r--src/controller/error.rs14
-rw-r--r--src/controller/incomes.rs56
-rw-r--r--src/controller/login.rs21
-rw-r--r--src/controller/payments.rs62
-rw-r--r--src/controller/statistics.rs15
-rw-r--r--src/controller/utils.rs39
-rw-r--r--src/controller/wallet.rs3
-rw-r--r--src/db/jobs.rs4
-rw-r--r--src/db/payments.rs6
-rw-r--r--src/jobs/mod.rs7
-rw-r--r--src/jobs/weekly_report.rs27
-rw-r--r--src/mail.rs4
-rw-r--r--src/main.rs3
-rw-r--r--src/routes.rs3
-rw-r--r--src/templates.rs164
18 files changed, 283 insertions, 208 deletions
diff --git a/src/assets.rs b/src/assets.rs
index 36fab55..fdfbfdd 100644
--- a/src/assets.rs
+++ b/src/assets.rs
@@ -1,8 +1,8 @@
use sha2::{Digest, Sha256};
use std::collections::HashMap;
+use std::fmt::Write;
use std::fs;
use std::iter::FromIterator;
-use std::fmt::Write;
pub fn get() -> HashMap<String, String> {
let paths = fs::read_dir("assets").unwrap().map(|e| {
diff --git a/src/controller/balance.rs b/src/controller/balance.rs
index c5d9d4a..efe5af6 100644
--- a/src/controller/balance.rs
+++ b/src/controller/balance.rs
@@ -2,7 +2,6 @@ use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -31,18 +30,16 @@ pub async fn get(wallet: &Wallet) -> Response<Full<Bytes>> {
let exceeding_payers =
payer::exceeding(&users, &user_incomes, &user_payments);
- let mut context = Context::new();
- context.insert("header", &templates::Header::Balance);
- context.insert("connected_user", &wallet.user);
- context.insert(
- "incomes_from",
- &incomes_from.map(|d| d.format("%d/%m/%Y").to_string()),
+ let context = minijinja::context!(
+ header => templates::Header::Balance,
+ connected_user => wallet.user,
+ incomes_from => incomes_from.map(|d| d.format("%d/%m/%Y").to_string()),
+ total_income => total_income,
+ user_incomes => template_user_incomes,
+ total_payments => total_payments,
+ user_payments => template_user_payments,
+ exceeding_payers => exceeding_payers
);
- context.insert("total_income", &total_income);
- context.insert("user_incomes", &template_user_incomes);
- context.insert("total_payments", &total_payments);
- context.insert("user_payments", &template_user_payments);
- context.insert("exceeding_payers", &exceeding_payers);
utils::template(&wallet.assets, &wallet.templates, "balance.html", context)
}
diff --git a/src/controller/categories.rs b/src/controller/categories.rs
index ff2d8e7..fbbd309 100644
--- a/src/controller/categories.rs
+++ b/src/controller/categories.rs
@@ -2,7 +2,6 @@ use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -17,11 +16,12 @@ pub async fn table(
) -> Response<Full<Bytes>> {
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Categories);
- context.insert("connected_user", &wallet.user);
- context.insert("categories", &categories);
- context.insert("highlight", &query.highlight);
+ let context = minijinja::context!(
+ header => templates::Header::Categories,
+ connected_user => wallet.user,
+ categories => categories,
+ highlight => query.highlight
+ );
utils::template(
&wallet.assets,
@@ -40,11 +40,12 @@ async fn create_form_feedback(
form: HashMap<String, String>,
error: Option<String>,
) -> Response<Full<Bytes>> {
- let mut context = Context::new();
- context.insert("header", &templates::Header::Categories);
- context.insert("connected_user", &wallet.user.clone());
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => &templates::Header::Categories,
+ connected_user => &wallet.user.clone(),
+ form => &form,
+ error => &error
+ );
utils::template(
&wallet.assets,
@@ -89,14 +90,15 @@ async fn update_form_feedback(
let is_category_used =
db::payments::is_category_used(&wallet.pool, id).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Categories);
- context.insert("connected_user", &wallet.user);
- context.insert("id", &id);
- context.insert("category", &category);
- context.insert("is_category_used", &is_category_used);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Categories,
+ connected_user => wallet.user,
+ id => id,
+ category => category,
+ is_category_used => is_category_used,
+ form => form,
+ error => error
+ );
utils::template(
&wallet.assets,
diff --git a/src/controller/error.rs b/src/controller/error.rs
index 0f6dcc1..fb1375c 100644
--- a/src/controller/error.rs
+++ b/src/controller/error.rs
@@ -3,7 +3,6 @@ use hyper::body::Bytes;
use hyper::header::CACHE_CONTROL;
use hyper::Response;
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -24,14 +23,15 @@ pub fn error(
pub fn template(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
title: &str,
message: &str,
) -> String {
- let mut context = Context::new();
- context.insert("title", title);
- context.insert("message", message);
- context.insert("assets", assets);
+ let context = minijinja::context!(
+ title => title,
+ message => message,
+ assets => assets
+ );
- templates.render("error.html", &context).unwrap()
+ templates.render_str("error.html", &context).unwrap()
}
diff --git a/src/controller/incomes.rs b/src/controller/incomes.rs
index f22098b..ac3a332 100644
--- a/src/controller/incomes.rs
+++ b/src/controller/incomes.rs
@@ -4,7 +4,6 @@ use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -24,13 +23,14 @@ pub async fn table(
let incomes = db::incomes::list(&wallet.pool, page, PER_PAGE).await;
let max_page = (count as f32 / PER_PAGE as f32).ceil() as i64;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Incomes);
- context.insert("connected_user", &wallet.user);
- context.insert("incomes", &incomes);
- context.insert("page", &page);
- context.insert("max_page", &max_page);
- context.insert("highlight", &query.highlight);
+ let context = minijinja::context!(
+ header => templates::Header::Incomes,
+ connected_user => wallet.user,
+ incomes => incomes,
+ page => page,
+ max_page => max_page,
+ highlight => query.highlight
+ );
utils::template(
&wallet.assets,
@@ -70,15 +70,16 @@ async fn create_form_feedback(
) -> Response<Full<Bytes>> {
let users = db::users::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Incomes);
- context.insert("connected_user", &wallet.user);
- context.insert("users", &users);
- context.insert("query", &query);
- context.insert("current_month", &Utc::now().date_naive().month());
- context.insert("months", &MONTHS);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Incomes,
+ connected_user => wallet.user,
+ users => users,
+ query => query,
+ current_month => Utc::now().date_naive().month(),
+ months => MONTHS,
+ form => form,
+ error => error,
+ );
utils::template(
&wallet.assets,
@@ -144,16 +145,17 @@ async fn update_form_feedback(
let users = db::users::list(&wallet.pool).await;
let income = db::incomes::get(&wallet.pool, id).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Incomes);
- context.insert("connected_user", &wallet.user);
- context.insert("users", &users);
- context.insert("id", &id);
- context.insert("income", &income);
- context.insert("query", &query);
- context.insert("months", &MONTHS);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => &templates::Header::Incomes,
+ connected_user => &wallet.user,
+ users => &users,
+ id => &id,
+ income => &income,
+ query => &query,
+ months => &MONTHS,
+ form => &form,
+ error => &error
+ );
utils::template(
&wallet.assets,
diff --git a/src/controller/login.rs b/src/controller/login.rs
index a1bf466..31370cc 100644
--- a/src/controller/login.rs
+++ b/src/controller/login.rs
@@ -5,7 +5,6 @@ use hyper::header::SET_COOKIE;
use hyper::Response;
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::controller::utils::with_headers;
use crate::controller::wallet::Wallet;
@@ -18,14 +17,15 @@ use crate::validation;
pub async fn page(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
error: Option<&str>,
) -> Response<Full<Bytes>> {
let connected_user: Option<User> = None;
- let mut context = Context::new();
- context.insert("connected_user", &connected_user);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ connected_user => &connected_user,
+ error => &error
+ );
utils::template(assets, templates, "login.html", context)
}
@@ -33,7 +33,7 @@ pub async fn page(
pub async fn login(
config: &Config,
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
form: HashMap<String, String>,
pool: SqlitePool,
) -> Response<Full<Bytes>> {
@@ -75,7 +75,10 @@ pub async fn login(
}
Ok(false) => not_authorized(assets, templates).await,
Err(err) => {
- log::error!("Error verifying bcrypt password: {:?}", err);
+ log::error!(
+ "Error verifying bcrypt password: {:?}",
+ err
+ );
server_error(assets, templates, "Erreur serveur").await
}
},
@@ -88,7 +91,7 @@ pub async fn login(
async fn server_error(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
msg: &str,
) -> Response<Full<Bytes>> {
page(assets, templates, Some(msg)).await
@@ -96,7 +99,7 @@ async fn server_error(
async fn not_authorized(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
) -> Response<Full<Bytes>> {
page(
assets,
diff --git a/src/controller/payments.rs b/src/controller/payments.rs
index 8184015..b5c0256 100644
--- a/src/controller/payments.rs
+++ b/src/controller/payments.rs
@@ -3,7 +3,6 @@ use hyper::body::Bytes;
use hyper::header::CONTENT_TYPE;
use hyper::Response;
use std::collections::HashMap;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -27,17 +26,18 @@ pub async fn table(
let users = db::users::list(&wallet.pool).await;
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Payments);
- context.insert("connected_user", &wallet.user);
- context.insert("payments", &payments);
- context.insert("page", &page);
- context.insert("max_page", &max_page);
- context.insert("query", &query);
- context.insert("count", &count.count);
- context.insert("total_cost", &count.total_cost);
- context.insert("users", &users);
- context.insert("categories", &categories);
+ let context = minijinja::context!(
+ header => templates::Header::Payments,
+ connected_user => wallet.user,
+ payments => payments,
+ page => page,
+ max_page => max_page,
+ query => query,
+ count => count.count,
+ total_cost => count.total_cost,
+ users => users,
+ categories => categories
+ );
utils::template(
&wallet.assets,
@@ -63,14 +63,15 @@ async fn create_form_feedback(
let users = db::users::list(&wallet.pool).await;
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Payments);
- context.insert("connected_user", &wallet.user);
- context.insert("users", &users);
- context.insert("categories", &categories);
- context.insert("query", &query);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Payments,
+ connected_user => wallet.user,
+ users => users,
+ categories => categories,
+ query => query,
+ form => form,
+ error => error
+ );
utils::template(
&wallet.assets,
@@ -142,16 +143,17 @@ async fn update_form_feedback(
let users = db::users::list(&wallet.pool).await;
let categories = db::categories::list(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Payments);
- context.insert("connected_user", &wallet.user);
- context.insert("id", &id);
- context.insert("payment", &payment);
- context.insert("users", &users);
- context.insert("categories", &categories);
- context.insert("query", &query);
- context.insert("form", &form);
- context.insert("error", &error);
+ let context = minijinja::context!(
+ header => templates::Header::Payments,
+ connected_user => wallet.user,
+ id => id,
+ payment => payment,
+ users => users,
+ categories => categories,
+ query => query,
+ form => form,
+ error => error
+ );
utils::template(
&wallet.assets,
diff --git a/src/controller/statistics.rs b/src/controller/statistics.rs
index eb1e704..e57e2be 100644
--- a/src/controller/statistics.rs
+++ b/src/controller/statistics.rs
@@ -1,7 +1,6 @@
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
-use tera::Context;
use crate::controller::utils;
use crate::controller::wallet::Wallet;
@@ -13,15 +12,13 @@ pub async fn get(wallet: &Wallet) -> Response<Full<Bytes>> {
let payments = db::payments::list_for_stats(&wallet.pool).await;
let incomes = db::incomes::total_each_month(&wallet.pool).await;
- let mut context = Context::new();
- context.insert("header", &templates::Header::Statistics);
- context.insert("connected_user", &wallet.user);
- context.insert(
- "json_categories",
- &serde_json::to_string(&categories).unwrap(),
+ let context = minijinja::context!(
+ header => templates::Header::Statistics,
+ connected_user => wallet.user,
+ json_categories => serde_json::to_string(&categories).unwrap(),
+ json_payments => serde_json::to_string(&payments).unwrap(),
+ json_incomes => serde_json::to_string(&incomes).unwrap()
);
- context.insert("json_payments", &serde_json::to_string(&payments).unwrap());
- context.insert("json_incomes", &serde_json::to_string(&incomes).unwrap());
utils::template(
&wallet.assets,
diff --git a/src/controller/utils.rs b/src/controller/utils.rs
index 1b58c68..340a5c7 100644
--- a/src/controller/utils.rs
+++ b/src/controller/utils.rs
@@ -5,7 +5,6 @@ use hyper::header::{
};
use hyper::{Response, StatusCode};
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::controller::error;
@@ -23,29 +22,45 @@ pub fn with_headers(
pub fn template(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
path: &str,
- context: Context,
+ context: minijinja::Value,
) -> Response<Full<Bytes>> {
- let mut context = context;
- context.insert("assets", assets);
+ let context = minijinja::context! { ..context, ..minijinja::context! {
+ assets => assets
+ }};
- match templates.render(path, &context) {
+ match render_template(templates, path, context) {
Ok(template) => with_headers(
Response::new(template.into()),
vec![(CONTENT_TYPE, "text/html"), (CACHE_CONTROL, "no-cache")],
),
- Err(err) => server_error(
- assets,
- templates,
- &format!("Erreur lors de la préparation de la page : {:?}", err),
- ),
+ Err(err) => {
+ log::error!("ERROR template rendering {}\n{:?}", path, err);
+ server_error(
+ assets,
+ templates,
+ &format!(
+ "Erreur lors de la préparation de la page : {:?}",
+ err
+ ),
+ )
+ }
}
}
+fn render_template(
+ templates: &minijinja::Environment<'_>,
+ name: &str,
+ context: minijinja::Value,
+) -> Result<String, minijinja::Error> {
+ let template = templates.get_template(name)?;
+ template.render(context)
+}
+
fn server_error(
assets: &HashMap<String, String>,
- templates: &Tera,
+ templates: &minijinja::Environment<'_>,
msg: &str,
) -> Response<Full<Bytes>> {
with_headers(
diff --git a/src/controller/wallet.rs b/src/controller/wallet.rs
index 2a4a593..7537406 100644
--- a/src/controller/wallet.rs
+++ b/src/controller/wallet.rs
@@ -1,6 +1,5 @@
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
-use tera::Tera;
use crate::model::user::User;
@@ -8,6 +7,6 @@ use crate::model::user::User;
pub struct Wallet {
pub pool: SqlitePool,
pub assets: HashMap<String, String>,
- pub templates: Tera,
+ pub templates: minijinja::Environment<'static>,
pub user: User,
}
diff --git a/src/db/jobs.rs b/src/db/jobs.rs
index a80ef68..7d9386a 100644
--- a/src/db/jobs.rs
+++ b/src/db/jobs.rs
@@ -48,6 +48,8 @@ WHERE
match res {
Ok(_) => (),
- Err(err) => log::error!("Error actualizing job last execution: {:?}", err),
+ Err(err) => {
+ log::error!("Error actualizing job last execution: {:?}", err)
+ }
}
}
diff --git a/src/db/payments.rs b/src/db/payments.rs
index b415a28..25b10f4 100644
--- a/src/db/payments.rs
+++ b/src/db/payments.rs
@@ -495,7 +495,8 @@ ORDER BY
Err(err) => {
log::error!(
"Error looking for the category of {}: {:?}",
- payment_name, err
+ payment_name,
+ err
);
None
}
@@ -523,7 +524,8 @@ LIMIT
Err(err) => {
log::error!(
"Error looking if category {} is used: {:?}",
- category_id, err
+ category_id,
+ err
);
false
}
diff --git a/src/jobs/mod.rs b/src/jobs/mod.rs
index 17df58c..a718d93 100644
--- a/src/jobs/mod.rs
+++ b/src/jobs/mod.rs
@@ -1,14 +1,17 @@
mod weekly_report;
use sqlx::sqlite::SqlitePool;
-use tera::Tera;
use tokio::time::{sleep, Duration};
use crate::db;
use crate::model::config::Config;
use crate::model::job::Job;
-pub async fn start(config: Config, pool: SqlitePool, templates: Tera) {
+pub async fn start(
+ config: Config,
+ pool: SqlitePool,
+ templates: minijinja::Environment<'_>,
+) {
loop {
if db::jobs::should_run(&pool, Job::WeeklyReport).await {
log::info!("Starting weekly report job");
diff --git a/src/jobs/weekly_report.rs b/src/jobs/weekly_report.rs
index 0c10143..5058c52 100644
--- a/src/jobs/weekly_report.rs
+++ b/src/jobs/weekly_report.rs
@@ -1,6 +1,5 @@
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
-use tera::{Context, Tera};
use crate::db;
use crate::mail;
@@ -10,9 +9,9 @@ use crate::payer;
pub async fn send(
config: &Config,
pool: &SqlitePool,
- templates: &Tera,
+ env: &minijinja::Environment<'_>,
) -> bool {
- match get_weekly_report(pool, templates).await {
+ match get_weekly_report(pool, env).await {
Ok(report) => {
let users = db::users::list(pool).await;
mail::send(
@@ -30,7 +29,10 @@ pub async fn send(
.await
}
Err(err) => {
- log::error!("Error preparing weekly report from template: {:?}", err);
+ log::error!(
+ "Error preparing weekly report from template: {:?}",
+ err
+ );
false
}
}
@@ -38,8 +40,8 @@ pub async fn send(
async fn get_weekly_report(
pool: &SqlitePool,
- templates: &Tera,
-) -> Result<String, tera::Error> {
+ env: &minijinja::Environment<'_>,
+) -> Result<String, minijinja::Error> {
let users = db::users::list(pool).await;
let incomes_from = db::incomes::defined_for_all(pool).await;
let user_incomes = match incomes_from {
@@ -53,10 +55,11 @@ async fn get_weekly_report(
let last_week_payments = db::payments::last_week(pool).await;
let last_week_incomes = db::incomes::last_week(pool).await;
- let mut context = Context::new();
- context.insert("exceeding_payers", &exceeding_payers);
- context.insert("payments", &last_week_payments);
- context.insert("incomes", &last_week_incomes);
-
- templates.render("report/report.j2", &context)
+ let template = env.get_template("report/report.j2")?;
+ template.render(minijinja::context!(
+ name => "John",
+ exceeding_payers => exceeding_payers,
+ payments => last_week_payments,
+ incomes => last_week_incomes
+ ))
}
diff --git a/src/mail.rs b/src/mail.rs
index c77e2ad..b6db0cd 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -56,7 +56,9 @@ pub async fn send(
true
} else {
match String::from_utf8(output.stderr) {
- Ok(error) => log::error!("Error sending email: {}", error),
+ Ok(error) => {
+ log::error!("Error sending email: {}", error)
+ }
_ => log::error!("Error sending email"),
};
false
diff --git a/src/main.rs b/src/main.rs
index 2b3aebd..5fe8a94 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,7 +22,6 @@ use model::config;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
-
env_logger::init();
let config = config::from_env()
@@ -34,7 +33,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let assets = assets::get();
- let templates = templates::get();
+ let templates = templates::get()?;
tokio::spawn(jobs::start(config.clone(), pool.clone(), templates.clone()));
diff --git a/src/routes.rs b/src/routes.rs
index ae87d39..5f17ca5 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -5,7 +5,6 @@ use serde::Deserialize;
use sqlx::sqlite::SqlitePool;
use std::collections::HashMap;
use std::convert::Infallible;
-use tera::Tera;
use url::form_urlencoded;
use crate::controller;
@@ -20,7 +19,7 @@ pub async fn routes(
config: Config,
pool: SqlitePool,
assets: HashMap<String, String>,
- templates: Tera,
+ templates: minijinja::Environment<'static>,
request: Request<Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
let method = request.method();
diff --git a/src/templates.rs b/src/templates.rs
index 1f86717..c9a750b 100644
--- a/src/templates.rs
+++ b/src/templates.rs
@@ -1,9 +1,5 @@
use serde::Serialize;
-use serde_json::json;
-use serde_json::value::Value;
-use std::collections::HashMap;
-use tera::Tera;
-use tera::{Error, Result};
+use std::fs;
use crate::queries;
@@ -16,71 +12,123 @@ pub enum Header {
Statistics,
}
-pub fn get() -> Tera {
- let mut tera = match Tera::new("templates/**/*") {
- Ok(t) => t,
- Err(e) => {
- log::error!("Parsing error(s): {}", e);
- ::std::process::exit(1);
- }
+pub fn get() -> Result<minijinja::Environment<'static>, String> {
+ let mut env = minijinja::Environment::new();
+ for path in read_files_recursive("templates") {
+ let path = path
+ .to_str()
+ .ok_or("Error getting string of path: {path:?}")?
+ .to_string();
+ let content = fs::read_to_string(&path)
+ .map_err(|_| "Error reading template {path}")?;
+ let path_without_prefix = path
+ .strip_prefix("templates/")
+ .ok_or("Error removing prefix from template path")?
+ .to_string();
+ env.add_template_owned(path_without_prefix, content)
+ .map_err(|_| "Error adding template {path} to environment")?;
+ }
+
+ env.add_function("payments_params", payments_params);
+ env.add_function("pluralize", pluralize);
+ env.add_function("now", now);
+
+ env.add_filter("numeric", numeric);
+ env.add_filter("euros", euros);
+ env.add_filter("round", round);
+ env.add_filter("with_param", with_param);
+ env.add_filter("filter", filter);
+
+ Ok(env)
+}
+
+fn read_files_recursive(
+ path: impl AsRef<std::path::Path>,
+) -> Vec<std::path::PathBuf> {
+ let Ok(entries) = fs::read_dir(path) else {
+ return vec![];
};
- tera.register_function("payments_params", payments_params);
- tera.register_filter("numeric", numeric);
- tera.register_filter("euros", euros);
- tera
+ entries
+ .flatten()
+ .flat_map(|entry| {
+ let Ok(meta) = entry.metadata() else {
+ return vec![];
+ };
+ if meta.is_dir() {
+ return read_files_recursive(entry.path());
+ }
+ if meta.is_file() {
+ return vec![entry.path()];
+ }
+ vec![]
+ })
+ .collect()
}
-fn payments_params(args: &HashMap<String, Value>) -> Result<Value> {
- let q = json!({
- "page": args.get("page"),
- "name": args.get("name"),
- "cost": args.get("cost"),
- "frequency": args.get("frequency"),
- "highlight": args.get("highlight"),
- "user": args.get("user"),
- "category": args.get("category"),
- "start_date": args.get("start_date"),
- "end_date": args.get("end_date"),
- });
-
- match serde_json::from_value(q) {
- Ok(q) => Ok(json!(queries::payments_url(q))),
- Err(msg) => Err(Error::msg(msg)),
+fn payments_params(value: minijinja::Value) -> String {
+ let str = value.to_string().replace("none", "null");
+ match serde_json::from_str(&str) {
+ Ok(q) => queries::payments_url(q),
+ Err(err) => {
+ log::error!("Error parsing payments params {}: {:?}", str, err);
+ "".to_string()
+ }
}
}
-fn euros(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
- match value {
- Value::Number(n) => {
- if let Some(n) = n.as_i64() {
- let str = rgrouped(n.abs().to_string(), 3).join(" ");
- let sign = if n < 0 { "-" } else { "" };
- Ok(json!(format!("{}{} €", sign, str)))
- } else if let Some(n) = n.as_f64() {
- Ok(json!(format!("{} €", n)))
- } else {
- Err(Error::msg("Error parsing number"))
- }
- }
- _ => Err(Error::msg(format!("{:?} should be a number", value))),
+fn now(format: &str) -> String {
+ let date = chrono::Local::now();
+ format!("{}", date.format(format))
+}
+
+fn euros(n: i64) -> String {
+ let str = rgrouped(n.abs().to_string(), 3).join(" ");
+ let sign = if n < 0 { "-" } else { "" };
+ format!("{}{} €", sign, str)
+}
+
+fn numeric(n: i64) -> String {
+ let str = rgrouped(n.abs().to_string(), 3).join(" ");
+ let sign = if n < 0 { "-" } else { "" };
+ format!("{}{}", sign, str)
+}
+
+fn pluralize(n: i32, s: String) -> String {
+ if n > 0 {
+ format!("{s}s")
+ } else {
+ s
+ }
+}
+
+fn round(n: f32) -> i32 {
+ n.round() as i32
+}
+
+fn with_param(url: String, key: String, value: String) -> String {
+ if url.contains("?") {
+ format!("{url}&{key}={value}")
+ } else {
+ format!("{url}?{key}={value}")
}
}
-fn numeric(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
- match value {
- Value::Number(n) => {
- if let Some(n) = n.as_i64() {
- let str = rgrouped(n.abs().to_string(), 3).join(" ");
- let sign = if n < 0 { "-" } else { "" };
- Ok(json!(format!("{}{}", sign, str)))
- } else if let Some(n) = n.as_f64() {
- Ok(json!(format!("{}", n)))
- } else {
- Err(Error::msg("Error parsing number"))
+fn filter(
+ xs: Vec<minijinja::Value>,
+ key: &str,
+ value: String,
+) -> Vec<minijinja::Value> {
+ let mut res = vec![];
+ for x in xs {
+ if let Ok(v) = x.get_attr(key) {
+ if let Some(v) = v.as_str() {
+ if v == value {
+ res.push(x);
+ }
}
}
- _ => Err(Error::msg(format!("{:?} should be a number", value))),
}
+ res
}
fn rgrouped(str: String, n: usize) -> Vec<String> {