1use std::fmt;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum ErrorKind {
10 #[default]
11 Other,
12 TimedOut,
16 CallFailed,
22 UncertainWrite,
25 Interrupted,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct SparError {
33 message: String,
34 kind: ErrorKind,
35}
36
37impl SparError {
38 pub fn new(message: impl Into<String>) -> Self {
39 Self {
40 message: message.into(),
41 kind: ErrorKind::Other,
42 }
43 }
44
45 pub fn timed_out(message: impl Into<String>) -> Self {
46 Self {
47 message: message.into(),
48 kind: ErrorKind::TimedOut,
49 }
50 }
51
52 pub fn call_failed(message: impl Into<String>) -> Self {
54 Self {
55 message: message.into(),
56 kind: ErrorKind::CallFailed,
57 }
58 }
59
60 pub fn uncertain_write(message: impl Into<String>) -> Self {
61 Self {
62 message: message.into(),
63 kind: ErrorKind::UncertainWrite,
64 }
65 }
66
67 pub fn interrupted(message: impl Into<String>) -> Self {
69 Self {
70 message: message.into(),
71 kind: ErrorKind::Interrupted,
72 }
73 }
74
75 pub fn with_message(&self, message: impl Into<String>) -> Self {
80 Self {
81 message: message.into(),
82 kind: self.kind,
83 }
84 }
85
86 pub fn kind(&self) -> ErrorKind {
87 self.kind
88 }
89
90 pub fn worth_retrying(&self) -> bool {
92 !matches!(
93 self.kind,
94 ErrorKind::TimedOut | ErrorKind::UncertainWrite | ErrorKind::Interrupted
95 )
96 }
97
98 pub fn message(&self) -> &str {
99 &self.message
100 }
101
102 pub fn last_line(&self) -> &str {
105 self.message.lines().next_back().unwrap_or(&self.message)
106 }
107
108 pub fn first_line(&self) -> &str {
110 self.message.lines().next().unwrap_or(&self.message)
111 }
112}
113
114impl fmt::Display for SparError {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 f.write_str(&self.message)
117 }
118}
119
120impl std::error::Error for SparError {}
121
122impl From<std::io::Error> for SparError {
123 fn from(e: std::io::Error) -> Self {
124 SparError::new(e.to_string())
125 }
126}
127
128impl From<serde_json::Error> for SparError {
129 fn from(e: serde_json::Error) -> Self {
130 SparError::new(format!("invalid JSON: {e}"))
131 }
132}
133
134impl From<toml::de::Error> for SparError {
135 fn from(e: toml::de::Error) -> Self {
136 SparError::new(format!("invalid TOML: {e}"))
137 }
138}
139
140pub type Result<T> = std::result::Result<T, SparError>;
141
142#[macro_export]
144macro_rules! spar_err {
145 ($($arg:tt)*) => { $crate::error::SparError::new(format!($($arg)*)) };
146}
147
148#[macro_export]
150macro_rules! bail {
151 ($($arg:tt)*) => { return Err($crate::spar_err!($($arg)*)) };
152}