dragonfly_client_core/error/
errors.rs1use std::{error::Error as ErrorTrait, fmt};
18
19use super::message::Message;
20
21#[derive(Debug, PartialEq, Eq, Clone)]
23pub enum ErrorType {
24 StorageError,
25 ConfigError,
26 SerializeError,
27 ValidationError,
28 ParseError,
29 CertificateError,
30 TLSConfigError,
31 AsyncRuntimeError,
32 StreamError,
33 ConnectError,
34 PluginError,
35}
36
37impl ErrorType {
39 pub fn as_str(&self) -> &'static str {
41 match self {
42 ErrorType::StorageError => "StorageError",
43 ErrorType::ConfigError => "ConfigError",
44 ErrorType::ValidationError => "ValidationError",
45 ErrorType::ParseError => "ParseError",
46 ErrorType::CertificateError => "CertificateError",
47 ErrorType::SerializeError => "SerializeError",
48 ErrorType::TLSConfigError => "TLSConfigError",
49 ErrorType::AsyncRuntimeError => "AsyncRuntimeError",
50 ErrorType::StreamError => "StreamError",
51 ErrorType::ConnectError => "ConnectError",
52 ErrorType::PluginError => "PluginError",
53 }
54 }
55}
56
57#[derive(Debug)]
59pub struct ExternalError {
60 pub etype: ErrorType,
61 pub cause: Option<Box<dyn ErrorTrait + Send + Sync>>,
62 pub context: Option<Message>,
63}
64
65impl ExternalError {
67 pub fn new(etype: ErrorType) -> Self {
69 ExternalError {
70 etype,
71 cause: None,
72 context: None,
73 }
74 }
75
76 pub fn with_context(mut self, message: impl Into<Message>) -> Self {
78 self.context = Some(message.into());
79 self
80 }
81
82 pub fn with_cause(mut self, cause: Box<dyn ErrorTrait + Send + Sync>) -> Self {
84 self.cause = Some(cause);
85 self
86 }
87
88 fn chain_display(
90 &self,
91 previous: Option<&ExternalError>,
92 f: &mut fmt::Formatter<'_>,
93 ) -> fmt::Result {
94 if previous.map(|p| p.etype != self.etype).unwrap_or(true) {
95 write!(f, "{}", self.etype.as_str())?
96 }
97
98 if let Some(c) = self.context.as_ref() {
99 write!(f, " context: {}", c.as_str())?;
100 }
101
102 if let Some(c) = self.cause.as_ref() {
103 if let Some(e) = c.downcast_ref::<Box<ExternalError>>() {
104 write!(f, " cause: ")?;
105 e.chain_display(Some(self), f)
106 } else {
107 write!(f, " cause: {}", c)
108 }
109 } else {
110 Ok(())
111 }
112 }
113}
114
115impl fmt::Display for ExternalError {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 self.chain_display(None, f)
119 }
120}
121
122impl ErrorTrait for ExternalError {}
124
125pub trait OrErr<T, E> {
127 fn or_err(self, et: ErrorType) -> Result<T, ExternalError>
131 where
132 E: Into<Box<dyn ErrorTrait + Send + Sync>>;
133
134 fn or_context(self, et: ErrorType, context: &'static str) -> Result<T, ExternalError>
135 where
136 E: Into<Box<dyn ErrorTrait + Send + Sync>>;
137}
138
139impl<T, E> OrErr<T, E> for Result<T, E> {
141 fn or_err(self, et: ErrorType) -> Result<T, ExternalError>
142 where
143 E: Into<Box<dyn ErrorTrait + Send + Sync>>,
144 {
145 self.map_err(|err| ExternalError::new(et).with_cause(err.into()))
146 }
147
148 fn or_context(self, et: ErrorType, context: &'static str) -> Result<T, ExternalError>
149 where
150 E: Into<Box<dyn ErrorTrait + Send + Sync>>,
151 {
152 self.map_err(|err| {
153 ExternalError::new(et)
154 .with_cause(err.into())
155 .with_context(context)
156 })
157 }
158}
159
160#[derive(Debug, thiserror::Error)]
162#[error("backend error: {message}")]
163pub struct BackendError {
164 pub message: String,
166
167 pub status_code: Option<reqwest::StatusCode>,
169
170 pub header: Option<reqwest::header::HeaderMap>,
172}
173
174#[derive(Debug, thiserror::Error)]
176#[error("download piece {piece_number} from parent {parent_id} failed")]
177pub struct DownloadFromParentFailed {
178 pub piece_number: u32,
180
181 pub parent_id: String,
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn should_create_error() {
191 let error = ExternalError::new(ErrorType::StorageError).with_context("error message");
192 assert_eq!(format!("{}", error), "StorageError context: error message");
193
194 let error = ExternalError::new(ErrorType::StorageError)
195 .with_context(format!("error message {}", "with owned string"));
196 assert_eq!(
197 format!("{}", error),
198 "StorageError context: error message with owned string"
199 );
200
201 let error = ExternalError::new(ErrorType::StorageError)
202 .with_context(format!("error message {}", "with owned string"))
203 .with_cause(Box::new(std::io::Error::new(
204 std::io::ErrorKind::Other,
205 "inner error",
206 )));
207
208 assert_eq!(
209 format!("{}", error),
210 "StorageError context: error message with owned string cause: inner error"
211 );
212 }
213
214 #[test]
215 fn should_extend_result_with_error() {
216 let result: Result<(), std::io::Error> = Err(std::io::Error::new(
217 std::io::ErrorKind::Other,
218 "inner error",
219 ));
220
221 let error = result.or_err(ErrorType::StorageError).unwrap_err();
222 assert_eq!(format!("{}", error), "StorageError cause: inner error");
223
224 let result: Result<(), std::io::Error> = Err(std::io::Error::new(
225 std::io::ErrorKind::Other,
226 "inner error",
227 ));
228
229 let error = result
230 .or_context(ErrorType::StorageError, "error message")
231 .unwrap_err();
232
233 assert_eq!(
234 format!("{}", error),
235 "StorageError context: error message cause: inner error"
236 );
237 }
238}