1use std::{borrow::Cow, error, fmt};
2
3#[non_exhaustive]
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum ErrorKind {
7 Transport,
9 InvalidRequest,
11 InvalidResponse,
13 Protocol,
15}
16
17impl fmt::Display for ErrorKind {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 Self::Transport => write!(f, "Transport")?,
21 Self::InvalidRequest => write!(f, "InvalidRequest")?,
22 Self::InvalidResponse => write!(f, "InvalidResponse")?,
23 Self::Protocol => write!(f, "SseProtocol")?,
24 }
25 Ok(())
26 }
27}
28
29pub struct ErrorSource(anyhow::Error);
33
34impl fmt::Debug for ErrorSource {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 fmt::Debug::fmt(&self.0, f)
37 }
38}
39
40impl fmt::Display for ErrorSource {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 fmt::Display::fmt(&self.0, f)
43 }
44}
45
46impl From<String> for ErrorSource {
47 fn from(message: String) -> Self {
48 ErrorSource(anyhow::Error::msg(message))
49 }
50}
51
52impl From<&'static str> for ErrorSource {
53 fn from(message: &'static str) -> Self {
54 ErrorSource(anyhow::Error::msg(message))
55 }
56}
57
58pub trait IntoErrorSource {
60 fn into_error_source(self) -> ErrorSource;
62}
63
64impl IntoErrorSource for ErrorSource {
65 fn into_error_source(self) -> ErrorSource {
66 self
67 }
68}
69
70impl<E> IntoErrorSource for E
71where
72 E: Into<anyhow::Error>,
73{
74 fn into_error_source(self) -> ErrorSource {
75 ErrorSource(self.into())
76 }
77}
78
79pub struct Error {
81 kind: ErrorKind,
82 message: Cow<'static, str>,
83 context: Vec<(&'static str, String)>,
84 source: Option<ErrorSource>,
85}
86
87impl Error {
88 pub fn new(kind: ErrorKind, message: impl Into<Cow<'static, str>>) -> Self {
90 Self { kind, message: message.into(), context: Vec::new(), source: None }
91 }
92
93 pub const fn kind(&self) -> ErrorKind {
95 self.kind
96 }
97
98 pub fn message(&self) -> &str {
100 &self.message
101 }
102
103 pub fn context(&self) -> &[(&'static str, String)] {
105 &self.context
106 }
107
108 pub fn with_context(mut self, key: &'static str, value: impl ToString) -> Self {
110 self.context.push((key, value.to_string()));
111 self
112 }
113
114 pub fn set_source(mut self, source: impl IntoErrorSource) -> Self {
116 debug_assert!(self.source.is_none(), "source already set");
117 self.source = Some(source.into_error_source());
118 self
119 }
120
121 pub fn source(&self) -> Option<&ErrorSource> {
123 self.source.as_ref()
124 }
125}
126
127impl fmt::Display for Error {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 write!(f, "{}: {}", self.kind, self.message)?;
130
131 if !self.context.is_empty() {
132 f.write_str(" { ")?;
133 for (index, (key, value)) in self.context.iter().enumerate() {
134 if index > 0 {
135 f.write_str(", ")?;
136 }
137 write!(f, "{key}={value}")?;
138 }
139 f.write_str(" }")?;
140 }
141
142 Ok(())
143 }
144}
145
146impl fmt::Debug for Error {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 writeln!(f, "{}: {}", self.kind, self.message)?;
149 if !self.context.is_empty() {
150 writeln!(f, "Context:")?;
151 for (key, value) in &self.context {
152 writeln!(f, " {key}: {value}")?;
153 }
154 }
155 if let Some(source) = &self.source {
156 writeln!(f, "Source: {source}")?;
157 }
158 Ok(())
159 }
160}
161
162impl error::Error for Error {
163 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
164 self.source.as_ref().map(|err| err.0.as_ref())
165 }
166}
167
168impl From<eventsrc::ProtocolError> for Error {
169 fn from(err: eventsrc::ProtocolError) -> Self {
170 Self {
171 kind: ErrorKind::Protocol,
172 message: Cow::Owned(err.to_string()),
173 context: Vec::new(),
174 source: None,
175 }
176 }
177}
178
179impl<E> From<eventsrc::StreamError<E>> for Error
180where
181 E: error::Error + Send + Sync + 'static,
182{
183 fn from(err: eventsrc::StreamError<E>) -> Self {
184 match err {
185 eventsrc::StreamError::Source(e) => Self {
186 kind: ErrorKind::InvalidResponse,
187 message: "invalid response body stream error".into(),
188 context: Vec::new(),
189 source: Some(anyhow::Error::new(e).into_error_source()),
190 },
191 eventsrc::StreamError::Protocol(e) => Self {
192 kind: ErrorKind::Protocol,
193 message: Cow::Owned(e.to_string()),
194 context: Vec::new(),
195 source: None,
196 },
197 }
198 }
199}