1use core::fmt;
2
3#[cfg(feature = "std")]
4mod backtrace;
5
6mod context;
7pub(super) mod opaque;
8
9use crate::{
10 BoxError,
11 extra::OpaqueError,
12 std::{Box, String},
13};
14
15pub trait ErrorContext: private::SealedErrorContext {
19 type Context;
21
22 type OpaqueContext;
24
25 fn into_box_error(self) -> Self::Context;
27
28 fn into_opaque_error(self) -> Self::OpaqueContext;
30
31 fn context<M>(self, value: M) -> Self::Context
33 where
34 M: fmt::Debug + fmt::Display + Send + Sync + 'static;
35
36 fn context_hex<M>(self, value: M) -> Self::Context
39 where
40 M: fmt::Debug + Send + Sync + 'static;
41
42 fn context_debug<M>(self, value: M) -> Self::Context
45 where
46 M: fmt::Debug + Send + Sync + 'static;
47
48 fn context_field<M>(self, key: &'static str, value: M) -> Self::Context
50 where
51 M: fmt::Debug + fmt::Display + Send + Sync + 'static;
52
53 fn context_str_field<M>(self, key: &'static str, value: M) -> Self::Context
57 where
58 M: Into<String>;
59
60 fn context_hex_field<M>(self, key: &'static str, value: M) -> Self::Context
63 where
64 M: fmt::Debug + Send + Sync + 'static;
65
66 fn context_debug_field<M>(self, key: &'static str, value: M) -> Self::Context
69 where
70 M: fmt::Debug + Send + Sync + 'static;
71
72 fn with_context<C, F>(self, cb: F) -> Self::Context
74 where
75 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
76 F: FnOnce() -> C;
77
78 fn with_context_hex<C, F>(self, cb: F) -> Self::Context
81 where
82 C: fmt::Debug + Send + Sync + 'static,
83 F: FnOnce() -> C;
84
85 fn with_context_debug<C, F>(self, cb: F) -> Self::Context
88 where
89 C: fmt::Debug + Send + Sync + 'static,
90 F: FnOnce() -> C;
91
92 fn with_context_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
94 where
95 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
96 F: FnOnce() -> C;
97
98 fn with_context_str_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
102 where
103 C: Into<String>,
104 F: FnOnce() -> C;
105
106 fn with_context_hex_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
109 where
110 C: fmt::Debug + Send + Sync + 'static,
111 F: FnOnce() -> C;
112
113 fn with_context_debug_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
116 where
117 C: fmt::Debug + Send + Sync + 'static,
118 F: FnOnce() -> C;
119}
120
121impl<T, E: Into<BoxError>> ErrorContext for Result<T, E> {
122 type Context = Result<T, BoxError>;
123 type OpaqueContext = Result<T, OpaqueError>;
124
125 #[inline(always)]
126 fn into_box_error(self) -> Self::Context {
127 self.map_err(Into::into)
128 }
129
130 #[inline(always)]
131 fn into_opaque_error(self) -> Self::OpaqueContext {
132 self.map_err(OpaqueError::from_box_error)
133 }
134
135 #[inline(always)]
136 fn context<M>(self, value: M) -> Self::Context
137 where
138 M: fmt::Debug + fmt::Display + Send + Sync + 'static,
139 {
140 self.map_err(|error| error.context(value))
141 }
142
143 #[inline(always)]
144 fn context_hex<M>(self, value: M) -> Self::Context
145 where
146 M: fmt::Debug + Send + Sync + 'static,
147 {
148 self.map_err(|error| error.context_hex(value))
149 }
150
151 #[inline(always)]
152 fn context_debug<M>(self, value: M) -> Self::Context
153 where
154 M: fmt::Debug + Send + Sync + 'static,
155 {
156 self.map_err(|error| error.context_debug(value))
157 }
158
159 #[inline(always)]
160 fn context_field<M>(self, key: &'static str, value: M) -> Self::Context
161 where
162 M: fmt::Debug + fmt::Display + Send + Sync + 'static,
163 {
164 self.map_err(|error| error.context_field(key, value))
165 }
166
167 #[inline(always)]
168 fn context_str_field<M>(self, key: &'static str, value: M) -> Self::Context
169 where
170 M: Into<String>,
171 {
172 self.map_err(|error| error.context_str_field(key, value))
173 }
174
175 #[inline(always)]
176 fn context_hex_field<M>(self, key: &'static str, value: M) -> Self::Context
177 where
178 M: fmt::Debug + Send + Sync + 'static,
179 {
180 self.map_err(|error| error.context_hex_field(key, value))
181 }
182
183 #[inline(always)]
184 fn context_debug_field<M>(self, key: &'static str, value: M) -> Self::Context
185 where
186 M: fmt::Debug + Send + Sync + 'static,
187 {
188 self.map_err(|error| error.context_debug_field(key, value))
189 }
190
191 #[inline(always)]
192 fn with_context<C, F>(self, cb: F) -> Self::Context
193 where
194 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
195 F: FnOnce() -> C,
196 {
197 self.map_err(|error| error.with_context(cb))
198 }
199
200 #[inline(always)]
201 fn with_context_hex<C, F>(self, cb: F) -> Self::Context
202 where
203 C: fmt::Debug + Send + Sync + 'static,
204 F: FnOnce() -> C,
205 {
206 self.map_err(|error| error.with_context_hex(cb))
207 }
208
209 #[inline(always)]
210 fn with_context_debug<C, F>(self, cb: F) -> Self::Context
211 where
212 C: fmt::Debug + Send + Sync + 'static,
213 F: FnOnce() -> C,
214 {
215 self.map_err(|error| error.with_context_debug(cb))
216 }
217
218 #[inline(always)]
219 fn with_context_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
220 where
221 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
222 F: FnOnce() -> C,
223 {
224 self.map_err(|error| error.with_context_field(key, cb))
225 }
226
227 #[inline(always)]
228 fn with_context_str_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
229 where
230 C: Into<String>,
231 F: FnOnce() -> C,
232 {
233 self.map_err(|error| error.with_context_str_field(key, cb))
234 }
235
236 #[inline(always)]
237 fn with_context_hex_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
238 where
239 C: fmt::Debug + Send + Sync + 'static,
240 F: FnOnce() -> C,
241 {
242 self.map_err(|error| error.with_context_hex_field(key, cb))
243 }
244
245 #[inline(always)]
246 fn with_context_debug_field<C, F>(self, key: &'static str, cb: F) -> Self::Context
247 where
248 C: fmt::Debug + Send + Sync + 'static,
249 F: FnOnce() -> C,
250 {
251 self.map_err(|error| error.with_context_debug_field(key, cb))
252 }
253}
254
255macro_rules! forward_none_context {
259 ($(
260 fn $name:ident < $($gen:ident : [$($bound:tt)*]),+ > ( $($arg:ident : $argty:ty),* ) => ( $($carg:expr),* );
261 )+) => {
262 $(
263 fn $name < $($gen),+ > (self, $($arg : $argty),*) -> Self::Context
264 where $($gen : $($bound)*),+
265 {
266 match self {
267 Some(value) => Ok(value),
268 None => Err(BoxError::from_static_str("Option is None")
269 .context_debug_field("type", core::any::type_name::<Self>())
270 .$name($($carg),*)),
271 }
272 }
273 )+
274 };
275}
276
277impl<T> ErrorContext for Option<T> {
278 type Context = Result<T, BoxError>;
279 type OpaqueContext = Result<T, OpaqueError>;
280
281 fn into_box_error(self) -> Self::Context {
282 match self {
283 Some(value) => Ok(value),
284 None => Err(BoxError::from_static_str("Option is None")
285 .context_debug_field("type", core::any::type_name::<Self>())),
286 }
287 }
288
289 #[inline(always)]
290 fn into_opaque_error(self) -> Self::OpaqueContext {
291 self.into_box_error().into_opaque_error()
292 }
293
294 forward_none_context! {
295 fn context<M: [fmt::Debug + fmt::Display + Send + Sync + 'static]>(value: M) => (value);
296 fn context_hex<M: [fmt::Debug + Send + Sync + 'static]>(value: M) => (value);
297 fn context_debug<M: [fmt::Debug + Send + Sync + 'static]>(value: M) => (value);
298 fn context_field<M: [fmt::Debug + fmt::Display + Send + Sync + 'static]>(key: &'static str, value: M) => (key, value);
299 fn context_str_field<M: [Into<String>]>(key: &'static str, value: M) => (key, value);
300 fn context_hex_field<M: [fmt::Debug + Send + Sync + 'static]>(key: &'static str, value: M) => (key, value);
301 fn context_debug_field<M: [fmt::Debug + Send + Sync + 'static]>(key: &'static str, value: M) => (key, value);
302 fn with_context<C: [fmt::Debug + fmt::Display + Send + Sync + 'static], F: [FnOnce() -> C]>(cb: F) => (cb);
303 fn with_context_hex<C: [fmt::Debug + Send + Sync + 'static], F: [FnOnce() -> C]>(cb: F) => (cb);
304 fn with_context_debug<C: [fmt::Debug + Send + Sync + 'static], F: [FnOnce() -> C]>(cb: F) => (cb);
305 fn with_context_field<C: [fmt::Debug + fmt::Display + Send + Sync + 'static], F: [FnOnce() -> C]>(key: &'static str, cb: F) => (key, cb);
306 fn with_context_str_field<C: [Into<String>], F: [FnOnce() -> C]>(key: &'static str, cb: F) => (key, cb);
307 fn with_context_hex_field<C: [fmt::Debug + Send + Sync + 'static], F: [FnOnce() -> C]>(key: &'static str, cb: F) => (key, cb);
308 fn with_context_debug_field<C: [fmt::Debug + Send + Sync + 'static], F: [FnOnce() -> C]>(key: &'static str, cb: F) => (key, cb);
309 }
310}
311
312pub trait ErrorExt: private::SealedErrorExt {
316 fn into_box_error(self) -> BoxError;
318
319 fn into_opaque_error(self) -> OpaqueError;
325
326 fn context<M>(self, value: M) -> BoxError
328 where
329 M: fmt::Debug + fmt::Display + Send + Sync + 'static;
330
331 fn context_hex<M>(self, value: M) -> BoxError
334 where
335 M: fmt::Debug + Send + Sync + 'static;
336
337 fn context_debug<M>(self, value: M) -> BoxError
340 where
341 M: fmt::Debug + Send + Sync + 'static;
342
343 fn context_field<M>(self, key: &'static str, value: M) -> BoxError
345 where
346 M: fmt::Debug + fmt::Display + Send + Sync + 'static;
347
348 fn context_str_field<M>(self, key: &'static str, value: M) -> BoxError
352 where
353 M: Into<String>;
354
355 fn context_hex_field<M>(self, key: &'static str, value: M) -> BoxError
358 where
359 M: fmt::Debug + Send + Sync + 'static;
360
361 fn context_debug_field<M>(self, key: &'static str, value: M) -> BoxError
364 where
365 M: fmt::Debug + Send + Sync + 'static;
366
367 fn with_context<C, F>(self, cb: F) -> BoxError
369 where
370 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
371 F: FnOnce() -> C;
372
373 fn with_context_hex<C, F>(self, cb: F) -> BoxError
376 where
377 C: fmt::Debug + Send + Sync + 'static,
378 F: FnOnce() -> C;
379
380 fn with_context_debug<C, F>(self, cb: F) -> BoxError
383 where
384 C: fmt::Debug + Send + Sync + 'static,
385 F: FnOnce() -> C;
386
387 fn with_context_field<C, F>(self, key: &'static str, cb: F) -> BoxError
389 where
390 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
391 F: FnOnce() -> C;
392
393 fn with_context_str_field<C, F>(self, key: &'static str, cb: F) -> BoxError
397 where
398 C: Into<String>,
399 F: FnOnce() -> C;
400
401 fn with_context_hex_field<C, F>(self, key: &'static str, cb: F) -> BoxError
404 where
405 C: fmt::Debug + Send + Sync + 'static,
406 F: FnOnce() -> C;
407
408 fn with_context_debug_field<C, F>(self, key: &'static str, cb: F) -> BoxError
411 where
412 C: fmt::Debug + Send + Sync + 'static,
413 F: FnOnce() -> C;
414
415 #[cfg(feature = "std")]
416 fn backtrace(self) -> BoxError;
418}
419
420impl<Error: Into<BoxError>> ErrorExt for Error {
421 #[inline(always)]
422 fn into_box_error(self) -> BoxError {
423 self.into()
424 }
425
426 #[inline(always)]
427 fn into_opaque_error(self) -> OpaqueError {
428 OpaqueError::from_box_error(self)
429 }
430
431 fn context<M>(self, value: M) -> BoxError
432 where
433 M: fmt::Debug + fmt::Display + Send + Sync + 'static,
434 {
435 let mut err = self.into();
436
437 if let Some(existing) = err.downcast_mut::<self::context::ErrorWithContext>() {
438 existing.insert_value(value);
439 return err;
440 }
441
442 let mut wrapped = self::context::ErrorWithContext::new(err);
443 wrapped.insert_value(value);
444 Box::new(wrapped)
445 }
446
447 #[inline(always)]
448 fn context_hex<M>(self, value: M) -> BoxError
449 where
450 M: fmt::Debug + Send + Sync + 'static,
451 {
452 self.context(self::context::HexContextValue(value))
453 }
454
455 #[inline(always)]
456 fn context_debug<M>(self, value: M) -> BoxError
457 where
458 M: fmt::Debug + Send + Sync + 'static,
459 {
460 self.context(self::context::DebugContextValue(value))
461 }
462
463 fn context_field<M>(self, key: &'static str, value: M) -> BoxError
464 where
465 M: fmt::Debug + fmt::Display + Send + Sync + 'static,
466 {
467 let mut err = self.into();
468
469 if let Some(existing) = err.downcast_mut::<self::context::ErrorWithContext>() {
470 existing.insert_key_value(key, value);
471 return err;
472 }
473
474 let mut wrapped = self::context::ErrorWithContext::new(err);
475 wrapped.insert_key_value(key, value);
476 Box::new(wrapped)
477 }
478
479 fn context_str_field<M>(self, key: &'static str, value: M) -> BoxError
480 where
481 M: Into<String>,
482 {
483 let mut err = self.into();
484
485 if let Some(existing) = err.downcast_mut::<self::context::ErrorWithContext>() {
486 existing.insert_key_value_str(key, value);
487 return err;
488 }
489
490 let mut wrapped = self::context::ErrorWithContext::new(err);
491 wrapped.insert_key_value_str(key, value);
492 Box::new(wrapped)
493 }
494
495 #[inline(always)]
496 fn context_hex_field<M>(self, key: &'static str, value: M) -> BoxError
497 where
498 M: fmt::Debug + Send + Sync + 'static,
499 {
500 self.context_field(key, self::context::HexContextValue(value))
501 }
502
503 #[inline(always)]
504 fn context_debug_field<M>(self, key: &'static str, value: M) -> BoxError
505 where
506 M: fmt::Debug + Send + Sync + 'static,
507 {
508 self.context_field(key, self::context::DebugContextValue(value))
509 }
510
511 #[inline(always)]
512 fn with_context<C, F>(self, cb: F) -> BoxError
513 where
514 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
515 F: FnOnce() -> C,
516 {
517 self.context(cb())
518 }
519
520 #[inline(always)]
521 fn with_context_hex<C, F>(self, cb: F) -> BoxError
522 where
523 C: fmt::Debug + Send + Sync + 'static,
524 F: FnOnce() -> C,
525 {
526 self.context(self::context::HexContextValue(cb()))
527 }
528
529 #[inline(always)]
530 fn with_context_debug<C, F>(self, cb: F) -> BoxError
531 where
532 C: fmt::Debug + Send + Sync + 'static,
533 F: FnOnce() -> C,
534 {
535 self.context(self::context::DebugContextValue(cb()))
536 }
537
538 #[inline(always)]
539 fn with_context_field<C, F>(self, key: &'static str, cb: F) -> BoxError
540 where
541 C: fmt::Debug + fmt::Display + Send + Sync + 'static,
542 F: FnOnce() -> C,
543 {
544 self.context_field(key, cb())
545 }
546
547 #[inline(always)]
548 fn with_context_str_field<C, F>(self, key: &'static str, cb: F) -> BoxError
549 where
550 C: Into<String>,
551 F: FnOnce() -> C,
552 {
553 self.context_str_field(key, cb())
554 }
555
556 #[inline(always)]
557 fn with_context_hex_field<C, F>(self, key: &'static str, cb: F) -> BoxError
558 where
559 C: fmt::Debug + Send + Sync + 'static,
560 F: FnOnce() -> C,
561 {
562 self.context_field(key, self::context::HexContextValue(cb()))
563 }
564
565 #[inline(always)]
566 fn with_context_debug_field<C, F>(self, key: &'static str, cb: F) -> BoxError
567 where
568 C: fmt::Debug + Send + Sync + 'static,
569 F: FnOnce() -> C,
570 {
571 self.context_field(key, self::context::DebugContextValue(cb()))
572 }
573
574 #[cfg(feature = "std")]
575 fn backtrace(self) -> BoxError {
576 let source = self.into();
577 Box::new(self::backtrace::ErrorWithBacktrace::new(source))
578 }
579}
580pub trait BoxErrorExt: private::SealedBoxErrorExt {
581 fn from_static_str(e: &'static str) -> Self;
587}
588
589impl BoxErrorExt for BoxError {
590 fn from_static_str(e: &'static str) -> Self {
591 OpaqueError::from_static_str(e).into_box_error()
592 }
593}
594
595mod private {
596 use crate::BoxError;
597
598 pub trait SealedErrorContext {}
604
605 impl<T, E> SealedErrorContext for Result<T, E> where E: Into<crate::BoxError> {}
606 impl<T> SealedErrorContext for Option<T> {}
607
608 pub trait SealedErrorExt {}
609
610 impl<Error: Into<crate::BoxError>> SealedErrorExt for Error {}
611
612 pub trait SealedBoxErrorExt {}
613 impl SealedBoxErrorExt for BoxError {}
614}
615
616#[cfg(test)]
617mod tests {
618 use core::cell::Cell;
619
620 use super::*;
621
622 use crate::StdError;
623
624 #[derive(Debug, Clone)]
625 struct BoomError;
626
627 impl fmt::Display for BoomError {
628 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
629 write!(f, "boom")
630 }
631 }
632
633 impl core::error::Error for BoomError {}
634
635 #[test]
636 fn result_context_adds_context_to_error() {
637 let res: Result<(), BoomError> = Err(BoomError);
638
639 let err = res.context("ctx").unwrap_err();
640 let s = format!("{err}");
641
642 assert!(s.starts_with("boom"), "got: {s:?}");
643 assert!(s.contains(" | "), "got: {s:?}");
644 assert!(s.contains(r#""ctx""#), "got: {s:?}");
645 }
646
647 #[test]
648 fn result_context_field_adds_keyed_context_to_error() {
649 let res: Result<(), BoomError> = Err(BoomError);
650
651 let err = res.context_field("path", "/a,b/c").unwrap_err();
652 let s = format!("{err}");
653
654 assert!(s.starts_with("boom"), "got: {s:?}");
655 assert!(s.contains(r#"path="/a,b/c""#), "got: {s:?}");
656 }
657
658 #[test]
659 fn result_with_context_is_lazy_and_called_once() {
660 let res: Result<(), BoomError> = Err(BoomError);
661
662 let calls = Cell::new(0);
663 let err = res
664 .with_context(|| {
665 calls.set(calls.get() + 1);
666 "lazy"
667 })
668 .unwrap_err();
669
670 assert_eq!(calls.get(), 1);
671
672 let s = format!("{err}");
673 assert!(s.contains(r#""lazy""#), "got: {s:?}");
674 }
675
676 #[test]
677 fn result_with_context_field_is_lazy_and_called_once() {
678 let res: Result<(), BoomError> = Err(BoomError);
679
680 let calls = Cell::new(0);
681 let err = res
682 .with_context_field("k", || {
683 calls.set(calls.get() + 1);
684 "v"
685 })
686 .unwrap_err();
687
688 assert_eq!(calls.get(), 1);
689
690 let s = format!("{err}");
691 assert!(s.contains(r#"k="v""#), "got: {s:?}");
692 }
693
694 #[test]
695 fn option_context_none_returns_error_with_context() {
696 let opt: Option<i32> = None;
697
698 let err = opt.context("missing").unwrap_err();
699 let s = format!("{err}");
700
701 assert!(s.starts_with("Option is None"), "got: {s:?}");
702 assert!(s.contains(r#""missing""#), "got: {s:?}");
703 }
704
705 #[test]
706 fn option_context_field_none_returns_error_with_keyed_context() {
707 let opt: Option<i32> = None;
708
709 let err = opt.context_field("user_id", 42).unwrap_err();
710 let s = format!("{err}");
711
712 assert!(s.starts_with("Option is None"), "got: {s:?}");
713 assert!(s.contains(r#"user_id="42""#), "got: {s:?}");
714 }
715
716 #[test]
717 fn option_with_context_none_is_lazy_and_called_once() {
718 let opt: Option<i32> = None;
719
720 let calls = Cell::new(0);
721 let err = opt
722 .with_context(|| {
723 calls.set(calls.get() + 1);
724 "lazy"
725 })
726 .unwrap_err();
727
728 assert_eq!(calls.get(), 1);
729
730 let s = format!("{err}");
731 assert!(s.contains(r#""lazy""#), "got: {s:?}");
732 }
733
734 #[test]
735 fn option_with_context_field_none_is_lazy_and_called_once() {
736 let opt: Option<i32> = None;
737
738 let calls = Cell::new(0);
739 let err = opt
740 .with_context_field("k", || {
741 calls.set(calls.get() + 1);
742 "v"
743 })
744 .unwrap_err();
745
746 assert_eq!(calls.get(), 1);
747
748 let s = format!("{err}");
749 assert!(s.contains(r#"k="v""#), "got: {s:?}");
750 }
751
752 #[test]
753 fn errorext_context_reuses_existing_context_wrapper() {
754 let err1: BoxError = BoomError.context("a");
756 let err2: BoxError = err1.context("b");
758
759 let s = format!("{err2}");
760
761 assert!(s.contains(r#""a""#), "got: {s:?}");
763 assert!(s.contains(r#""b""#), "got: {s:?}");
764
765 assert_eq!(s.matches(" | ").count(), 1, "got: {s:?}");
767 }
768
769 #[test]
770 fn errorext_context_field_reuses_existing_context_wrapper() {
771 let err1: BoxError = BoomError.context_field("k1", "v1");
772 let err2: BoxError = err1.context_field("k2", "v2");
773
774 let s = format!("{err2}");
775
776 assert!(s.contains(r#"k1="v1""#), "got: {s:?}");
777 assert!(s.contains(r#"k2="v2""#), "got: {s:?}");
778 assert_eq!(s.matches(" | ").count(), 1, "got: {s:?}");
779 }
780
781 #[test]
782 #[cfg(feature = "std")]
783 fn errorext_backtrace_wraps_error_and_preserves_source() {
784 let err: BoxError = BoomError.backtrace();
785
786 assert_eq!(format!("{err}"), "boom");
788
789 let pretty = format!("{err:#}");
791 assert!(pretty.starts_with("boom\n"), "got: {pretty:?}");
792 assert!(
793 pretty.contains("\nBacktrace:\n") || pretty.contains("\nBacktrace:\r\n"),
794 "got: {pretty:?}"
795 );
796
797 let src = err.source().expect("source exists");
799 assert_eq!(src.to_string(), "boom");
800 }
801
802 #[test]
803 fn option_with_context_str_field_none_includes_type_debug_field() {
804 let opt: Option<i32> = None;
805 let err = opt
806 .with_context_str_field("k", || "v".to_owned())
807 .unwrap_err();
808 let s = format!("{err}");
809
810 assert!(s.starts_with("Option is None"), "got: {s:?}");
811 assert!(s.contains("type="), "got: {s:?}");
814 assert!(s.contains("Option<i32>"), "got: {s:?}");
815 assert!(s.contains(r#"k="v""#), "got: {s:?}");
816 }
817
818 #[test]
819 fn option_with_context_hex_field_none_includes_type_debug_field() {
820 let opt: Option<i32> = None;
821 let err = opt.with_context_hex_field("addr", || 0xfeu8).unwrap_err();
822 let s = format!("{err}");
823
824 assert!(s.starts_with("Option is None"), "got: {s:?}");
825 assert!(s.contains("type="), "got: {s:?}");
826 assert!(s.contains("Option<i32>"), "got: {s:?}");
827 assert!(s.contains("addr="), "got: {s:?}");
828 }
829
830 #[test]
831 fn option_with_context_debug_field_none_includes_type_debug_field() {
832 let opt: Option<i32> = None;
833 let err = opt
834 .with_context_debug_field("payload", || ("a", 1u32))
835 .unwrap_err();
836 let s = format!("{err}");
837
838 assert!(s.starts_with("Option is None"), "got: {s:?}");
839 assert!(s.contains("type="), "got: {s:?}");
840 assert!(s.contains("Option<i32>"), "got: {s:?}");
841 assert!(s.contains("payload="), "got: {s:?}");
842 }
843
844 #[test]
845 fn errorcontext_for_result_converts_error_into_boxerror() {
846 #[derive(Debug)]
848 struct MyErr;
849
850 impl core::fmt::Display for MyErr {
851 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
852 write!(f, "myerr")
853 }
854 }
855
856 impl StdError for MyErr {}
857
858 let res: Result<(), MyErr> = Err(MyErr);
860
861 let err = res.context("ctx").unwrap_err();
862 let s = format!("{err}");
863
864 assert!(s.starts_with("myerr"), "got: {s:?}");
865 assert!(s.contains(r#""ctx""#), "got: {s:?}");
866 }
867}