]> git.proxmox.com Git - proxmox.git/blame - proxmox-notify/src/renderer/plaintext.rs
notify: make template rendering helpers more robust
[proxmox.git] / proxmox-notify / src / renderer / plaintext.rs
CommitLineData
7cb339df
WB
1use std::collections::HashMap;
2
48657113
LW
3use handlebars::{
4 Context, Handlebars, Helper, HelperResult, Output, RenderContext,
5 RenderError as HandlebarsRenderError,
6};
7use serde_json::Value;
48657113
LW
8
9use super::{table::Table, value_to_string};
7cb339df
WB
10use crate::define_helper_with_prefix_and_postfix;
11use crate::renderer::BlockRenderFunctions;
48657113
LW
12
13fn optimal_column_widths(table: &Table) -> HashMap<&str, usize> {
14 let mut widths = HashMap::new();
15
16 for column in &table.schema.columns {
17 let mut min_width = column.label.len();
18
19 for row in &table.data {
20 let entry = row.get(&column.id).unwrap_or(&Value::Null);
21
22 let text = if let Some(renderer) = &column.renderer {
d49fc1aa 23 renderer.render(entry)
48657113
LW
24 } else {
25 value_to_string(entry)
26 };
27
28 min_width = std::cmp::max(text.len(), min_width);
29 }
30
31 widths.insert(column.label.as_str(), min_width + 4);
32 }
33
34 widths
35}
36
37fn render_plaintext_table(
38 h: &Helper,
39 _: &Handlebars,
40 _: &Context,
41 _: &mut RenderContext,
42 out: &mut dyn Output,
43) -> HelperResult {
44 let param = h
45 .param(0)
46 .ok_or_else(|| HandlebarsRenderError::new("parameter not found"))?;
47 let value = param.value();
48 let table: Table = serde_json::from_value(value.clone())?;
49 let widths = optimal_column_widths(&table);
50
51 // Write header
52 for column in &table.schema.columns {
53 let width = widths.get(column.label.as_str()).unwrap_or(&0);
54 out.write(&format!("{label:width$}", label = column.label))?;
55 }
56
57 out.write("\n")?;
58
59 // Write individual rows
60 for row in &table.data {
61 for column in &table.schema.columns {
62 let entry = row.get(&column.id).unwrap_or(&Value::Null);
63 let width = widths.get(column.label.as_str()).unwrap_or(&0);
64
65 let text = if let Some(renderer) = &column.renderer {
d49fc1aa 66 renderer.render(entry)
48657113
LW
67 } else {
68 value_to_string(entry)
69 };
70
71 out.write(&format!("{text:width$}",))?;
72 }
73 out.write("\n")?;
74 }
75
76 Ok(())
77}
78
79macro_rules! define_underlining_heading_fn {
80 ($name:ident, $underline:expr) => {
81 fn $name<'reg, 'rc>(
82 h: &Helper<'reg, 'rc>,
83 _handlebars: &'reg Handlebars,
84 _context: &'rc Context,
85 _render_context: &mut RenderContext<'reg, 'rc>,
86 out: &mut dyn Output,
87 ) -> HelperResult {
88 let param = h
89 .param(0)
90 .ok_or_else(|| HandlebarsRenderError::new("No parameter provided"))?;
91
92 let value = param.value();
93 let text = value.as_str().ok_or_else(|| {
94 HandlebarsRenderError::new(format!("value {value} is not a string"))
95 })?;
96
97 out.write(text)?;
98 out.write("\n")?;
99
100 for _ in 0..text.len() {
101 out.write($underline)?;
102 }
103 Ok(())
104 }
105 };
106}
107
108define_helper_with_prefix_and_postfix!(verbatim_monospaced, "", "");
109define_underlining_heading_fn!(heading_1, "=");
110define_underlining_heading_fn!(heading_2, "-");
111define_helper_with_prefix_and_postfix!(verbatim, "", "");
112
113fn render_object(
114 h: &Helper,
115 _: &Handlebars,
116 _: &Context,
117 _: &mut RenderContext,
118 out: &mut dyn Output,
119) -> HelperResult {
120 let param = h
121 .param(0)
122 .ok_or_else(|| HandlebarsRenderError::new("parameter not found"))?;
123
124 let value = param.value();
125
126 out.write("\n")?;
127 out.write(&serde_json::to_string_pretty(&value)?)?;
128 out.write("\n")?;
129
130 Ok(())
131}
132
133pub(super) fn block_render_functions() -> BlockRenderFunctions {
134 BlockRenderFunctions {
135 table: Box::new(render_plaintext_table),
136 verbatim_monospaced: Box::new(verbatim_monospaced),
137 verbatim: Box::new(verbatim),
138 object: Box::new(render_object),
139 heading_1: Box::new(heading_1),
140 heading_2: Box::new(heading_2),
141 }
142}