use thiserror::Error;
#[derive(Error, Debug, Clone, PartialEq)]
pub enum ColorParseError {
#[error("invalid hex color format: {0}")]
InvalidHexFormat(String),
#[error("invalid RGB color format: {0}")]
InvalidRgbFormat(String),
#[error("color component out of range: {0}")]
ComponentOutOfRange(String),
#[error("unknown color name: {0}")]
UnknownColorName(String),
#[error("invalid color specification: {0}")]
InvalidColorSpec(String),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum StyleError {
#[error("invalid style syntax: {0}")]
InvalidSyntax(String),
#[error("unknown style attribute: {0}")]
UnknownAttribute(String),
#[error("missing style definition: {0}")]
MissingStyle(String),
#[error("invalid style combination: {0}")]
InvalidCombination(String),
#[error("style stack error: {0}")]
StackError(String),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum ConsoleError {
#[error("rendering error: {0}")]
RenderError(String),
#[error("object is not renderable: {0}")]
NotRenderable(String),
#[error("invalid markup syntax: {0}")]
MarkupError(String),
#[error("live display error: {0}")]
LiveError(String),
#[error("alternate screen not available: {0}")]
NoAltScreen(String),
#[error("console error: {0}")]
Generic(String),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum SegmentError {
#[error("invalid segment: {0}")]
InvalidSegment(String),
#[error("segment processing error: {0}")]
ProcessingError(String),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum CellError {
#[error("invalid cell width: {0}")]
InvalidWidth(String),
#[error("unicode processing error: {0}")]
UnicodeError(String),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum PaletteError {
#[error("invalid palette index: {0}")]
InvalidIndex(usize),
#[error("palette not available: {0}")]
NotAvailable(String),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum MarkupError {
#[error("closing tag '{tag}' at position {position} doesn't match any open tag")]
MismatchedTag {
tag: String,
position: usize,
},
#[error("closing tag '[/]' at position {position} has nothing to close")]
NothingToClose {
position: usize,
},
}
pub type GiltResult<T> = Result<T, Box<dyn std::error::Error>>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_parse_error_invalid_hex() {
let err = ColorParseError::InvalidHexFormat("#gggggg".to_string());
assert_eq!(err.to_string(), "invalid hex color format: #gggggg");
}
#[test]
fn test_color_parse_error_invalid_rgb() {
let err = ColorParseError::InvalidRgbFormat("rgb(300, 0, 0)".to_string());
assert_eq!(err.to_string(), "invalid RGB color format: rgb(300, 0, 0)");
}
#[test]
fn test_color_parse_error_component_out_of_range() {
let err = ColorParseError::ComponentOutOfRange("red value 300 exceeds 255".to_string());
assert_eq!(
err.to_string(),
"color component out of range: red value 300 exceeds 255"
);
}
#[test]
fn test_color_parse_error_unknown_name() {
let err = ColorParseError::UnknownColorName("notacolor".to_string());
assert_eq!(err.to_string(), "unknown color name: notacolor");
}
#[test]
fn test_color_parse_error_invalid_spec() {
let err = ColorParseError::InvalidColorSpec("???".to_string());
assert_eq!(err.to_string(), "invalid color specification: ???");
}
#[test]
fn test_color_parse_error_clone() {
let err1 = ColorParseError::InvalidHexFormat("#gg".to_string());
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn test_color_parse_error_equality() {
let err1 = ColorParseError::UnknownColorName("red".to_string());
let err2 = ColorParseError::UnknownColorName("red".to_string());
let err3 = ColorParseError::UnknownColorName("blue".to_string());
assert_eq!(err1, err2);
assert_ne!(err1, err3);
}
#[test]
fn test_color_parse_error_is_error_trait() {
let err: Box<dyn std::error::Error> =
Box::new(ColorParseError::InvalidHexFormat("test".to_string()));
assert!(err.to_string().contains("invalid hex color format"));
}
#[test]
fn test_style_error_invalid_syntax() {
let err = StyleError::InvalidSyntax("bold on purple red".to_string());
assert_eq!(err.to_string(), "invalid style syntax: bold on purple red");
}
#[test]
fn test_style_error_unknown_attribute() {
let err = StyleError::UnknownAttribute("blinking".to_string());
assert_eq!(err.to_string(), "unknown style attribute: blinking");
}
#[test]
fn test_style_error_missing_style() {
let err = StyleError::MissingStyle("warning".to_string());
assert_eq!(err.to_string(), "missing style definition: warning");
}
#[test]
fn test_style_error_invalid_combination() {
let err = StyleError::InvalidCombination("bold and dim".to_string());
assert_eq!(err.to_string(), "invalid style combination: bold and dim");
}
#[test]
fn test_style_error_stack_error() {
let err = StyleError::StackError("pop from empty stack".to_string());
assert_eq!(err.to_string(), "style stack error: pop from empty stack");
}
#[test]
fn test_style_error_clone() {
let err1 = StyleError::UnknownAttribute("test".to_string());
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn test_style_error_is_error_trait() {
let err: Box<dyn std::error::Error> =
Box::new(StyleError::InvalidSyntax("test".to_string()));
assert!(err.to_string().contains("invalid style syntax"));
}
#[test]
fn test_console_error_render_error() {
let err = ConsoleError::RenderError("width calculation failed".to_string());
assert_eq!(err.to_string(), "rendering error: width calculation failed");
}
#[test]
fn test_console_error_not_renderable() {
let err = ConsoleError::NotRenderable("NoneType".to_string());
assert_eq!(err.to_string(), "object is not renderable: NoneType");
}
#[test]
fn test_console_error_markup_error() {
let err = ConsoleError::MarkupError("unclosed tag [bold".to_string());
assert_eq!(err.to_string(), "invalid markup syntax: unclosed tag [bold");
}
#[test]
fn test_console_error_live_error() {
let err = ConsoleError::LiveError("cannot nest live displays".to_string());
assert_eq!(
err.to_string(),
"live display error: cannot nest live displays"
);
}
#[test]
fn test_console_error_no_alt_screen() {
let err =
ConsoleError::NoAltScreen("terminal does not support alternate screen".to_string());
assert_eq!(
err.to_string(),
"alternate screen not available: terminal does not support alternate screen"
);
}
#[test]
fn test_console_error_generic() {
let err = ConsoleError::Generic("unknown error".to_string());
assert_eq!(err.to_string(), "console error: unknown error");
}
#[test]
fn test_console_error_clone() {
let err1 = ConsoleError::MarkupError("test".to_string());
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn test_console_error_is_error_trait() {
let err: Box<dyn std::error::Error> =
Box::new(ConsoleError::NotRenderable("test".to_string()));
assert!(err.to_string().contains("not renderable"));
}
#[test]
fn test_segment_error_invalid_segment() {
let err = SegmentError::InvalidSegment("empty text".to_string());
assert_eq!(err.to_string(), "invalid segment: empty text");
}
#[test]
fn test_segment_error_processing() {
let err = SegmentError::ProcessingError("failed to split".to_string());
assert_eq!(err.to_string(), "segment processing error: failed to split");
}
#[test]
fn test_segment_error_clone() {
let err1 = SegmentError::InvalidSegment("test".to_string());
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn test_segment_error_is_error_trait() {
let err: Box<dyn std::error::Error> =
Box::new(SegmentError::ProcessingError("test".to_string()));
assert!(err.to_string().contains("processing error"));
}
#[test]
fn test_cell_error_invalid_width() {
let err = CellError::InvalidWidth("negative width".to_string());
assert_eq!(err.to_string(), "invalid cell width: negative width");
}
#[test]
fn test_cell_error_unicode() {
let err = CellError::UnicodeError("invalid UTF-8 sequence".to_string());
assert_eq!(
err.to_string(),
"unicode processing error: invalid UTF-8 sequence"
);
}
#[test]
fn test_cell_error_clone() {
let err1 = CellError::InvalidWidth("test".to_string());
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn test_cell_error_is_error_trait() {
let err: Box<dyn std::error::Error> = Box::new(CellError::UnicodeError("test".to_string()));
assert!(err.to_string().contains("unicode processing"));
}
#[test]
fn test_palette_error_invalid_index() {
let err = PaletteError::InvalidIndex(300);
assert_eq!(err.to_string(), "invalid palette index: 300");
}
#[test]
fn test_palette_error_not_available() {
let err = PaletteError::NotAvailable("256-color mode required".to_string());
assert_eq!(
err.to_string(),
"palette not available: 256-color mode required"
);
}
#[test]
fn test_palette_error_clone() {
let err1 = PaletteError::InvalidIndex(42);
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn test_palette_error_is_error_trait() {
let err: Box<dyn std::error::Error> = Box::new(PaletteError::InvalidIndex(100));
assert!(err.to_string().contains("invalid palette index"));
}
#[test]
fn test_different_error_types_not_equal() {
let color_err: Box<dyn std::error::Error> =
Box::new(ColorParseError::InvalidHexFormat("test".to_string()));
let style_err: Box<dyn std::error::Error> =
Box::new(StyleError::InvalidSyntax("test".to_string()));
assert_ne!(color_err.to_string(), style_err.to_string());
}
#[test]
fn test_all_errors_implement_debug() {
format!(
"{:?}",
ColorParseError::InvalidHexFormat("test".to_string())
);
format!("{:?}", StyleError::InvalidSyntax("test".to_string()));
format!("{:?}", ConsoleError::Generic("test".to_string()));
format!("{:?}", SegmentError::InvalidSegment("test".to_string()));
format!("{:?}", CellError::InvalidWidth("test".to_string()));
format!("{:?}", PaletteError::InvalidIndex(0));
}
#[test]
fn test_gilt_result_type_alias() {
let result1: GiltResult<i32> = Ok(42);
assert!(result1.is_ok());
let result2: GiltResult<String> = Err(Box::new(ColorParseError::InvalidHexFormat(
"bad".to_string(),
)));
assert!(result2.is_err());
}
#[test]
fn test_error_source_chain() {
let err = ColorParseError::InvalidHexFormat("test".to_string());
let boxed: Box<dyn std::error::Error> = Box::new(err);
assert!(boxed.source().is_none());
}
}