1#![cfg_attr(not(feature = "std"), no_std)]
6#![forbid(unsafe_code)]
7#![cfg_attr(not(test), deny(clippy::unwrap_used))]
8#![cfg_attr(not(test), deny(clippy::expect_used))]
9#![cfg_attr(not(test), deny(clippy::panic))]
10#![warn(missing_docs)]
11#[cfg(not(feature = "std"))]
12extern crate alloc;
13
14mod context;
15mod error;
16
17pub use context::Context;
18pub use error::NexError;
19
20#[cfg(feature = "derive")]
22pub use nexcore_error_derive::Error;
23
24pub type Result<T, E = NexError> = core::result::Result<T, E>;
26
27#[macro_export]
29macro_rules! nexerror {
30 ($fmt:literal) => { $crate::NexError::new(format!($fmt)) };
31 ($fmt:literal, $($arg:tt)*) => { $crate::NexError::new(format!($fmt, $($arg)*)) };
32}
33
34#[macro_export]
36macro_rules! bail {
37 ($msg:literal) => {
38 return core::result::Result::Err($crate::NexError::new(format!($msg)).into())
39 };
40 ($err:expr) => {
41 return core::result::Result::Err($crate::NexError::from($err).into())
42 };
43 ($fmt:literal, $($arg:tt)*) => {
44 return core::result::Result::Err($crate::NexError::new(format!($fmt, $($arg)*)).into())
45 };
46}
47
48#[macro_export]
50macro_rules! ensure {
51 ($cond:expr $(,)?) => {
52 if !($cond) {
53 return core::result::Result::Err($crate::NexError::new(concat!("Condition failed: `", stringify!($cond), "`")).into());
54 }
55 };
56 ($cond:expr, $msg:literal $(,)?) => {
57 if !($cond) {
58 return core::result::Result::Err($crate::NexError::new($msg).into());
59 }
60 };
61 ($cond:expr, $err:expr $(,)?) => {
62 if !($cond) {
63 return core::result::Result::Err($crate::NexError::from($err).into());
64 }
65 };
66 ($cond:expr, $fmt:literal, $($arg:tt)*) => {
67 if !($cond) {
68 return core::result::Result::Err($crate::NexError::new(format!($fmt, $($arg)*)).into());
69 }
70 };
71}
72
73pub mod prelude {
75 pub use crate::{Context, NexError, Result, bail, ensure, nexerror};
76
77 #[cfg(feature = "derive")]
78 pub use crate::Error;
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_nexerror_msg() {
87 let err = nexerror!("test error");
88 assert_eq!(err.to_string(), "test error");
89 }
90
91 #[test]
92 fn test_nexerror_format() {
93 let err = nexerror!("error code: {}", 42);
94 assert_eq!(err.to_string(), "error code: 42");
95 }
96
97 #[cfg(feature = "derive")]
100 mod derive_tests {
101 use super::*;
102
103 #[derive(Debug, Error)]
104 enum TestError {
105 #[error("not found")]
106 NotFound,
107
108 #[error("parse error: {0}")]
109 Parse(String),
110
111 #[error("io failed: {0}")]
112 Io(#[from] std::io::Error),
113
114 #[error("named: {msg}")]
115 Named { msg: String },
116
117 #[error("debug fmt: {0:?}")]
118 DebugFmt(Vec<String>),
119
120 #[error(transparent)]
121 Other(#[from] std::fmt::Error),
122 }
123
124 #[derive(Debug, Error)]
125 #[error("struct error: {msg}")]
126 struct StructError {
127 msg: String,
128 }
129
130 #[test]
131 fn test_unit_variant_display() {
132 let err = TestError::NotFound;
133 assert_eq!(err.to_string(), "not found");
134 }
135
136 #[test]
137 fn test_unnamed_field_display() {
138 let err = TestError::Parse("bad input".into());
139 assert_eq!(err.to_string(), "parse error: bad input");
140 }
141
142 #[test]
143 fn test_named_field_display() {
144 let err = TestError::Named { msg: "oops".into() };
145 assert_eq!(err.to_string(), "named: oops");
146 }
147
148 #[test]
149 fn test_debug_format() {
150 let err = TestError::DebugFmt(vec!["a".into(), "b".into()]);
151 assert_eq!(err.to_string(), "debug fmt: [\"a\", \"b\"]");
152 }
153
154 #[test]
155 fn test_from_io_error() {
156 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "gone");
157 let err: TestError = io_err.into();
158 assert!(err.to_string().contains("io failed"));
159 }
160
161 #[test]
162 fn test_source_from_io() {
163 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "gone");
164 let err: TestError = io_err.into();
165 assert!(std::error::Error::source(&err).is_some());
166 }
167
168 #[test]
169 fn test_transparent_display() {
170 let inner = std::fmt::Error;
171 let err: TestError = inner.into();
172 assert_eq!(err.to_string(), std::fmt::Error.to_string());
173 }
174
175 #[test]
176 fn test_transparent_source() {
177 let inner = std::fmt::Error;
178 let err: TestError = inner.into();
179 assert!(std::error::Error::source(&err).is_some());
180 }
181
182 #[test]
183 fn test_struct_error_display() {
184 let err = StructError {
185 msg: "broken".into(),
186 };
187 assert_eq!(err.to_string(), "struct error: broken");
188 }
189
190 #[test]
191 fn test_unit_no_source() {
192 let err = TestError::NotFound;
193 assert!(std::error::Error::source(&err).is_none());
194 }
195 }
196}