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}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct SparError {
29 message: String,
30 kind: ErrorKind,
31}
32
33impl SparError {
34 pub fn new(message: impl Into<String>) -> Self {
35 Self {
36 message: message.into(),
37 kind: ErrorKind::Other,
38 }
39 }
40
41 pub fn timed_out(message: impl Into<String>) -> Self {
42 Self {
43 message: message.into(),
44 kind: ErrorKind::TimedOut,
45 }
46 }
47
48 pub fn call_failed(message: impl Into<String>) -> Self {
50 Self {
51 message: message.into(),
52 kind: ErrorKind::CallFailed,
53 }
54 }
55
56 pub fn uncertain_write(message: impl Into<String>) -> Self {
57 Self {
58 message: message.into(),
59 kind: ErrorKind::UncertainWrite,
60 }
61 }
62
63 pub fn with_message(&self, message: impl Into<String>) -> Self {
68 Self {
69 message: message.into(),
70 kind: self.kind,
71 }
72 }
73
74 pub fn kind(&self) -> ErrorKind {
75 self.kind
76 }
77
78 pub fn worth_retrying(&self) -> bool {
80 !matches!(self.kind, ErrorKind::TimedOut | ErrorKind::UncertainWrite)
81 }
82
83 pub fn message(&self) -> &str {
84 &self.message
85 }
86
87 pub fn last_line(&self) -> &str {
90 self.message.lines().next_back().unwrap_or(&self.message)
91 }
92
93 pub fn first_line(&self) -> &str {
95 self.message.lines().next().unwrap_or(&self.message)
96 }
97}
98
99impl fmt::Display for SparError {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 f.write_str(&self.message)
102 }
103}
104
105impl std::error::Error for SparError {}
106
107impl From<std::io::Error> for SparError {
108 fn from(e: std::io::Error) -> Self {
109 SparError::new(e.to_string())
110 }
111}
112
113impl From<serde_json::Error> for SparError {
114 fn from(e: serde_json::Error) -> Self {
115 SparError::new(format!("invalid JSON: {e}"))
116 }
117}
118
119impl From<toml::de::Error> for SparError {
120 fn from(e: toml::de::Error) -> Self {
121 SparError::new(format!("invalid TOML: {e}"))
122 }
123}
124
125pub type Result<T> = std::result::Result<T, SparError>;
126
127#[macro_export]
129macro_rules! spar_err {
130 ($($arg:tt)*) => { $crate::error::SparError::new(format!($($arg)*)) };
131}
132
133#[macro_export]
135macro_rules! bail {
136 ($($arg:tt)*) => { return Err($crate::spar_err!($($arg)*)) };
137}