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}