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