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 kind(&self) -> ErrorKind {
64 self.kind
65 }
66
67 pub fn worth_retrying(&self) -> bool {
69 !matches!(self.kind, ErrorKind::TimedOut | ErrorKind::UncertainWrite)
70 }
71
72 pub fn message(&self) -> &str {
73 &self.message
74 }
75
76 pub fn last_line(&self) -> &str {
79 self.message.lines().next_back().unwrap_or(&self.message)
80 }
81
82 pub fn first_line(&self) -> &str {
84 self.message.lines().next().unwrap_or(&self.message)
85 }
86}
87
88impl fmt::Display for SparError {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 f.write_str(&self.message)
91 }
92}
93
94impl std::error::Error for SparError {}
95
96impl From<std::io::Error> for SparError {
97 fn from(e: std::io::Error) -> Self {
98 SparError::new(e.to_string())
99 }
100}
101
102impl From<serde_json::Error> for SparError {
103 fn from(e: serde_json::Error) -> Self {
104 SparError::new(format!("invalid JSON: {e}"))
105 }
106}
107
108impl From<toml::de::Error> for SparError {
109 fn from(e: toml::de::Error) -> Self {
110 SparError::new(format!("invalid TOML: {e}"))
111 }
112}
113
114pub type Result<T> = std::result::Result<T, SparError>;
115
116#[macro_export]
118macro_rules! spar_err {
119 ($($arg:tt)*) => { $crate::error::SparError::new(format!($($arg)*)) };
120}
121
122#[macro_export]
124macro_rules! bail {
125 ($($arg:tt)*) => { return Err($crate::spar_err!($($arg)*)) };
126}