1use std::fmt;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum ErrorKind {
10 #[default]
11 Other,
12 TimedOut,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct SparError {
20 message: String,
21 kind: ErrorKind,
22}
23
24impl SparError {
25 pub fn new(message: impl Into<String>) -> Self {
26 Self {
27 message: message.into(),
28 kind: ErrorKind::Other,
29 }
30 }
31
32 pub fn timed_out(message: impl Into<String>) -> Self {
33 Self {
34 message: message.into(),
35 kind: ErrorKind::TimedOut,
36 }
37 }
38
39 pub fn kind(&self) -> ErrorKind {
40 self.kind
41 }
42
43 pub fn worth_retrying(&self) -> bool {
45 self.kind != ErrorKind::TimedOut
46 }
47
48 pub fn message(&self) -> &str {
49 &self.message
50 }
51
52 pub fn last_line(&self) -> &str {
55 self.message.lines().next_back().unwrap_or(&self.message)
56 }
57
58 pub fn first_line(&self) -> &str {
60 self.message.lines().next().unwrap_or(&self.message)
61 }
62}
63
64impl fmt::Display for SparError {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.write_str(&self.message)
67 }
68}
69
70impl std::error::Error for SparError {}
71
72impl From<std::io::Error> for SparError {
73 fn from(e: std::io::Error) -> Self {
74 SparError::new(e.to_string())
75 }
76}
77
78impl From<serde_json::Error> for SparError {
79 fn from(e: serde_json::Error) -> Self {
80 SparError::new(format!("invalid JSON: {e}"))
81 }
82}
83
84impl From<toml::de::Error> for SparError {
85 fn from(e: toml::de::Error) -> Self {
86 SparError::new(format!("invalid TOML: {e}"))
87 }
88}
89
90pub type Result<T> = std::result::Result<T, SparError>;
91
92#[macro_export]
94macro_rules! spar_err {
95 ($($arg:tt)*) => { $crate::error::SparError::new(format!($($arg)*)) };
96}
97
98#[macro_export]
100macro_rules! bail {
101 ($($arg:tt)*) => { return Err($crate::spar_err!($($arg)*)) };
102}