Skip to main content

standout_render/
error.rs

1//! Error types for template rendering.
2//!
3//! This module provides [`RenderError`], the primary error type for all rendering
4//! operations. It abstracts over the underlying template engine's errors, providing
5//! a stable public API.
6
7use std::fmt;
8
9/// Error type for template rendering operations.
10///
11/// This error type provides a stable API that doesn't expose implementation details
12/// of the underlying template engine. All public rendering functions return this type.
13#[derive(Debug)]
14pub enum RenderError {
15    /// Template syntax error or compilation failure.
16    TemplateError(String),
17
18    /// Template not found in the registry.
19    TemplateNotFound(String),
20
21    /// Data serialization error.
22    SerializationError(String),
23
24    /// Style validation error (invalid alias, cycle, etc.).
25    StyleError(String),
26
27    /// I/O error (e.g., reading template from disk).
28    IoError(std::io::Error),
29
30    /// Other operational error.
31    OperationError(String),
32
33    /// Error during context resolution or conversion.
34    ContextError(String),
35}
36
37impl fmt::Display for RenderError {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            RenderError::TemplateError(msg) => write!(f, "template error: {}", msg),
41            RenderError::TemplateNotFound(name) => write!(f, "template not found: {}", name),
42            RenderError::SerializationError(msg) => write!(f, "serialization error: {}", msg),
43            RenderError::StyleError(msg) => write!(f, "style error: {}", msg),
44            RenderError::IoError(err) => write!(f, "I/O error: {}", err),
45            RenderError::OperationError(msg) => write!(f, "{}", msg),
46            RenderError::ContextError(msg) => write!(f, "context error: {}", msg),
47        }
48    }
49}
50
51impl std::error::Error for RenderError {
52    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
53        match self {
54            RenderError::IoError(err) => Some(err),
55            _ => None,
56        }
57    }
58}
59
60impl From<std::io::Error> for RenderError {
61    fn from(err: std::io::Error) -> Self {
62        RenderError::IoError(err)
63    }
64}
65
66impl From<serde_json::Error> for RenderError {
67    fn from(err: serde_json::Error) -> Self {
68        RenderError::SerializationError(err.to_string())
69    }
70}
71
72impl From<serde_yaml::Error> for RenderError {
73    fn from(err: serde_yaml::Error) -> Self {
74        RenderError::SerializationError(err.to_string())
75    }
76}
77
78impl From<quick_xml::DeError> for RenderError {
79    fn from(err: quick_xml::DeError) -> Self {
80        RenderError::SerializationError(err.to_string())
81    }
82}
83
84impl From<csv::Error> for RenderError {
85    fn from(err: csv::Error) -> Self {
86        RenderError::SerializationError(err.to_string())
87    }
88}
89
90impl From<csv::IntoInnerError<csv::Writer<Vec<u8>>>> for RenderError {
91    fn from(err: csv::IntoInnerError<csv::Writer<Vec<u8>>>) -> Self {
92        RenderError::SerializationError(err.to_string())
93    }
94}
95
96impl From<std::string::FromUtf8Error> for RenderError {
97    fn from(err: std::string::FromUtf8Error) -> Self {
98        RenderError::SerializationError(err.to_string())
99    }
100}
101
102// Conversion from minijinja::Error - this keeps internal compatibility
103impl From<minijinja::Error> for RenderError {
104    fn from(err: minijinja::Error) -> Self {
105        use minijinja::ErrorKind;
106
107        match err.kind() {
108            ErrorKind::TemplateNotFound => RenderError::TemplateNotFound(err.to_string()),
109            ErrorKind::SyntaxError
110            | ErrorKind::BadEscape
111            | ErrorKind::UndefinedError
112            | ErrorKind::UnknownTest
113            | ErrorKind::UnknownFunction
114            | ErrorKind::UnknownFilter
115            | ErrorKind::UnknownMethod => RenderError::TemplateError(err.to_string()),
116            ErrorKind::BadSerialization => RenderError::SerializationError(err.to_string()),
117            _ => RenderError::OperationError(err.to_string()),
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_error_display() {
128        let err = RenderError::TemplateNotFound("foo".to_string());
129        assert!(err.to_string().contains("template not found"));
130        assert!(err.to_string().contains("foo"));
131    }
132
133    #[test]
134    fn test_from_io_error() {
135        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
136        let render_err: RenderError = io_err.into();
137        assert!(matches!(render_err, RenderError::IoError(_)));
138    }
139
140    #[test]
141    fn test_from_minijinja_template_not_found() {
142        let mj_err = minijinja::Error::new(
143            minijinja::ErrorKind::TemplateNotFound,
144            "template 'foo' not found",
145        );
146        let render_err: RenderError = mj_err.into();
147        assert!(matches!(render_err, RenderError::TemplateNotFound(_)));
148    }
149}