1use std::borrow::Cow;
5use std::error::Error as StdError;
6
7use crate::{OhnoCore, TraceInfo};
8
9pub trait ErrorTrace {
14 fn add_error_trace(&mut self, trace: TraceInfo);
18}
19
20pub trait ErrorTraceExt: ErrorTrace {
26 #[must_use]
28 fn error_trace(mut self, trace: impl Into<Cow<'static, str>>) -> Self
29 where
30 Self: Sized,
31 {
32 self.add_error_trace(TraceInfo::new(trace));
33 self
34 }
35
36 #[must_use]
38 fn detailed_error_trace(mut self, trace: impl Into<Cow<'static, str>>, file: &'static str, line: u32) -> Self
39 where
40 Self: Sized,
41 {
42 self.add_error_trace(TraceInfo::detailed(trace, file, line));
43 self
44 }
45
46 #[must_use]
48 fn with_error_trace<F, R>(mut self, f: F) -> Self
49 where
50 F: FnOnce() -> R,
51 R: Into<Cow<'static, str>>,
52 Self: Sized,
53 {
54 self.add_error_trace(TraceInfo::new(f()));
55 self
56 }
57
58 #[must_use]
60 fn with_detailed_error_trace<F, R>(mut self, f: F, file: &'static str, line: u32) -> Self
61 where
62 F: FnOnce() -> R,
63 R: Into<Cow<'static, str>>,
64 Self: Sized,
65 {
66 self.add_error_trace(TraceInfo::detailed(f(), file, line));
67 self
68 }
69}
70
71impl ErrorTrace for OhnoCore {
72 fn add_error_trace(&mut self, trace: TraceInfo) {
73 self.data.context.push(trace);
74 }
75}
76
77impl<T, E> ErrorTrace for Result<T, E>
78where
79 E: StdError + ErrorTrace,
80{
81 fn add_error_trace(&mut self, trace: TraceInfo) {
82 if let Err(e) = self {
83 e.add_error_trace(trace);
84 }
85 }
86}
87
88impl<T: ErrorTrace> ErrorTraceExt for T {}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[derive(Default, ohno::Error)]
96 pub struct TestError {
97 pub data: OhnoCore,
98 }
99
100 #[test]
101 fn test_error_trace() {
102 let mut error = TestError::default();
103 error.add_error_trace(TraceInfo::new("Test trace"));
104 assert_eq!(error.data.data.context.len(), 1);
105 assert_eq!(error.data.data.context[0].message, "Test trace");
106 assert!(error.data.data.context[0].location.is_none());
107
108 error.add_error_trace(TraceInfo::detailed("Test trace", "test.rs", 10));
109 assert_eq!(error.data.data.context.len(), 2);
110 assert_eq!(error.data.data.context[1].message, "Test trace");
111 let location = error.data.data.context[1].location.as_ref().unwrap();
112 assert_eq!(location.file, "test.rs");
113 assert_eq!(location.line, 10);
114 }
115
116 #[test]
117 fn test_error_trace_ext() {
118 let error = TestError::default();
119 let mut result: Result<(), _> = Err(error);
120
121 result.add_error_trace(TraceInfo::new("Immediate trace"));
122
123 let err = result.unwrap_err();
124 assert_eq!(err.data.data.context.len(), 1);
125 assert_eq!(err.data.data.context[0].message, "Immediate trace");
126 assert!(err.data.data.context[0].location.is_none());
127
128 result = Err(err).detailed_error_trace("Detailed trace", "test.rs", 20);
129 let err = result.unwrap_err();
130
131 assert_eq!(err.data.data.context.len(), 2);
132 assert_eq!(err.data.data.context[1].message, "Detailed trace");
133 let location = err.data.data.context[1].location.as_ref().unwrap();
134 assert_eq!(location.file, "test.rs");
135 assert_eq!(location.line, 20);
136 }
137}