shared_error/
std.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under both the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree and the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree.
8 */
9
10use std::error::Error;
11use std::sync::Arc;
12
13use thiserror::Error;
14
15/// SharedError is a simple, cloneable Error wrapper.
16/// It holds the inner error in an Arc to support Clone.
17///
18/// Propagation of errors via `?` converts automatically
19/// to SharedError.
20///
21/// ```
22/// use shared_error::std::*;
23/// use thiserror::Error;
24///
25/// #[derive(Debug, Error)]
26/// enum SomeErrorType {
27///     #[error("Some error variant: {0}")]
28///     SomeErrorVariant(String),
29/// }
30///
31/// fn some_fallible_func() -> Result<(), SharedError<SomeErrorType>> {
32///     let result: Result<(), SomeErrorType> =
33///         Err(SomeErrorType::SomeErrorVariant("some context".to_owned()));
34///     Ok(result?)
35/// }
36///
37/// fn some_caller() {
38///     let result = some_fallible_func();
39///     match result {
40///         Ok(_) => { /* do something */ },
41///         Err(shared_error) => {
42///             // some_func_1_that_consumes_error(shared_error.clone());
43///             // ...
44///             // some_func_N_that_consumes_error(shared_error.clone());
45///         }
46///     }
47/// }
48///
49/// ```
50#[derive(Error, Debug)]
51#[error(transparent)]
52pub struct SharedError<T: Error + 'static> {
53    #[from]
54    error: Arc<T>,
55}
56
57impl<T: Error + 'static> Clone for SharedError<T> {
58    fn clone(&self) -> Self {
59        Self {
60            error: self.error.clone(),
61        }
62    }
63}
64
65impl<T: Error + 'static> From<T> for SharedError<T> {
66    fn from(error: T) -> SharedError<T> {
67        SharedError {
68            error: Arc::new(error),
69        }
70    }
71}
72
73impl<T: Error + 'static> SharedError<T> {
74    /// Return reference to the inner Error.
75    pub fn inner(&self) -> &T {
76        &self.error
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use thiserror::Error;
83
84    use super::*;
85
86    #[derive(Debug, Error)]
87    enum TestError {
88        #[error("Some error: {0}")]
89        SomeError(String),
90    }
91
92    #[test]
93    fn test_convert_to_shared_error() {
94        let error = TestError::SomeError("some context".to_owned());
95        let shared_error: SharedError<_> = error.into();
96        assert_eq!(
97            shared_error.inner().to_string(),
98            "Some error: some context".to_owned()
99        );
100        assert_eq!(
101            shared_error.to_string(),
102            "Some error: some context".to_owned()
103        );
104        assert!(shared_error.source().is_none());
105    }
106
107    #[test]
108    #[allow(clippy::redundant_clone)]
109    fn test_clone_shared_error() {
110        let error = TestError::SomeError("some context".to_owned());
111        let shared_error: SharedError<_> = error.into();
112        let cloned_error = shared_error.clone();
113        assert_eq!(
114            cloned_error.inner().to_string(),
115            "Some error: some context".to_owned()
116        );
117        assert_eq!(
118            cloned_error.to_string(),
119            "Some error: some context".to_owned()
120        );
121        assert!(shared_error.source().is_none());
122    }
123
124    #[test]
125    fn test_convert_to_result_with_shared_error() {
126        fn some_fallible_func() -> Result<(), SharedError<TestError>> {
127            let result: Result<(), TestError> =
128                Err(TestError::SomeError("some context".to_owned()));
129            Ok(result?)
130        }
131
132        let shared_error_result = some_fallible_func();
133        match shared_error_result {
134            Ok(_) => panic!("Can't be an Ok result"),
135            Err(shared_error) => {
136                assert_eq!(
137                    shared_error.to_string(),
138                    "Some error: some context".to_owned()
139                );
140                assert!(shared_error.source().is_none());
141            }
142        }
143    }
144}