actix_admin/
tera_templates.rs

1use std::{collections::HashMap, hash::BuildHasher};
2use tera::Tera;
3use tera::{to_value, try_get_value, Result};
4
5use crate::view_model::{ActixAdminViewModelField, ActixAdminViewModelFieldType};
6
7struct TeraTemplate {
8    // Pages
9    list_html: &'static str,
10    create_or_edit_html: &'static str,
11    base_html: &'static str,
12    head_html: &'static str,
13    index_html: &'static str,
14    loader_html: &'static str,
15    navbar_html: &'static str,
16    not_found_html: &'static str,
17    show_html: &'static str,
18    unauthorized_html: &'static str,
19    // create or edit
20    checkbox_html: &'static str,
21    input_html: &'static str,
22    selectlist_html: &'static str,
23    create_or_edit_inline_html: &'static str,
24    // list
25    list_header_html: &'static str,
26    list_row_html: &'static str,
27    list_filter_html: &'static str,
28}
29
30pub fn get_tera() -> Tera {
31    let mut tera = load_templates();
32
33    tera.register_filter("get_html_input_type", get_html_input_type);
34    tera.register_filter("get_html_input_class", get_html_input_class);
35    tera.register_filter("get_icon", get_icon);
36    tera.register_filter("get_regex_val", get_regex_val);
37    tera.register_filter("shorten", shorten_filter);
38    tera
39}
40
41fn shorten_filter<S: BuildHasher>(
42    value: &tera::Value,
43    args: &HashMap<String, tera::Value, S>,
44) -> Result<tera::Value> {
45    let max_length = args
46        .get("max_length")
47        .and_then(|v| v.as_number())
48        .and_then(|s| s.as_u64());
49    let input = value.as_str().unwrap();
50
51    if let Some(max) = max_length {
52        if input.len() <= max as usize {
53            Ok(tera::Value::String(input.to_string()))
54        } else {
55            Ok(tera::Value::String(
56                input.chars().take(max as usize).collect(),
57            ))
58        }
59    } else {
60        Ok(tera::Value::String(input.to_string()))
61    }
62}
63
64fn get_html_input_class<S: BuildHasher>(
65    value: &tera::Value,
66    _: &HashMap<String, tera::Value, S>,
67) -> Result<tera::Value> {
68    let field = try_get_value!(
69        "get_html_input_class",
70        "value",
71        ActixAdminViewModelField,
72        value
73    );
74    let html_input_type = match field.field_type {
75        ActixAdminViewModelFieldType::TextArea => "textarea",
76        ActixAdminViewModelFieldType::Checkbox => "checkbox",
77        _ => "input",
78    };
79
80    Ok(to_value(html_input_type).unwrap())
81}
82
83fn get_icon<S: BuildHasher>(
84    value: &tera::Value,
85    _: &HashMap<String, tera::Value, S>,
86) -> Result<tera::Value> {
87    let field = try_get_value!("get_icon", "value", String, value);
88    let font_awesome_icon = match field.as_str() {
89        "true" => "<i class=\"fa-solid fa-check\"></i>",
90        "false" => "<i class=\"fa-solid fa-xmark\"></i>",
91        _ => panic!("not implemented icon"),
92    };
93
94    Ok(to_value(font_awesome_icon).unwrap())
95}
96
97fn get_regex_val<S: BuildHasher>(
98    value: &tera::Value,
99    args: &HashMap<String, tera::Value, S>,
100) -> Result<tera::Value> {
101    let field = try_get_value!("get_regex_val", "value", ActixAdminViewModelField, value);
102
103    let s = args.get("values");
104    let field_val = s.unwrap().get(&field.field_name);
105
106    // println!(
107    //     "field {} regex {:?}",
108    //     field.field_name, field.list_regex_mask
109    // );
110    match (field_val, field.list_regex_mask) {
111        (Some(val), Some(r)) => {
112            let val_str = val.to_string();
113            //let is_match = r.is_match(&val_str);
114            //println!("is match: {}, regex {}", is_match, r.to_string());
115            let result_str = r.replace_all(&val_str, "*");
116            return Ok(to_value(result_str).unwrap());
117        }
118        (Some(val), None) => {
119            return Ok(to_value(val).unwrap());
120        }
121        (_, _) => panic!("key {} not found in model values", &field.field_name),
122    }
123}
124
125fn get_html_input_type<S: BuildHasher>(
126    value: &tera::Value,
127    _: &HashMap<String, tera::Value, S>,
128) -> Result<tera::Value> {
129    let field = try_get_value!(
130        "get_html_input_type",
131        "value",
132        ActixAdminViewModelField,
133        value
134    );
135
136    // TODO: convert to option
137    if field.html_input_type != "" {
138        return Ok(to_value(field.html_input_type).unwrap());
139    }
140
141    let html_input_type = match field.field_type {
142        ActixAdminViewModelFieldType::Text => "text",
143        ActixAdminViewModelFieldType::DateTime => "datetime-local",
144        ActixAdminViewModelFieldType::Date => "date",
145        ActixAdminViewModelFieldType::Checkbox => "checkbox",
146        ActixAdminViewModelFieldType::FileUpload => "file",
147        _ => "text",
148    };
149
150    Ok(to_value(html_input_type).unwrap())
151}
152
153fn add_templates_to_tera(tera: &mut Tera, tera_template: TeraTemplate) {
154    let _res = tera.add_raw_templates(vec![
155        ("base.html", tera_template.base_html),
156        ("list.html", tera_template.list_html),
157        ("create_or_edit.html", tera_template.create_or_edit_html),
158        ("head.html", tera_template.head_html),
159        ("index.html", tera_template.index_html),
160        ("loader.html", tera_template.loader_html),
161        ("navbar.html", tera_template.navbar_html),
162        ("not_found.html", tera_template.not_found_html),
163        ("show.html", tera_template.show_html),
164        ("unauthorized.html", tera_template.unauthorized_html),
165        // create or edit
166        ("create_or_edit/checkbox.html", tera_template.checkbox_html),
167        ("create_or_edit/input.html", tera_template.input_html),
168        ("create_or_edit/selectlist.html", tera_template.selectlist_html),
169        ("create_or_edit/inline.html", tera_template.create_or_edit_inline_html),
170        // list
171        ("list/header.html", tera_template.list_header_html),
172        ("list/row.html", tera_template.list_row_html),
173        ("list/filter.html", tera_template.list_filter_html),
174    ]);
175}
176
177#[cfg(all(feature = "bootstrapv5_css", feature = "bulma_css"))]
178compile_error!("feature \"foo\" and feature \"bar\" cannot be enabled at the same time");
179
180// Cargo Features for CSS
181#[cfg(feature = "bulma_css")]
182fn load_templates() -> Tera {
183    let mut tera = Tera::new(concat!(
184        env!("CARGO_MANIFEST_DIR"),
185        "/src/templates/bulma/*.html"
186    ))
187    .unwrap();
188    let tera_template = TeraTemplate {
189        list_html: include_str!("templates/bulma/list.html"),
190        create_or_edit_html: include_str!("templates/bulma/create_or_edit.html"),
191        base_html: include_str!("templates/bulma/base.html"),
192        head_html: include_str!("templates/bulma/head.html"),
193        index_html: include_str!("templates/bulma/index.html"),
194        loader_html: include_str!("templates/bulma/loader.html"),
195        navbar_html: include_str!("templates/bulma/navbar.html"),
196        not_found_html: include_str!("templates/bulma/not_found.html"),
197        show_html: include_str!("templates/bulma/show.html"),
198        unauthorized_html: include_str!("templates/bulma/unauthorized.html"),
199        // create or edit
200        checkbox_html: include_str!("templates/bulma/create_or_edit/checkbox.html"),
201        input_html: include_str!("templates/bulma/create_or_edit/input.html"),
202        selectlist_html: include_str!("templates/bulma/create_or_edit/selectlist.html"),
203        create_or_edit_inline_html: include_str!("templates/bulma/create_or_edit/inline.html"),
204        // list
205        list_header_html: include_str!("templates/bulma/list/header.html"),
206        list_row_html: include_str!("templates/bulma/list/row.html"),
207        list_filter_html: include_str!("templates/bulma/list/filter.html"),
208    };
209
210    add_templates_to_tera(&mut tera, tera_template);
211
212    tera
213}
214
215#[cfg(feature = "bootstrapv5_css")]
216fn load_templates() -> Tera {
217    let mut tera = Tera::new(concat!(
218        env!("CARGO_MANIFEST_DIR"),
219        "/src/templates/bootstrapv5/*.html"
220    ))
221    .unwrap();
222    let tera_template = TeraTemplate {
223        list_html: include_str!("templates/bootstrapv5/list.html"),
224        create_or_edit_html: include_str!("templates/bootstrapv5/create_or_edit.html"),
225        base_html: include_str!("templates/bootstrapv5/base.html"),
226        head_html: include_str!("templates/bootstrapv5/head.html"),
227        index_html: include_str!("templates/bootstrapv5/index.html"),
228        loader_html: include_str!("templates/bootstrapv5/loader.html"),
229        navbar_html: include_str!("templates/bootstrapv5/navbar.html"),
230        not_found_html: include_str!("templates/bootstrapv5/not_found.html"),
231        show_html: include_str!("templates/bootstrapv5/show.html"),
232        unauthorized_html: include_str!("templates/bootstrapv5/unauthorized.html"),
233        // create or edit
234        checkbox_html: include_str!("templates/bootstrapv5/create_or_edit/checkbox.html"),
235        input_html: include_str!("templates/bootstrapv5/create_or_edit/input.html"),
236        selectlist_html: include_str!("templates/bootstrapv5/create_or_edit/selectlist.html"),
237        create_or_edit_inline_html: include_str!("templates/bootstrapv5/create_or_edit/inline.html"),
238        // list
239        list_header_html: include_str!("templates/bootstrapv5/list/header.html"),
240        list_row_html: include_str!("templates/bootstrapv5/list/row.html"),
241        list_filter_html: include_str!("templates/bootstrapv5/list/filter.html"),
242    };
243
244    add_templates_to_tera(&mut tera, tera_template);
245
246    tera
247}