1use thiserror::Error;
2
3#[derive(Debug, Error)]
4pub enum RenderError {
5 #[error("template parse failed for `{name}`: {source}")]
6 TemplateParse {
7 name: String,
8 #[source]
9 source: tera::Error,
10 },
11
12 #[error("template not registered: `{name}`")]
13 MissingTemplate { name: String },
14
15 #[error(
16 "template `{name}` references the non-deterministic `now()` function; \
17 nils-markdown rejects templates that depend on wall-clock time"
18 )]
19 NonDeterministicTemplate { name: String },
20
21 #[error("view serialization failed: {source}")]
22 Serialize {
23 #[source]
24 source: serde_json::Error,
25 },
26
27 #[error("tera render failed for `{name}`: {source}")]
28 Render {
29 name: String,
30 #[source]
31 source: tera::Error,
32 },
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 fn assert_send_sync_static<T: Send + Sync + 'static>() {}
40
41 #[test]
42 fn render_error_is_send_sync_static() {
43 assert_send_sync_static::<RenderError>();
44 }
45
46 #[test]
47 fn render_error_round_trips_through_anyhow() {
48 let err = RenderError::MissingTemplate {
49 name: "issue_body".into(),
50 };
51 let any: anyhow::Error = err.into();
52 let printed = format!("{any}");
53 assert!(printed.contains("issue_body"), "{printed}");
54 }
55
56 #[test]
57 fn template_parse_error_renders_name_and_source() {
58 let tera_err = tera::Error::msg("syntax error at line 3");
59 let err = RenderError::TemplateParse {
60 name: "dashboard".into(),
61 source: tera_err,
62 };
63 let printed = format!("{err}");
64 assert!(printed.contains("dashboard"), "{printed}");
65 assert!(printed.contains("syntax error"), "{printed}");
66 }
67
68 #[test]
69 fn missing_template_renders_name() {
70 let err = RenderError::MissingTemplate {
71 name: "absent".into(),
72 };
73 assert_eq!(format!("{err}"), "template not registered: `absent`");
74 }
75
76 #[test]
77 fn non_deterministic_template_renders_name() {
78 let err = RenderError::NonDeterministicTemplate {
79 name: "time_based".into(),
80 };
81 let printed = format!("{err}");
82 assert!(printed.contains("time_based"), "{printed}");
83 assert!(printed.contains("non-deterministic"), "{printed}");
84 }
85
86 #[test]
87 fn serialize_error_renders_source() {
88 let source =
89 serde_json::from_str::<serde_json::Value>("{ not json").expect_err("invalid json");
90 let err = RenderError::Serialize { source };
91 let printed = format!("{err}");
92 assert!(
93 printed.starts_with("view serialization failed"),
94 "{printed}"
95 );
96 }
97
98 #[test]
99 fn render_runtime_error_renders_name() {
100 let err = RenderError::Render {
101 name: "lifecycle".into(),
102 source: tera::Error::msg("missing variable"),
103 };
104 let printed = format!("{err}");
105 assert!(printed.contains("lifecycle"), "{printed}");
106 assert!(printed.contains("missing variable"), "{printed}");
107 }
108}