Skip to main content

oxidize_pdf/templates/
error.rs

1use std::fmt;
2
3/// Template-specific errors
4#[derive(Debug, Clone)]
5pub enum TemplateError {
6    /// Variable not found in context
7    VariableNotFound(String),
8    /// Invalid placeholder syntax
9    InvalidPlaceholder(String),
10    /// Circular reference in variables
11    CircularReference(String),
12    /// Invalid variable name
13    InvalidVariableName(String),
14    /// Template parsing error
15    ParseError(String),
16    /// Rendering error
17    RenderError(String),
18}
19
20impl fmt::Display for TemplateError {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            Self::VariableNotFound(var) => {
24                write!(f, "Variable '{}' not found in template context", var)
25            }
26            Self::InvalidPlaceholder(placeholder) => {
27                write!(f, "Invalid placeholder syntax: '{}'", placeholder)
28            }
29            Self::CircularReference(var) => {
30                write!(f, "Circular reference detected for variable '{}'", var)
31            }
32            Self::InvalidVariableName(name) => {
33                write!(f, "Invalid variable name: '{}' (must contain only alphanumeric characters, underscores, and dots)", name)
34            }
35            Self::ParseError(msg) => {
36                write!(f, "Template parsing error: {}", msg)
37            }
38            Self::RenderError(msg) => {
39                write!(f, "Template rendering error: {}", msg)
40            }
41        }
42    }
43}
44
45impl std::error::Error for TemplateError {}
46
47/// Result type for template operations
48pub type TemplateResult<T> = std::result::Result<T, TemplateError>;
49
50impl From<regex::Error> for TemplateError {
51    fn from(err: regex::Error) -> Self {
52        TemplateError::ParseError(format!("Regex error: {}", err))
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_error_display() {
62        let err = TemplateError::VariableNotFound("name".to_string());
63        assert_eq!(
64            format!("{}", err),
65            "Variable 'name' not found in template context"
66        );
67
68        let err = TemplateError::InvalidPlaceholder("{{invalid}}".to_string());
69        assert!(format!("{}", err).contains("Invalid placeholder syntax"));
70    }
71
72    #[test]
73    fn test_error_debug() {
74        let err = TemplateError::CircularReference("var1".to_string());
75        assert!(format!("{:?}", err).contains("CircularReference"));
76    }
77
78    #[test]
79    fn test_variable_not_found_display() {
80        let err = TemplateError::VariableNotFound("username".to_string());
81        let msg = format!("{}", err);
82        assert!(msg.contains("username"));
83        assert!(msg.contains("not found"));
84    }
85
86    #[test]
87    fn test_invalid_placeholder_display() {
88        let err = TemplateError::InvalidPlaceholder("{{bad syntax".to_string());
89        let msg = format!("{}", err);
90        assert!(msg.contains("Invalid placeholder syntax"));
91        assert!(msg.contains("{{bad syntax"));
92    }
93
94    #[test]
95    fn test_circular_reference_display() {
96        let err = TemplateError::CircularReference("var_a".to_string());
97        let msg = format!("{}", err);
98        assert!(msg.contains("Circular reference"));
99        assert!(msg.contains("var_a"));
100    }
101
102    #[test]
103    fn test_invalid_variable_name_display() {
104        let err = TemplateError::InvalidVariableName("123invalid".to_string());
105        let msg = format!("{}", err);
106        assert!(msg.contains("Invalid variable name"));
107        assert!(msg.contains("123invalid"));
108        assert!(msg.contains("alphanumeric"));
109    }
110
111    #[test]
112    fn test_parse_error_display() {
113        let err = TemplateError::ParseError("Unexpected token".to_string());
114        let msg = format!("{}", err);
115        assert!(msg.contains("Template parsing error"));
116        assert!(msg.contains("Unexpected token"));
117    }
118
119    #[test]
120    fn test_render_error_display() {
121        let err = TemplateError::RenderError("Failed to render".to_string());
122        let msg = format!("{}", err);
123        assert!(msg.contains("Template rendering error"));
124        assert!(msg.contains("Failed to render"));
125    }
126
127    #[test]
128    fn test_error_clone() {
129        let err1 = TemplateError::VariableNotFound("test".to_string());
130        let err2 = err1.clone();
131        assert_eq!(format!("{}", err1), format!("{}", err2));
132    }
133
134    #[test]
135    fn test_from_regex_error() {
136        // Create an invalid regex to get a regex::Error
137        let regex_result = regex::Regex::new("[invalid(");
138        if let Err(regex_err) = regex_result {
139            let template_err: TemplateError = regex_err.into();
140            let msg = format!("{}", template_err);
141            assert!(msg.contains("Regex error"));
142        }
143    }
144
145    #[test]
146    fn test_template_result_ok() {
147        fn returns_ok() -> TemplateResult<i32> {
148            Ok(42)
149        }
150        assert_eq!(returns_ok().unwrap(), 42);
151    }
152
153    #[test]
154    fn test_template_result_err() {
155        fn returns_err() -> TemplateResult<i32> {
156            Err(TemplateError::ParseError("test".to_string()))
157        }
158        assert!(returns_err().is_err());
159    }
160
161    #[test]
162    fn test_error_is_std_error() {
163        // Verify it implements std::error::Error
164        fn assert_error<T: std::error::Error>(_: &T) {}
165        let err = TemplateError::ParseError("test".to_string());
166        assert_error(&err);
167    }
168
169    #[test]
170    fn test_all_variants_debug() {
171        // Test Debug impl for all variants
172        let variants: Vec<TemplateError> = vec![
173            TemplateError::VariableNotFound("v".to_string()),
174            TemplateError::InvalidPlaceholder("p".to_string()),
175            TemplateError::CircularReference("c".to_string()),
176            TemplateError::InvalidVariableName("n".to_string()),
177            TemplateError::ParseError("pe".to_string()),
178            TemplateError::RenderError("re".to_string()),
179        ];
180
181        for err in variants {
182            let debug_str = format!("{:?}", err);
183            assert!(!debug_str.is_empty());
184        }
185    }
186
187    #[test]
188    fn test_empty_string_errors() {
189        // Test with empty strings
190        let err = TemplateError::VariableNotFound(String::new());
191        assert!(format!("{}", err).contains("''"));
192
193        let err = TemplateError::ParseError(String::new());
194        assert!(format!("{}", err).contains("Template parsing error:"));
195    }
196
197    #[test]
198    fn test_unicode_in_errors() {
199        let err = TemplateError::VariableNotFound("变量名".to_string());
200        assert!(format!("{}", err).contains("变量名"));
201
202        let err = TemplateError::RenderError("🎯 Error".to_string());
203        assert!(format!("{}", err).contains("🎯"));
204    }
205}