A better Rust ATProto crate
1use core::fmt;
2
3use crate::StringMsg;
4/// A specialized [`Result`] type for I/O operations.
5///
6/// This type is broadly used across [`mycelium_util::io`] for any operation which may
7/// produce an error.
8///
9/// This typedef is generally used to avoid writing out [`io::Error`] directly and
10/// is otherwise a direct mapping to [`Result`].
11///
12/// While usual Rust style is to import types directly, aliases of [`Result`]
13/// often are not, to make it easier to distinguish between them. [`Result`] is
14/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias
15/// will generally use `io::Result` instead of shadowing the [prelude]'s import
16/// of [`core::result::Result`][`Result`].
17///
18/// [`mycelium_util::io`]: crate::io
19/// [`io::Error`]: Error
20/// [`Result`]: core::result::Result
21/// [prelude]: core::prelude
22pub type Result<T> = core::result::Result<T, self::Error>;
23
24/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and
25/// associated traits.
26///
27/// Errors mostly originate from the underlying OS, but custom instances of
28/// `Error` can be created with crafted error messages and a particular value of
29/// [`ErrorKind`].
30///
31/// [`Read`]: crate::io::Read
32/// [`Write`]: crate::io::Write
33/// [`Seek`]: crate::io::Seek
34#[derive(Debug)]
35pub struct Error<E: core::error::Error + 'static = StringMsg> {
36 kind: ErrorKind,
37 source: Option<E>,
38}
39
40/// A list specifying general categories of I/O error.
41///
42/// This list is intended to grow over time and it is not recommended to
43/// exhaustively match against it.
44///
45/// It is used with the [`io::Error`] type.
46///
47/// [`io::Error`]: Error
48///
49/// # Handling errors and matching on `ErrorKind`
50///
51/// In application code, use `match` for the `ErrorKind` values you are
52/// expecting; use `_` to match "all other errors".
53///
54/// In comprehensive and thorough tests that want to verify that a test doesn't
55/// return any known incorrect error kind, you may want to cut-and-paste the
56/// current full list of errors from here into your test code, and then match
57/// `_` as the correct case. This seems counterintuitive, but it will make your
58/// tests more robust. In particular, if you want to verify that your code does
59/// produce an unrecognized error kind, the robust solution is to check for all
60/// the recognized error kinds and fail in those cases.
61#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
62#[non_exhaustive]
63pub enum ErrorKind {
64 /// An entity was not found, often a file.
65 NotFound,
66 /// The operation lacked the necessary privileges to complete.
67 PermissionDenied,
68 /// The connection was refused by the remote server.
69 ConnectionRefused,
70 /// The connection was reset by the remote server.
71 ConnectionReset,
72 /// The connection was aborted (terminated) by the remote server.
73 ConnectionAborted,
74 /// The network operation failed because it was not connected yet.
75 NotConnected,
76 /// A socket address could not be bound because the address is already in
77 /// use elsewhere.
78 AddrInUse,
79 /// A nonexistent interface was requested or the requested address was not
80 /// local.
81 AddrNotAvailable,
82 /// The operation failed because a pipe was closed.
83 BrokenPipe,
84 /// An entity already exists, often a file.
85 AlreadyExists,
86 /// The operation needs to block to complete, but the blocking operation was
87 /// requested to not occur.
88 WouldBlock,
89 /// A parameter was incorrect.
90 InvalidInput,
91 /// Data not valid for the operation were encountered.
92 ///
93 /// Unlike [`InvalidInput`], this typically means that the operation
94 /// parameters were valid, however the error was caused by malformed
95 /// input data.
96 ///
97 /// For example, a function that reads a file into a string will error with
98 /// `InvalidData` if the file's contents are not valid UTF-8.
99 ///
100 /// [`InvalidInput`]: #variant.InvalidInput
101 InvalidData,
102 /// The I/O operation's timeout expired, causing it to be canceled.
103 TimedOut,
104 /// An error returned when an operation could not be completed because a
105 /// call to [`write`] returned [`Ok(0)`].
106 ///
107 /// This typically means that an operation could only succeed if it wrote a
108 /// particular number of bytes but only a smaller number of bytes could be
109 /// written.
110 ///
111 /// [`write`]: ../../std/io/trait.Write.html#tymethod.write
112 /// [`Ok(0)`]: ../../std/io/type.Result.html
113 WriteZero,
114 /// This operation was interrupted.
115 ///
116 /// Interrupted operations can typically be retried.
117 Interrupted,
118 /// Any I/O error not part of this list.
119 Other,
120
121 /// An error returned when an operation could not be completed because an
122 /// "end of file" was reached prematurely.
123 ///
124 /// This typically means that an operation could only succeed if it read a
125 /// particular number of bytes but only a smaller number of bytes could be
126 /// read.
127 UnexpectedEof,
128}
129
130impl<E: core::error::Error + 'static> Error<E> {
131 /// Returns a new I/O error with the provided [`ErrorKind`] and `source`
132 /// error.
133 #[must_use]
134 #[inline]
135 pub fn new(kind: ErrorKind, source: E) -> Self {
136 Self {
137 kind,
138 source: Some(source),
139 }
140 }
141
142 /// Returns the [`ErrorKind`] of this error.
143 #[must_use]
144 #[inline]
145 pub fn kind(&self) -> ErrorKind {
146 self.kind
147 }
148}
149
150impl From<ErrorKind> for Error {
151 fn from(kind: ErrorKind) -> Self {
152 Error { kind, source: None }
153 }
154}
155
156impl<E: core::error::Error + 'static> core::error::Error for Error<E> {
157 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
158 self.source
159 .as_ref()
160 .map(|e| e as &(dyn core::error::Error + 'static))
161 }
162}
163
164impl<E: core::error::Error + 'static> fmt::Display for Error<E> {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 if let Some(ref source) = self.source {
167 write!(f, "{}: {}", self.kind.as_str(), source)
168 } else {
169 f.write_str(self.kind.as_str())
170 }
171 }
172}
173
174impl ErrorKind {
175 pub(crate) fn as_str(self) -> &'static str {
176 match self {
177 ErrorKind::NotFound => "entity not found",
178 ErrorKind::PermissionDenied => "permission denied",
179 ErrorKind::ConnectionRefused => "connection refused",
180 ErrorKind::ConnectionReset => "connection reset",
181 ErrorKind::ConnectionAborted => "connection aborted",
182 ErrorKind::NotConnected => "not connected",
183 ErrorKind::AddrInUse => "address in use",
184 ErrorKind::AddrNotAvailable => "address not available",
185 ErrorKind::BrokenPipe => "broken pipe",
186 ErrorKind::AlreadyExists => "entity already exists",
187 ErrorKind::WouldBlock => "operation would block",
188 ErrorKind::InvalidInput => "invalid input parameter",
189 ErrorKind::InvalidData => "invalid data",
190 ErrorKind::TimedOut => "timed out",
191 ErrorKind::WriteZero => "write zero",
192 ErrorKind::Interrupted => "operation interrupted",
193 ErrorKind::Other => "other error",
194 ErrorKind::UnexpectedEof => "unexpected end of file",
195 }
196 }
197}
198
199#[cfg(feature = "std")]
200impl From<std::io::ErrorKind> for ErrorKind {
201 fn from(value: std::io::ErrorKind) -> Self {
202 match value {
203 std::io::ErrorKind::ConnectionRefused => ErrorKind::ConnectionRefused,
204 std::io::ErrorKind::ConnectionReset => ErrorKind::ConnectionReset,
205 std::io::ErrorKind::ConnectionAborted => ErrorKind::ConnectionAborted,
206 std::io::ErrorKind::NotConnected => ErrorKind::NotConnected,
207 std::io::ErrorKind::AddrInUse => ErrorKind::AddrInUse,
208 std::io::ErrorKind::AddrNotAvailable => ErrorKind::AddrNotAvailable,
209 std::io::ErrorKind::BrokenPipe => ErrorKind::BrokenPipe,
210 std::io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists,
211 std::io::ErrorKind::WouldBlock => ErrorKind::WouldBlock,
212 std::io::ErrorKind::InvalidInput => ErrorKind::InvalidInput,
213 std::io::ErrorKind::InvalidData => ErrorKind::InvalidData,
214 std::io::ErrorKind::TimedOut => ErrorKind::TimedOut,
215 std::io::ErrorKind::WriteZero => ErrorKind::WriteZero,
216 std::io::ErrorKind::Interrupted => ErrorKind::Interrupted,
217 std::io::ErrorKind::Other => ErrorKind::Other,
218 std::io::ErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof,
219 _ => ErrorKind::Other,
220 }
221 }
222}
223
224impl From<embedded_io::ErrorKind> for ErrorKind {
225 fn from(value: embedded_io::ErrorKind) -> Self {
226 match value {
227 embedded_io::ErrorKind::ConnectionRefused => ErrorKind::ConnectionRefused,
228 embedded_io::ErrorKind::ConnectionReset => ErrorKind::ConnectionReset,
229 embedded_io::ErrorKind::ConnectionAborted => ErrorKind::ConnectionAborted,
230 embedded_io::ErrorKind::NotConnected => ErrorKind::NotConnected,
231 embedded_io::ErrorKind::AddrInUse => ErrorKind::AddrInUse,
232 embedded_io::ErrorKind::AddrNotAvailable => ErrorKind::AddrNotAvailable,
233 embedded_io::ErrorKind::BrokenPipe => ErrorKind::BrokenPipe,
234 embedded_io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists,
235 embedded_io::ErrorKind::InvalidInput => ErrorKind::InvalidInput,
236 embedded_io::ErrorKind::InvalidData => ErrorKind::InvalidData,
237 embedded_io::ErrorKind::TimedOut => ErrorKind::TimedOut,
238 embedded_io::ErrorKind::WriteZero => ErrorKind::WriteZero,
239 embedded_io::ErrorKind::Interrupted => ErrorKind::Interrupted,
240 embedded_io::ErrorKind::Other => ErrorKind::Other,
241 embedded_io::ErrorKind::NotFound => ErrorKind::NotFound,
242 embedded_io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
243 embedded_io::ErrorKind::Unsupported => ErrorKind::Other,
244 embedded_io::ErrorKind::OutOfMemory => ErrorKind::Other,
245 _ => ErrorKind::Other,
246 }
247 }
248}
249
250impl From<ErrorKind> for embedded_io::ErrorKind {
251 fn from(value: ErrorKind) -> Self {
252 match value {
253 ErrorKind::ConnectionRefused => embedded_io::ErrorKind::ConnectionRefused,
254 ErrorKind::ConnectionReset => embedded_io::ErrorKind::ConnectionReset,
255 ErrorKind::ConnectionAborted => embedded_io::ErrorKind::ConnectionAborted,
256 ErrorKind::NotConnected => embedded_io::ErrorKind::NotConnected,
257 ErrorKind::AddrInUse => embedded_io::ErrorKind::AddrInUse,
258 ErrorKind::AddrNotAvailable => embedded_io::ErrorKind::AddrNotAvailable,
259 ErrorKind::BrokenPipe => embedded_io::ErrorKind::BrokenPipe,
260 ErrorKind::AlreadyExists => embedded_io::ErrorKind::AlreadyExists,
261 ErrorKind::InvalidInput => embedded_io::ErrorKind::InvalidInput,
262 ErrorKind::InvalidData => embedded_io::ErrorKind::InvalidData,
263 ErrorKind::TimedOut => embedded_io::ErrorKind::TimedOut,
264 ErrorKind::WriteZero => embedded_io::ErrorKind::WriteZero,
265 ErrorKind::Interrupted => embedded_io::ErrorKind::Interrupted,
266 ErrorKind::Other => embedded_io::ErrorKind::Other,
267 ErrorKind::NotFound => embedded_io::ErrorKind::NotFound,
268 ErrorKind::PermissionDenied => embedded_io::ErrorKind::PermissionDenied,
269 ErrorKind::WouldBlock => embedded_io::ErrorKind::TimedOut,
270 ErrorKind::UnexpectedEof => embedded_io::ErrorKind::BrokenPipe,
271 }
272 }
273}
274
275#[cfg(feature = "std")]
276impl From<ErrorKind> for std::io::ErrorKind {
277 fn from(value: ErrorKind) -> Self {
278 match value {
279 ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists,
280 ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput,
281 ErrorKind::InvalidData => std::io::ErrorKind::InvalidData,
282 ErrorKind::TimedOut => std::io::ErrorKind::TimedOut,
283 ErrorKind::WriteZero => std::io::ErrorKind::WriteZero,
284 ErrorKind::Interrupted => std::io::ErrorKind::Interrupted,
285 ErrorKind::Other => std::io::ErrorKind::Other,
286 ErrorKind::NotFound => std::io::ErrorKind::NotFound,
287 ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied,
288 ErrorKind::WouldBlock => std::io::ErrorKind::WouldBlock,
289 ErrorKind::UnexpectedEof => std::io::ErrorKind::UnexpectedEof,
290 ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe,
291 ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset,
292 ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted,
293 ErrorKind::NotConnected => std::io::ErrorKind::NotConnected,
294 ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused,
295 ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse,
296 ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable,
297 }
298 }
299}
300
301#[cfg(feature = "std")]
302impl From<Error> for std::io::Error {
303 fn from(value: Error) -> Self {
304 std::io::Error::new(value.kind().into(), value)
305 }
306}