wami_core/error/helpers.rs
1//! Error Helper Functions
2//!
3//! Provides convenient helper functions for creating common error types.
4
5use crate::error::{AmiError, Result};
6
7/// Helper functions for creating common AmiError variants
8impl AmiError {
9 /// Create a "Resource not found" error with a formatted message
10 ///
11 /// # Example
12 ///
13 /// ```rust
14 /// use wami_core::error::AmiError;
15 ///
16 /// let error = AmiError::resource_not_found("User", "alice");
17 /// assert!(error.to_string().contains("User: alice"));
18 /// ```
19 pub fn resource_not_found(resource_type: &str, resource_id: &str) -> Self {
20 AmiError::ResourceNotFound {
21 resource: format!("{}: {}", resource_type, resource_id),
22 }
23 }
24
25 /// Create a "Permission denied" error with action and resource context
26 ///
27 /// # Example
28 ///
29 /// ```rust
30 /// use wami_core::error::AmiError;
31 ///
32 /// let error = AmiError::permission_denied("delete", "User: alice");
33 /// assert!(error.to_string().contains("Cannot delete"));
34 /// ```
35 pub fn permission_denied(action: &str, resource: &str) -> Self {
36 AmiError::PermissionDenied {
37 reason: format!("Cannot {} on {}", action, resource),
38 }
39 }
40
41 /// Create an "Access denied" error with a detailed message
42 ///
43 /// # Example
44 ///
45 /// ```rust
46 /// use wami_core::error::AmiError;
47 ///
48 /// let error = AmiError::access_denied("User alice does not have permission to delete users");
49 /// ```
50 pub fn access_denied(message: impl Into<String>) -> Self {
51 AmiError::AccessDenied {
52 message: message.into(),
53 }
54 }
55
56 /// Create an "Invalid parameter" error
57 ///
58 /// # Example
59 ///
60 /// ```rust
61 /// use wami_core::error::AmiError;
62 ///
63 /// let error = AmiError::invalid_parameter("User name cannot be empty");
64 /// ```
65 pub fn invalid_parameter(message: impl Into<String>) -> Self {
66 AmiError::InvalidParameter {
67 message: message.into(),
68 }
69 }
70
71 /// Create a "Resource already exists" error
72 ///
73 /// # Example
74 ///
75 /// ```rust
76 /// use wami_core::error::AmiError;
77 ///
78 /// let error = AmiError::resource_exists("User: alice");
79 /// ```
80 pub fn resource_exists(resource: impl Into<String>) -> Self {
81 AmiError::ResourceExists {
82 resource: resource.into(),
83 }
84 }
85
86 /// Create a "Resource limit exceeded" error
87 ///
88 /// # Example
89 ///
90 /// ```rust
91 /// use wami_core::error::AmiError;
92 ///
93 /// let error = AmiError::resource_limit_exceeded("AccessKey", 2);
94 /// ```
95 pub fn resource_limit_exceeded(resource_type: &str, limit: usize) -> Self {
96 AmiError::ResourceLimitExceeded {
97 resource_type: resource_type.to_string(),
98 limit,
99 }
100 }
101
102 /// Create an "Operation not supported" error
103 ///
104 /// # Example
105 ///
106 /// ```rust
107 /// use wami_core::error::AmiError;
108 ///
109 /// let error = AmiError::operation_not_supported("batch_delete");
110 /// ```
111 pub fn operation_not_supported(operation: impl Into<String>) -> Self {
112 AmiError::OperationNotSupported {
113 operation: operation.into(),
114 }
115 }
116}
117
118/// Extension trait for Option to provide convenient error handling
119///
120/// # Example
121///
122/// ```rust
123/// use wami_core::error::{Result, OptionExt};
124///
125/// fn example() -> Result<String> {
126/// let user: Option<String> = Some("alice".to_string());
127/// let result = user.or_not_found("User", "alice")?;
128/// Ok(result)
129/// }
130/// ```
131#[allow(clippy::result_large_err)]
132pub trait OptionExt<T> {
133 /// Convert None to a ResourceNotFound error
134 fn or_not_found(self, resource_type: &str, resource_id: &str) -> Result<T>;
135}
136
137impl<T> OptionExt<T> for Option<T> {
138 fn or_not_found(self, resource_type: &str, resource_id: &str) -> Result<T> {
139 self.ok_or_else(|| AmiError::resource_not_found(resource_type, resource_id))
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_resource_not_found() {
149 let error = AmiError::resource_not_found("User", "alice");
150 assert!(matches!(error, AmiError::ResourceNotFound { .. }));
151 assert!(error.to_string().contains("User: alice"));
152 }
153
154 #[test]
155 fn test_permission_denied() {
156 let error = AmiError::permission_denied("delete", "User: alice");
157 assert!(matches!(error, AmiError::PermissionDenied { .. }));
158 assert!(error.to_string().contains("Cannot delete"));
159 }
160
161 #[test]
162 fn test_or_not_found_some() {
163 let option: Option<String> = Some("value".to_string());
164 let result = option.or_not_found("Resource", "id");
165 assert!(result.is_ok());
166 assert_eq!(result.unwrap(), "value");
167 }
168
169 #[test]
170 fn test_or_not_found_none() {
171 let option: Option<String> = None;
172 let result = option.or_not_found("User", "alice");
173 assert!(result.is_err());
174 assert!(matches!(
175 result.unwrap_err(),
176 AmiError::ResourceNotFound { .. }
177 ));
178 }
179}