Skip to main content

orion_error/traits/
conversion.rs

1use crate::{core::convert_error, DomainReason, ErrorCode, StructError};
2use std::fmt::Display;
3
4pub trait ErrorConv<T, R: DomainReason>: Sized {
5    fn err_conv(self) -> Result<T, StructError<R>>;
6}
7
8pub trait ConvStructError<R: DomainReason>: Sized {
9    fn conv(self) -> StructError<R>;
10}
11
12pub trait ErrorWrap<T, R: DomainReason>: Sized {
13    fn err_wrap(self, reason: R) -> Result<T, StructError<R>>;
14}
15
16pub trait WrapStructError<R: DomainReason>: Sized {
17    fn wrap(self, reason: R) -> StructError<R>;
18}
19
20impl<T, R1, R2> ErrorConv<T, R2> for Result<T, StructError<R1>>
21where
22    R1: DomainReason,
23    R2: DomainReason + From<R1>,
24{
25    fn err_conv(self) -> Result<T, StructError<R2>> {
26        match self {
27            Ok(o) => Ok(o),
28            Err(e) => Err(convert_error::<R1, R2>(e)),
29        }
30    }
31}
32
33impl<R1, R2> ConvStructError<R2> for StructError<R1>
34where
35    R1: DomainReason,
36    R2: DomainReason + From<R1>,
37{
38    fn conv(self) -> StructError<R2> {
39        convert_error::<R1, R2>(self)
40    }
41}
42
43impl<T, R1, R2> ErrorWrap<T, R2> for Result<T, StructError<R1>>
44where
45    R1: DomainReason + ErrorCode + Display + std::fmt::Debug + Send + Sync + 'static,
46    R2: DomainReason,
47{
48    fn err_wrap(self, reason: R2) -> Result<T, StructError<R2>> {
49        self.map_err(|e| e.wrap(reason))
50    }
51}
52
53impl<R1, R2> WrapStructError<R2> for StructError<R1>
54where
55    R1: DomainReason + ErrorCode + Display + std::fmt::Debug + Send + Sync + 'static,
56    R2: DomainReason,
57{
58    fn wrap(self, reason: R2) -> StructError<R2> {
59        StructError::from(reason).with_struct_source(self)
60    }
61}
62
63pub trait ToStructError<R>
64where
65    R: DomainReason,
66{
67    fn to_err(self) -> StructError<R>;
68    fn err_result<T>(self) -> Result<T, StructError<R>>;
69}
70impl<R> ToStructError<R> for R
71where
72    R: DomainReason,
73{
74    fn to_err(self) -> StructError<R> {
75        StructError::from(self)
76    }
77    fn err_result<T>(self) -> Result<T, StructError<R>> {
78        Err(StructError::from(self))
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use std::error::Error as StdError;
85
86    use super::*;
87    use crate::{ErrorCode, ErrorWith, OperationContext, StructError, UvsReason};
88
89    // 定义测试用的 DomainReason
90    #[derive(Debug, Clone, PartialEq, thiserror::Error)]
91    enum TestReason {
92        #[error("test error")]
93        TestError,
94        #[error("{0}")]
95        Uvs(UvsReason),
96    }
97
98    impl ErrorCode for TestReason {
99        fn error_code(&self) -> i32 {
100            match self {
101                TestReason::TestError => 1001,
102                TestReason::Uvs(uvs) => uvs.error_code(),
103            }
104        }
105    }
106
107    impl From<UvsReason> for TestReason {
108        fn from(uvs: UvsReason) -> Self {
109            TestReason::Uvs(uvs)
110        }
111    }
112
113    // 定义另一个 DomainReason 用于测试转换
114    #[derive(Debug, Clone, PartialEq, thiserror::Error)]
115    enum AnotherReason {
116        #[error("another error")]
117        AnotherError,
118        #[error("{0}")]
119        Uvs(UvsReason),
120    }
121
122    impl ErrorCode for AnotherReason {
123        fn error_code(&self) -> i32 {
124            match self {
125                AnotherReason::AnotherError => 2001,
126                AnotherReason::Uvs(uvs) => uvs.error_code(),
127            }
128        }
129    }
130
131    impl From<UvsReason> for AnotherReason {
132        fn from(uvs: UvsReason) -> Self {
133            AnotherReason::Uvs(uvs)
134        }
135    }
136
137    impl From<TestReason> for AnotherReason {
138        fn from(test: TestReason) -> Self {
139            match test {
140                TestReason::TestError => AnotherReason::AnotherError,
141                TestReason::Uvs(uvs) => AnotherReason::Uvs(uvs),
142            }
143        }
144    }
145
146    #[test]
147    fn test_error_conv_trait() {
148        // 测试 ErrorConv trait 的 err_conv 方法
149        let original_result: Result<i32, StructError<TestReason>> =
150            Err(TestReason::TestError.to_err());
151
152        let converted_result: Result<i32, StructError<AnotherReason>> = original_result.err_conv();
153
154        assert!(converted_result.is_err());
155        let converted_error = converted_result.unwrap_err();
156        assert_eq!(converted_error.error_code(), 2001);
157
158        // 测试成功情况下的转换
159        let success_result: Result<i32, StructError<TestReason>> = Ok(42);
160        let converted_success: Result<i32, StructError<AnotherReason>> = success_result.err_conv();
161
162        assert!(converted_success.is_ok());
163        assert_eq!(converted_success.unwrap(), 42);
164    }
165
166    #[test]
167    fn test_conv_struct_error_trait() {
168        // 测试 ConvStructError trait 的 conv 方法
169        let original_error: StructError<TestReason> = TestReason::TestError.to_err();
170
171        let converted_error: StructError<AnotherReason> = original_error.conv();
172
173        assert_eq!(converted_error.error_code(), 2001);
174
175        // 测试带有 UvsReason 的转换
176        let uvs_error: StructError<TestReason> =
177            TestReason::Uvs(UvsReason::network_error()).to_err();
178
179        let converted_uvs_error: StructError<AnotherReason> = uvs_error.conv();
180
181        assert_eq!(converted_uvs_error.error_code(), 202);
182    }
183
184    #[test]
185    fn test_to_struct_error_trait() {
186        // 测试 ToStructError trait 的 to_err 方法
187        let reason = TestReason::TestError;
188        let error: StructError<TestReason> = reason.to_err();
189
190        assert_eq!(error.error_code(), 1001);
191
192        // 测试 ToStructError trait 的 err_result 方法
193        let reason2 = TestReason::TestError;
194        let result: Result<String, StructError<TestReason>> = reason2.err_result();
195
196        assert!(result.is_err());
197        let error_from_result = result.unwrap_err();
198        assert_eq!(error_from_result.error_code(), 1001);
199
200        // 测试使用 UvsReason
201        let uvs_reason1 = UvsReason::validation_error();
202        let uvs_error: StructError<UvsReason> = uvs_reason1.to_err();
203
204        assert_eq!(uvs_error.error_code(), 100);
205
206        let uvs_reason2 = UvsReason::validation_error();
207        let uvs_result: Result<i32, StructError<UvsReason>> = uvs_reason2.err_result();
208        assert!(uvs_result.is_err());
209        assert_eq!(uvs_result.unwrap_err().error_code(), 100);
210    }
211
212    #[test]
213    fn test_err_conv_preserves_source() {
214        let source = std::io::Error::other("db unavailable");
215        let original: Result<i32, StructError<TestReason>> =
216            Err(StructError::from(TestReason::TestError).with_source(source));
217
218        let converted: Result<i32, StructError<AnotherReason>> = original.err_conv();
219        let err = converted.unwrap_err();
220
221        assert_eq!(err.error_code(), 2001);
222        assert_eq!(
223            StdError::source(&err).unwrap().to_string(),
224            "db unavailable"
225        );
226    }
227
228    #[test]
229    fn test_err_wrap_preserves_previous_struct_error_chain() {
230        let original: Result<i32, StructError<TestReason>> =
231            Err(StructError::from(TestReason::TestError)
232                .with_detail("repo layer failed")
233                .with_source(std::io::Error::other("db unavailable")));
234
235        let wrapped: Result<i32, StructError<AnotherReason>> =
236            original.err_wrap(AnotherReason::AnotherError);
237        let err = wrapped.unwrap_err();
238
239        assert_eq!(err.error_code(), 2001);
240        assert_eq!(
241            StdError::source(&err).unwrap().to_string(),
242            "[1001] test error\n  -> Details: repo layer failed\n  -> Source: db unavailable"
243        );
244        assert_eq!(err.root_cause().unwrap().to_string(), "db unavailable");
245        assert_eq!(err.source_chain().len(), 2);
246        assert_eq!(err.source_frames()[0].message, "test error");
247        assert!(err.source_frames()[0]
248            .display
249            .as_ref()
250            .unwrap()
251            .contains("repo layer failed"));
252        assert_eq!(err.source_frames()[0].error_code, Some(1001));
253        assert_eq!(err.source_frames()[0].reason.as_deref(), Some("test error"));
254        assert_eq!(
255            err.source_frames()[0].detail.as_deref(),
256            Some("repo layer failed")
257        );
258        assert_eq!(err.source_frames()[1].message, "db unavailable");
259        assert!(err.source_frames()[1].is_root_cause);
260    }
261
262    #[test]
263    fn test_err_conv_preserves_context_metadata() {
264        let original: Result<i32, StructError<TestReason>> =
265            Err(StructError::from(TestReason::TestError).with(
266                OperationContext::want("load sink defaults")
267                    .with_meta("config.kind", "sink_defaults"),
268            ));
269
270        let converted: Result<i32, StructError<AnotherReason>> = original.err_conv();
271        let err = converted.unwrap_err();
272
273        assert_eq!(
274            err.context_metadata().get_str("config.kind"),
275            Some("sink_defaults")
276        );
277    }
278
279    #[test]
280    fn test_err_wrap_preserves_source_frame_metadata() {
281        let original: Result<i32, StructError<TestReason>> =
282            Err(StructError::from(TestReason::TestError).with(
283                OperationContext::want("load sink defaults")
284                    .with_meta("config.kind", "sink_defaults"),
285            ));
286
287        let wrapped: Result<i32, StructError<AnotherReason>> =
288            original.err_wrap(AnotherReason::AnotherError);
289        let err = wrapped.unwrap_err();
290
291        assert_eq!(
292            err.source_frames()[0].metadata.get_str("config.kind"),
293            Some("sink_defaults")
294        );
295    }
296}