1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Error types.
//!
//! Port of the exceptions in upstream `rich/errors.py`. Upstream's classes
//! form a small hierarchy (`ConsoleError` and `StyleError` at the root); here
//! each class is a [`RichError`] variant, and [`RichError::is_console_error`]
//! and [`RichError::is_style_error`] answer upstream's `isinstance` checks.
//! As more of the library is ported, additional variants are added here rather
//! than scattering ad-hoc error types across modules.
use std::fmt;
/// Errors produced while parsing colors, styles, or markup.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RichError {
/// A color could not be parsed (`rich.color.ColorParseError`).
ColorParse(String),
/// A style definition could not be parsed (`rich.errors.StyleSyntaxError`).
StyleSyntax(String),
/// Console markup was malformed (`rich.errors.MarkupError`).
Markup(String),
/// A JSON string could not be parsed (used by `rich.json.JSON`).
Json(String),
/// A regular expression could not be compiled or run. Python has no rich
/// equivalent — it lets `re.error` propagate — but a caller-supplied pattern
/// has to fail somewhere, and returning it beats panicking.
Regex(String),
/// The console's base theme cannot be popped (`rich.theme.ThemeStackError`).
ThemeStack(String),
/// A theme config file could not be read or parsed. Upstream surfaces
/// Python's `configparser` errors (`NoSectionError`, `DuplicateOptionError`,
/// `ParsingError`, `InterpolationSyntaxError`) and `OSError` here.
ThemeConfig(String),
/// A screen update without the alternate screen
/// (`rich.errors.NoAltScreen`).
NoAltScreen(String),
/// An error in console operation (`rich.errors.ConsoleError`).
Console(String),
/// An error in styles (`rich.errors.StyleError`).
Style(String),
/// No such style (`rich.errors.MissingStyle`).
MissingStyle(String),
/// The style stack is invalid (`rich.errors.StyleStackError`).
StyleStack(String),
/// An object is not renderable (`rich.errors.NotRenderableError`).
NotRenderable(String),
/// An error related to a live display (`rich.errors.LiveError`).
Live(String),
}
impl RichError {
/// The message upstream's exception carries, which is what `str(error)`
/// prints there. [`Display`](fmt::Display) prefixes it with the kind.
pub fn message(&self) -> &str {
match self {
RichError::ColorParse(message)
| RichError::StyleSyntax(message)
| RichError::Markup(message)
| RichError::Json(message)
| RichError::Regex(message)
| RichError::ThemeStack(message)
| RichError::ThemeConfig(message)
| RichError::NoAltScreen(message)
| RichError::Console(message)
| RichError::Style(message)
| RichError::MissingStyle(message)
| RichError::StyleStack(message)
| RichError::NotRenderable(message)
| RichError::Live(message) => message,
}
}
/// Whether upstream's class derives from `ConsoleError`: `ConsoleError`
/// itself, `StyleSyntaxError`, `StyleStackError`, `NotRenderableError`,
/// `MarkupError`, `LiveError` and `NoAltScreen`.
pub fn is_console_error(&self) -> bool {
matches!(
self,
RichError::Console(_)
| RichError::StyleSyntax(_)
| RichError::StyleStack(_)
| RichError::NotRenderable(_)
| RichError::Markup(_)
| RichError::Live(_)
| RichError::NoAltScreen(_)
)
}
/// Whether upstream's class derives from `StyleError`: `StyleError`
/// itself and `MissingStyle`.
pub fn is_style_error(&self) -> bool {
matches!(self, RichError::Style(_) | RichError::MissingStyle(_))
}
}
impl fmt::Display for RichError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RichError::ColorParse(msg) => write!(f, "color parse error: {msg}"),
RichError::StyleSyntax(msg) => write!(f, "style syntax error: {msg}"),
RichError::Markup(msg) => write!(f, "markup error: {msg}"),
RichError::Json(msg) => write!(f, "json parse error: {msg}"),
RichError::Regex(msg) => write!(f, "regex error: {msg}"),
RichError::ThemeStack(msg) => write!(f, "theme stack error: {msg}"),
RichError::ThemeConfig(msg) => write!(f, "theme config error: {msg}"),
RichError::NoAltScreen(msg) => write!(f, "{msg}"),
RichError::Console(msg) => write!(f, "console error: {msg}"),
RichError::Style(msg) => write!(f, "style error: {msg}"),
RichError::MissingStyle(msg) => write!(f, "missing style: {msg}"),
RichError::StyleStack(msg) => write!(f, "style stack error: {msg}"),
RichError::NotRenderable(msg) => write!(f, "not renderable: {msg}"),
RichError::Live(msg) => write!(f, "live error: {msg}"),
}
}
}
impl std::error::Error for RichError {}
/// Convenience alias mirroring the subset of `rich` that raises on bad input.
pub type Result<T> = std::result::Result<T, RichError>;
#[cfg(test)]
mod tests {
use super::*;
/// The hierarchy of `rich/errors.py`.
#[test]
fn hierarchy_matches_upstream() {
assert!(RichError::Markup(String::new()).is_console_error());
assert!(RichError::StyleSyntax(String::new()).is_console_error());
assert!(!RichError::StyleSyntax(String::new()).is_style_error());
assert!(RichError::MissingStyle(String::new()).is_style_error());
assert!(!RichError::MissingStyle(String::new()).is_console_error());
assert_eq!(RichError::Live("x".into()).message(), "x");
}
}