use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum EngineError {
ParseError { source: String, message: String },
ComponentNotFound(String),
RenderError(String),
ActionNotFound(String),
StateError(String),
ExpressionError(String),
}
impl fmt::Display for EngineError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EngineError::ParseError { source, message } => {
write!(f, "Parse error in '{}': {}", truncate(source, 60), message)
}
EngineError::ComponentNotFound(name) => {
write!(f, "Component not found: {}", name)
}
EngineError::RenderError(msg) => {
write!(f, "Render error: {}", msg)
}
EngineError::ActionNotFound(name) => {
write!(f, "No handler registered for action: {}", name)
}
EngineError::StateError(msg) => {
write!(f, "State error: {}", msg)
}
EngineError::ExpressionError(msg) => {
write!(f, "Expression error: {}", msg)
}
}
}
}
impl std::error::Error for EngineError {}
impl From<String> for EngineError {
fn from(msg: String) -> Self {
EngineError::RenderError(msg)
}
}
fn truncate(s: &str, max_len: usize) -> &str {
if s.len() <= max_len {
s
} else {
let mut end = max_len;
while !s.is_char_boundary(end) && end > 0 {
end -= 1;
}
&s[..end]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_error_display() {
let err = EngineError::ParseError {
source: "Column { broken".to_string(),
message: "unexpected end of input".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("Parse error"));
assert!(msg.contains("Column { broken"));
assert!(msg.contains("unexpected end of input"));
}
#[test]
fn test_component_not_found_display() {
let err = EngineError::ComponentNotFound("MyWidget".to_string());
assert_eq!(err.to_string(), "Component not found: MyWidget");
}
#[test]
fn test_action_not_found_display() {
let err = EngineError::ActionNotFound("submitForm".to_string());
assert_eq!(
err.to_string(),
"No handler registered for action: submitForm"
);
}
#[test]
fn test_render_error_display() {
let err = EngineError::RenderError("node tree corrupted".to_string());
assert_eq!(err.to_string(), "Render error: node tree corrupted");
}
#[test]
fn test_state_error_display() {
let err = EngineError::StateError("invalid JSON".to_string());
assert_eq!(err.to_string(), "State error: invalid JSON");
}
#[test]
fn test_expression_error_display() {
let err = EngineError::ExpressionError("division by zero".to_string());
assert_eq!(err.to_string(), "Expression error: division by zero");
}
#[test]
fn test_error_is_clone_and_eq() {
let err1 = EngineError::ActionNotFound("test".to_string());
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn test_error_debug() {
let err = EngineError::ComponentNotFound("Foo".to_string());
let debug = format!("{:?}", err);
assert!(debug.contains("ComponentNotFound"));
assert!(debug.contains("Foo"));
}
#[test]
fn test_from_string() {
let err: EngineError = "something went wrong".to_string().into();
assert_eq!(
err,
EngineError::RenderError("something went wrong".to_string())
);
}
#[test]
fn test_truncate_long_source() {
let long_source = "a".repeat(200);
let err = EngineError::ParseError {
source: long_source,
message: "error".to_string(),
};
let display = err.to_string();
assert!(display.len() < 200);
}
#[test]
fn test_error_implements_std_error() {
let err = EngineError::StateError("test".to_string());
let std_err: &dyn std::error::Error = &err;
assert!(std_err.to_string().contains("State error"));
}
}