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
use chrono::{Duration, NaiveDate};
use log::debug;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::PathBuf;
use tera::{from_value, to_value, Error, Tera, Value};
use walkdir::WalkDir;
use crate::utils::is_dir;
const DATE_FORMAT: &str = "%Y-%m-%d";
pub fn get_tera(target_path: PathBuf, working_dir: PathBuf) -> anyhow::Result<Tera> {
let is_dir = is_dir(&target_path);
let working_dir_str = working_dir.to_str().expect("could not get working dir str");
let prefix = format!("{}/", working_dir_str);
let mut tera = Tera::default();
let templates = WalkDir::new(&working_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter_map(|e| {
if e.path().extension() == Some(OsStr::new("sql")) {
Some(e)
} else {
None
}
})
.map(|e| {
let template_path = e.path().display().to_string();
let template_name = template_path.trim_start_matches(&prefix).to_string();
(template_path, Some(template_name))
})
.collect::<Vec<_>>();
debug!("Loaded: {:?}", templates);
tera.add_template_files(templates)?;
if !is_dir {
let template_path = target_path.display().to_string();
let template_name = target_path.file_name().expect("could not get file name");
tera.add_template_file(template_path, template_name.to_str())?;
}
tera.register_function("date_range", date_range);
Ok(tera)
}
fn get_native_date(key: &str, input: Option<&Value>) -> tera::Result<NaiveDate> {
if input.is_none() {
return Err(Error::msg(format!(
"Function `date_range` was called without a `{key}` argument",
)));
}
let input = input.unwrap();
match from_value::<String>(input.clone()) {
Ok(val) => match NaiveDate::parse_from_str(&val, DATE_FORMAT) {
Ok(v) => Ok(v),
Err(_) => {
Err(Error::msg(format!(
"Function `date_range` received {key}={input} but `{key}` is invalid format ({DATE_FORMAT})",
)))
}
},
Err(_) => Err(Error::msg("Function `date_range` received {key}={input} but it is not a string")),
}
}
pub fn date_range(args: &HashMap<String, Value>) -> tera::Result<Value> {
let start = get_native_date("start", args.get("start")).unwrap();
let end = get_native_date("end", args.get("end")).unwrap();
let mut items = vec![];
let mut cursor = start;
while cursor < end {
items.push(cursor.format(DATE_FORMAT).to_string());
cursor += Duration::days(1);
}
Ok(to_value(items).unwrap())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_date_range() {
let mut args = HashMap::new();
args.insert("start".to_string(), to_value("2022-01-29").unwrap());
args.insert("end".to_string(), to_value("2022-02-02").unwrap());
let res = date_range(&args).unwrap();
assert_eq!(
res,
to_value(vec!["2022-01-29", "2022-01-30", "2022-01-31", "2022-02-01"]).unwrap()
);
}
}