error_code/lib.rs
1//! Error code library provides generic errno/winapi error wrapper
2//!
3//! User can define own [Category](struct.Category.html) if you want to create new error wrapper.
4//!
5//! ## Features
6//!
7//! - `std` - Enables `std::error::Error` implementation and conversion from `std::io::Error`
8//!
9//! ## Usage
10//!
11//! ```rust
12//! use error_code::ErrorCode;
13//!
14//! use std::fs::File;
15//!
16//! File::open("non_existing");
17//! println!("{}", ErrorCode::last_system());
18//! ```
19
20#![no_std]
21#![warn(missing_docs)]
22#![allow(clippy::style)]
23
24#[cfg(feature = "std")]
25extern crate std;
26
27use core::{mem, hash, fmt};
28
29#[deprecated]
30///Text to return when cannot map error
31pub const UNKNOWN_ERROR: &str = "Unknown error";
32///Text to return when error fails to be converted into utf-8
33pub const FAIL_ERROR_FORMAT: &str = "Failed to format error into utf-8";
34
35///Error message buffer size
36pub const MESSAGE_BUF_SIZE: usize = 256;
37///Type alias for buffer to hold error code description.
38pub type MessageBuf = [mem::MaybeUninit<u8>; MESSAGE_BUF_SIZE];
39///Type alias for Result with `ErrorCode` as error variant by default
40pub type Result<T, E = ErrorCode> = core::result::Result<T, E>;
41
42pub mod defs;
43pub mod types;
44pub mod utils;
45mod posix;
46pub use posix::POSIX_CATEGORY;
47mod system;
48pub use system::SYSTEM_CATEGORY;
49
50#[macro_export]
51///Defines error code `Category` as enum which implements conversion into generic ErrorCode
52///
53///This enum shall implement following traits:
54///
55///- `Clone`
56///- `Copy`
57///- `Debug`
58///- `Display` - uses `ErrorCode` `fmt::Display`
59///- `PartialEq` / `Eq`
60///- `PartialOrd` / `Ord`
61///
62///# Usage
63///
64///```
65///use error_code::{define_category, ErrorCode};
66///
67///define_category!(
68/// ///This is documentation for my error
69/// ///
70/// ///Documentation of variants only allow 1 line comment and it should be within 256 characters
71/// pub enum MyError {
72/// ///Success
73/// Success = 0,
74/// ///This is bad
75/// Error = 1,
76/// }
77///);
78///
79///fn handle_error(res: Result<(), MyError>) -> Result<(), ErrorCode> {
80/// res?;
81/// Ok(())
82///}
83///
84///let error = handle_error(Err(MyError::Error)).expect_err("Should return error");
85///assert_eq!(MyError::Error, error);
86///assert_eq!(error.to_string(), "MyError(1): This is bad");
87///assert_eq!(error.to_string(), MyError::Error.to_string());
88///define_category!(
89/// ///This is documentation for my error
90/// ///
91/// ///Documentation of variants only allow 1 line comment and it should be within 256 characters
92/// pub enum DuplicateError {
93/// ///Success
94/// Success = 0,
95/// ///This is bad
96/// Error = 1,
97/// }
98///);
99///assert_ne!(MyError::Error, DuplicateError::Error.into_error_code());
100///assert_ne!(MyError::Error.into_error_code(), DuplicateError::Error.into_error_code());
101///```
102macro_rules! define_category {
103 (
104 $(#[$docs:meta])*
105 pub enum $name:ident {
106 $(
107 #[doc = $msg:literal]
108 $ident:ident = $code:literal,
109 )+
110 }
111 ) => {
112 #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)]
113 #[repr(i32)]
114 $(#[$docs])*
115 pub enum $name {
116 $(
117 #[doc = $msg]
118 $ident = $code,
119 )+
120 }
121
122 impl From<$name> for $crate::ErrorCode {
123 #[inline(always)]
124 fn from(this: $name) -> $crate::ErrorCode {
125 this.into_error_code()
126 }
127 }
128
129 impl core::fmt::Display for $name {
130 #[inline(always)]
131 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
132 core::fmt::Display::fmt(&self.into_error_code(), fmt)
133 }
134 }
135
136 impl PartialEq<$crate::ErrorCode> for $name {
137 #[inline(always)]
138 fn eq(&self, other: &$crate::ErrorCode) -> bool {
139 core::ptr::eq($name::category(), other.category()) && self.raw_code() == other.raw_code()
140 }
141 }
142
143 impl $name {
144 const _ASSERT: () = {
145 $(
146 assert!($msg.len() <= $crate::MESSAGE_BUF_SIZE, "Message buffer overflow, make sure your messages are not beyond MESSAGE_BUF_SIZE");
147 )+
148 };
149
150
151 #[inline(always)]
152 ///Converts self into raw integer code
153 pub const fn raw_code(&self) -> $crate::types::c_int {
154 *self as _
155 }
156
157 ///Returns error code category pointer
158 pub const fn category() -> &'static $crate::Category {
159 let _ = Self::_ASSERT;
160
161 static CATEGORY: $crate::Category = $crate::Category {
162 name: core::stringify!($name),
163 message: $name::message,
164 equivalent,
165 is_would_block
166 };
167
168 fn equivalent(code: $crate::types::c_int, other: &$crate::ErrorCode) -> bool {
169 core::ptr::eq(&CATEGORY, other.category()) && code == other.raw_code()
170 }
171
172 fn is_would_block(_: $crate::types::c_int) -> bool {
173 false
174 }
175
176
177 &CATEGORY
178 }
179
180 #[inline(always)]
181 ///Map raw error code to textual representation.
182 pub fn map_code(code: $crate::types::c_int) -> Option<&'static str> {
183 match code {
184 $($code => Some($msg),)+
185 _ => None,
186 }
187 }
188
189 fn message(code: $crate::types::c_int, out: &mut $crate::MessageBuf) -> &str {
190 let msg = match Self::map_code(code) {
191 Some(msg) => msg,
192 None => $crate::utils::generic_map_error_code(code),
193 };
194
195 debug_assert!(msg.len() <= out.len());
196 unsafe {
197 core::ptr::copy_nonoverlapping(msg.as_ptr(), out.as_mut_ptr() as *mut u8, msg.len());
198 core::str::from_utf8_unchecked(
199 core::slice::from_raw_parts(out.as_ptr() as *const u8, msg.len())
200 )
201 }
202 }
203
204 #[inline]
205 ///Converts `self` into error code
206 pub const fn into_error_code(self) -> $crate::ErrorCode {
207 $crate::ErrorCode::new(self.raw_code(), Self::category())
208 }
209 }
210 }
211}
212
213///Interface for error category
214///
215///It is implemented as pointers in order to avoid generics or overhead of fat pointers.
216///
217///## Custom implementation example
218///
219///```rust
220///use error_code::{ErrorCode, Category};
221///use error_code::types::c_int;
222///
223///use core::ptr;
224///
225///static MY_CATEGORY: Category = Category {
226/// name: "MyError",
227/// message,
228/// equivalent,
229/// is_would_block
230///};
231///
232///fn equivalent(code: c_int, other: &ErrorCode) -> bool {
233/// ptr::eq(&MY_CATEGORY, other.category()) && code == other.raw_code()
234///}
235///
236///fn is_would_block(_: c_int) -> bool {
237/// false
238///}
239///
240///fn message(code: c_int, out: &mut error_code::MessageBuf) -> &str {
241/// let msg = match code {
242/// 0 => "Success",
243/// 1 => "Bad",
244/// _ => "Whatever",
245/// };
246///
247/// debug_assert!(msg.len() <= out.len());
248/// unsafe {
249/// ptr::copy_nonoverlapping(msg.as_ptr(), out.as_mut_ptr() as *mut u8, msg.len())
250/// }
251/// msg
252///}
253///
254///#[inline(always)]
255///pub fn my_error(code: c_int) -> ErrorCode {
256/// ErrorCode::new(code, &MY_CATEGORY)
257///}
258///```
259pub struct Category {
260 ///Category name
261 pub name: &'static str,
262 ///Maps error code and writes descriptive error message accordingly.
263 ///
264 ///In case of insufficient buffer, prefer to truncate message or just don't write big ass message.
265 ///
266 ///In case of error, just write generic name.
267 ///
268 ///Returns formatted message as string.
269 pub message: fn(types::c_int, &mut MessageBuf) -> &str,
270 ///Checks whether error code is equivalent to another one.
271 ///
272 ///## Args:
273 ///
274 ///- Raw error code, belonging to this category
275 ///- Another error code being compared against this category.
276 ///
277 ///## Recommendation
278 ///
279 ///Generally error code is equal if it belongs to the same category (use `ptr::eq` to compare
280 ///pointers to `Category`) and raw error codes are equal.
281 pub equivalent: fn(types::c_int, &ErrorCode) -> bool,
282 ///Returns `true` if supplied error code indicates WouldBlock like error.
283 ///
284 ///This should `true` only for errors that indicate operation can be re-tried later.
285 pub is_would_block: fn(types::c_int) -> bool,
286}
287
288#[derive(Copy, Clone)]
289///Describes error code of particular category.
290pub struct ErrorCode {
291 code: types::c_int,
292 category: &'static Category
293}
294
295impl ErrorCode {
296 #[inline]
297 ///Initializes error code with provided category
298 pub const fn new(code: types::c_int, category: &'static Category) -> Self {
299 Self {
300 code,
301 category,
302 }
303 }
304
305 #[inline(always)]
306 ///Creates new POSIX error code.
307 pub fn new_posix(code: types::c_int) -> Self {
308 Self::new(code, &POSIX_CATEGORY)
309 }
310
311 #[inline(always)]
312 ///Creates new System error code.
313 pub fn new_system(code: types::c_int) -> Self {
314 Self::new(code, &SYSTEM_CATEGORY)
315 }
316
317 #[inline]
318 ///Gets last POSIX error
319 pub fn last_posix() -> Self {
320 Self::new_posix(posix::get_last_error())
321 }
322
323 #[inline]
324 ///Gets last System error
325 pub fn last_system() -> Self {
326 Self::new_system(system::get_last_error())
327 }
328
329 #[inline(always)]
330 ///Gets raw error code.
331 pub const fn raw_code(&self) -> types::c_int {
332 self.code
333 }
334
335 #[inline(always)]
336 ///Gets reference to underlying Category.
337 pub const fn category(&self) -> &'static Category {
338 self.category
339 }
340
341 #[inline(always)]
342 ///Returns `true` if underlying error indicates operation can or should be re-tried at later date.
343 pub fn is_would_block(&self) -> bool {
344 (self.category.is_would_block)(self.code)
345 }
346}
347
348impl PartialEq for ErrorCode {
349 #[inline]
350 fn eq(&self, other: &Self) -> bool {
351 (self.category.equivalent)(self.code, other)
352 }
353}
354
355impl Eq for ErrorCode {}
356
357impl hash::Hash for ErrorCode {
358 #[inline]
359 fn hash<H: hash::Hasher>(&self, state: &mut H) {
360 self.code.hash(state);
361 }
362}
363
364impl fmt::Debug for ErrorCode {
365 #[inline]
366 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
367 let mut out = [mem::MaybeUninit::uninit(); MESSAGE_BUF_SIZE];
368 let message = (self.category.message)(self.code, &mut out);
369 fmt.debug_struct(self.category.name).field("code", &self.code).field("message", &message).finish()
370 }
371}
372
373impl fmt::Display for ErrorCode {
374 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
375 let mut out = [mem::MaybeUninit::uninit(); MESSAGE_BUF_SIZE];
376 let message = (self.category.message)(self.code, &mut out);
377 fmt.write_fmt(format_args!("{}({}): {}", self.category.name, self.code, message))
378 }
379}
380
381#[cfg(feature = "std")]
382impl std::error::Error for ErrorCode {}
383
384#[cfg(feature = "std")]
385impl From<std::io::Error> for ErrorCode {
386 #[inline]
387 fn from(err: std::io::Error) -> Self {
388 match err.raw_os_error() {
389 Some(err) => Self::new_posix(err),
390 None => Self::new_posix(-1),
391 }
392 }
393}