1use core::{error::Error as StdError, fmt};
2
3#[cfg(feature = "alloc")]
4use alloc::boxed::Box;
5#[cfg(feature = "alloc")]
6use core::ops::Deref;
7
8#[cfg(feature = "valuable")]
9use alloc::string::ToString;
10#[cfg(feature = "valuable")]
11use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct TooShortError {
16 min: usize,
17 actual: usize,
18}
19
20impl TooShortError {
21 pub const fn new(min: usize, actual: usize) -> Self {
23 Self { min, actual }
24 }
25
26 pub const fn min(&self) -> usize {
28 self.min
29 }
30
31 pub const fn actual(&self) -> usize {
33 self.actual
34 }
35}
36
37impl fmt::Display for TooShortError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(
40 f,
41 "is too short (min {} chars, got {})",
42 self.min, self.actual
43 )
44 }
45}
46
47impl StdError for TooShortError {}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct TooLongError {
52 max: usize,
53 actual: usize,
54}
55
56impl TooLongError {
57 pub const fn new(max: usize, actual: usize) -> Self {
59 Self { max, actual }
60 }
61
62 pub const fn max(&self) -> usize {
64 self.max
65 }
66
67 pub const fn actual(&self) -> usize {
69 self.actual
70 }
71}
72
73impl fmt::Display for TooLongError {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(
76 f,
77 "is too long (max {} chars, got {})",
78 self.max, self.actual
79 )
80 }
81}
82
83impl StdError for TooLongError {}
84
85#[derive(Debug, Copy, Clone, PartialEq, Eq)]
86enum NumericValueRepr {
87 Signed(i128),
88 Unsigned(u128),
89}
90
91#[derive(Debug, Copy, Clone, PartialEq, Eq)]
97pub struct NumericValue(NumericValueRepr);
98
99impl NumericValue {
100 pub const fn from_i128(value: i128) -> Self {
102 Self(NumericValueRepr::Signed(value))
103 }
104
105 pub const fn from_u128(value: u128) -> Self {
107 Self(NumericValueRepr::Unsigned(value))
108 }
109
110 pub fn as_i64(self) -> Option<i64> {
112 match self.0 {
113 NumericValueRepr::Signed(value) => i64::try_from(value).ok(),
114 NumericValueRepr::Unsigned(value) => i64::try_from(value).ok(),
115 }
116 }
117
118 pub fn as_u64(self) -> Option<u64> {
120 match self.0 {
121 NumericValueRepr::Signed(value) => u64::try_from(value).ok(),
122 NumericValueRepr::Unsigned(value) => u64::try_from(value).ok(),
123 }
124 }
125
126 pub fn as_i128(self) -> Option<i128> {
128 match self.0 {
129 NumericValueRepr::Signed(value) => Some(value),
130 NumericValueRepr::Unsigned(value) => i128::try_from(value).ok(),
131 }
132 }
133
134 pub fn as_u128(self) -> Option<u128> {
136 match self.0 {
137 NumericValueRepr::Signed(value) => u128::try_from(value).ok(),
138 NumericValueRepr::Unsigned(value) => Some(value),
139 }
140 }
141}
142
143impl fmt::Display for NumericValue {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 match self.0 {
146 NumericValueRepr::Signed(value) => write!(f, "{value}"),
147 NumericValueRepr::Unsigned(value) => write!(f, "{value}"),
148 }
149 }
150}
151
152macro_rules! impl_signed_numeric_value_from {
153 ($($ty:ty),* $(,)?) => {
154 $(
155 impl From<$ty> for NumericValue {
156 fn from(value: $ty) -> Self {
157 Self::from_i128(i128::from(value))
158 }
159 }
160 )*
161 };
162}
163
164macro_rules! impl_unsigned_numeric_value_from {
165 ($($ty:ty),* $(,)?) => {
166 $(
167 impl From<$ty> for NumericValue {
168 fn from(value: $ty) -> Self {
169 Self::from_u128(u128::from(value))
170 }
171 }
172 )*
173 };
174}
175
176impl_signed_numeric_value_from!(i8, i16, i32, i64, i128);
177impl_unsigned_numeric_value_from!(u8, u16, u32, u64, u128);
178
179#[cfg(feature = "valuable")]
180impl Valuable for NumericValue {
181 fn as_value(&self) -> Value<'_> {
182 match self.0 {
183 NumericValueRepr::Signed(value) => Value::I128(value),
184 NumericValueRepr::Unsigned(value) => Value::U128(value),
185 }
186 }
187
188 fn visit(&self, visit: &mut dyn Visit) {
189 visit.visit_value(self.as_value());
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct OutOfRangeNumericError {
196 actual: NumericValue,
197 lower_bound: Option<NumericValue>,
198 upper_bound: Option<NumericValue>,
199}
200
201impl OutOfRangeNumericError {
202 pub const fn new(actual: NumericValue) -> Self {
204 Self {
205 actual,
206 lower_bound: None,
207 upper_bound: None,
208 }
209 }
210
211 #[must_use]
213 pub const fn with_lower_bound(self, lower_bound: NumericValue) -> Self {
214 Self {
215 actual: self.actual,
216 lower_bound: Some(lower_bound),
217 upper_bound: self.upper_bound,
218 }
219 }
220
221 #[must_use]
223 pub const fn with_upper_bound(self, upper_bound: NumericValue) -> Self {
224 Self {
225 actual: self.actual,
226 lower_bound: self.lower_bound,
227 upper_bound: Some(upper_bound),
228 }
229 }
230
231 pub const fn actual(&self) -> NumericValue {
233 self.actual
234 }
235
236 pub const fn lower_bound(&self) -> Option<NumericValue> {
238 self.lower_bound
239 }
240
241 pub const fn upper_bound(&self) -> Option<NumericValue> {
243 self.upper_bound
244 }
245}
246
247impl fmt::Display for OutOfRangeNumericError {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 let actual = self.actual;
250 match (self.lower_bound, self.upper_bound) {
251 (Some(lower_bound), Some(upper_bound)) => {
252 write!(
253 f,
254 "out of range (value {actual}, lower bound {lower_bound}, upper bound {upper_bound})"
255 )
256 }
257 (Some(lower_bound), None) => {
258 write!(
259 f,
260 "out of range (value {actual}, lower bound {lower_bound})"
261 )
262 }
263 (None, Some(upper_bound)) => {
264 write!(
265 f,
266 "out of range (value {actual}, upper bound {upper_bound})"
267 )
268 }
269 (None, None) => write!(f, "out of range (value {actual})"),
270 }
271 }
272}
273
274impl StdError for OutOfRangeNumericError {}
275
276#[cfg(feature = "valuable")]
277static OUT_OF_RANGE_NUMERIC_ERROR_FIELDS: &[NamedField<'static>] = &[
278 NamedField::new("actual"),
279 NamedField::new("lower_bound"),
280 NamedField::new("upper_bound"),
281];
282
283#[cfg(feature = "valuable")]
284impl Valuable for OutOfRangeNumericError {
285 fn as_value(&self) -> Value<'_> {
286 Value::Structable(self)
287 }
288
289 fn visit(&self, visit: &mut dyn Visit) {
290 let values = [
291 self.actual.as_value(),
292 self.lower_bound.as_value(),
293 self.upper_bound.as_value(),
294 ];
295 visit.visit_named_fields(&NamedValues::new(
296 OUT_OF_RANGE_NUMERIC_ERROR_FIELDS,
297 &values,
298 ));
299 }
300}
301
302#[cfg(feature = "valuable")]
303impl Structable for OutOfRangeNumericError {
304 fn definition(&self) -> StructDef<'_> {
305 StructDef::new_static(
306 "OutOfRangeNumericError",
307 Fields::Named(OUT_OF_RANGE_NUMERIC_ERROR_FIELDS),
308 )
309 }
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
314pub struct InvalidCharError {
315 index: usize,
316 ch: char,
317}
318
319impl InvalidCharError {
320 pub const fn new(index: usize, ch: char) -> Self {
322 Self { index, ch }
323 }
324
325 pub const fn index(&self) -> usize {
327 self.index
328 }
329
330 pub const fn ch(&self) -> char {
332 self.ch
333 }
334}
335
336impl fmt::Display for InvalidCharError {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 write!(
339 f,
340 "contains invalid character '{}' at index {}",
341 self.ch, self.index
342 )
343 }
344}
345
346impl StdError for InvalidCharError {}
347
348pub trait VouchedError: StdError + Send + Sync + 'static {
353 fn as_too_short(&self) -> Option<&TooShortError> {
355 None
356 }
357 fn as_too_long(&self) -> Option<&TooLongError> {
359 None
360 }
361 fn as_out_of_range_numeric(&self) -> Option<&OutOfRangeNumericError> {
363 None
364 }
365 fn as_invalid_char(&self) -> Option<&InvalidCharError> {
367 None
368 }
369}
370
371#[cfg(feature = "alloc")]
416#[derive(Debug)]
417pub struct Error(Box<dyn VouchedError>);
418
419#[cfg(feature = "alloc")]
420impl Error {
421 pub fn new(inner: Box<dyn VouchedError>) -> Self {
423 Self(inner)
424 }
425
426 pub fn into_inner(self) -> Box<dyn VouchedError> {
428 self.0
429 }
430}
431
432#[cfg(feature = "alloc")]
433impl fmt::Display for Error {
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 self.0.fmt(f)
436 }
437}
438
439#[cfg(feature = "alloc")]
440impl StdError for Error {
441 fn source(&self) -> Option<&(dyn StdError + 'static)> {
442 Some(&*self.0)
443 }
444}
445
446#[cfg(feature = "alloc")]
447impl<E> From<E> for Error
448where
449 E: VouchedError,
450{
451 fn from(e: E) -> Self {
452 Self(Box::new(e))
453 }
454}
455
456#[cfg(feature = "alloc")]
457impl Deref for Error {
458 type Target = dyn VouchedError;
459
460 fn deref(&self) -> &Self::Target {
461 &*self.0
462 }
463}
464
465#[cfg(feature = "alloc")]
466impl AsRef<dyn VouchedError> for Error {
467 fn as_ref(&self) -> &(dyn VouchedError + 'static) {
468 &*self.0
469 }
470}
471
472#[cfg(feature = "valuable")]
473static ERROR_FIELDS: &[NamedField<'static>] = &[NamedField::new("message")];
474
475#[cfg(feature = "valuable")]
476impl Valuable for Error {
477 fn as_value(&self) -> Value<'_> {
478 Value::Structable(self)
479 }
480
481 fn visit(&self, visit: &mut dyn Visit) {
482 let message = self.to_string();
483 let values = [message.as_value()];
484 visit.visit_named_fields(&NamedValues::new(ERROR_FIELDS, &values));
485 }
486}
487
488#[cfg(feature = "valuable")]
489impl Structable for Error {
490 fn definition(&self) -> StructDef<'_> {
491 StructDef::new_static("Error", Fields::Named(ERROR_FIELDS))
492 }
493}