Skip to main content

dynamo_runtime/protocols/
maybe_error.rs

1// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! MaybeError trait for types that may contain error information.
5//!
6//! This module provides the `MaybeError` trait which allows types to represent
7//! either successful data or error states. It integrates with the `DynamoError`
8//! system to provide structured error handling.
9
10use crate::error::DynamoError;
11
12/// A trait for types that may contain error information.
13///
14/// This trait allows a type to represent either a successful value or an error state.
15/// It integrates with `DynamoError` for structured error information.
16///
17/// # Example
18///
19/// ```rust,ignore
20/// use dynamo_runtime::protocols::maybe_error::MaybeError;
21/// use dynamo_runtime::error::DynamoError;
22///
23/// struct MyResponse {
24///     data: Option<String>,
25///     error: Option<DynamoError>,
26/// }
27///
28/// impl MaybeError for MyResponse {
29///     fn from_err(err: impl std::error::Error + 'static) -> Self {
30///         MyResponse {
31///             data: None,
32///             error: Some(DynamoError::from(
33///                 Box::new(err) as Box<dyn std::error::Error + 'static>
34///             )),
35///         }
36///     }
37///
38///     fn err(&self) -> Option<DynamoError> {
39///         self.error.clone()
40///     }
41/// }
42/// ```
43pub trait MaybeError {
44    /// Construct an instance from an error.
45    ///
46    /// The error is converted to a `DynamoError` for serialization.
47    fn from_err(err: impl std::error::Error + 'static) -> Self;
48
49    /// Get the error as a `DynamoError` if this represents an error state.
50    ///
51    /// Returns `Some(DynamoError)` if this instance represents an error, `None` otherwise.
52    fn err(&self) -> Option<DynamoError>;
53
54    /// Check if the current instance represents a success.
55    fn is_ok(&self) -> bool {
56        !self.is_err()
57    }
58
59    /// Check if the current instance represents an error.
60    fn is_err(&self) -> bool {
61        self.err().is_some()
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    struct TestError {
70        error: Option<DynamoError>,
71    }
72
73    impl MaybeError for TestError {
74        fn from_err(err: impl std::error::Error + 'static) -> Self {
75            TestError {
76                error: Some(DynamoError::from(
77                    Box::new(err) as Box<dyn std::error::Error + 'static>
78                )),
79            }
80        }
81
82        fn err(&self) -> Option<DynamoError> {
83            self.error.clone()
84        }
85    }
86
87    #[test]
88    fn test_maybe_error_default_implementations() {
89        let dynamo_err = DynamoError::msg("Test error");
90        let err = TestError::from_err(dynamo_err);
91        assert!(err.err().unwrap().to_string().contains("Test error"));
92        assert!(!err.is_ok());
93        assert!(err.is_err());
94    }
95
96    #[test]
97    fn test_from_std_error() {
98        let std_err = std::io::Error::other("io failure");
99        let test_err = TestError::from_err(std_err);
100
101        assert!(test_err.is_err());
102        assert!(test_err.err().unwrap().to_string().contains("io failure"));
103    }
104
105    #[test]
106    fn test_not_error() {
107        let test = TestError { error: None };
108        assert!(test.is_ok());
109        assert!(!test.is_err());
110        assert!(test.err().is_none());
111    }
112}