Skip to main content

reinhardt_admin/types/
errors.rs

1//! Error types for admin panel
2
3use thiserror::Error;
4
5/// Admin panel error type
6#[derive(Debug, Error)]
7pub enum AdminError {
8	/// Model not registered with admin
9	#[error("Model '{0}' is not registered with admin")]
10	ModelNotRegistered(String),
11
12	/// Permission denied
13	#[error("Permission denied: {0}")]
14	PermissionDenied(String),
15
16	/// Invalid action
17	#[error("Invalid action: {0}")]
18	InvalidAction(String),
19
20	/// Database error
21	#[error("Database error: {0}")]
22	DatabaseError(String),
23
24	/// Validation error
25	#[error("Validation error: {0}")]
26	ValidationError(String),
27
28	/// Template rendering error
29	#[error("Template rendering error: {0}")]
30	TemplateError(String),
31}
32
33/// Result type for admin panel operations
34pub type AdminResult<T> = Result<T, AdminError>;
35
36#[cfg(all(test, server))]
37mod tests {
38	use super::*;
39	use rstest::rstest;
40
41	#[rstest]
42	#[case::model_not_registered(
43		AdminError::ModelNotRegistered("User".to_string()),
44		"Model 'User' is not registered with admin"
45	)]
46	#[case::permission_denied(
47		AdminError::PermissionDenied("insufficient role".to_string()),
48		"Permission denied: insufficient role"
49	)]
50	#[case::invalid_action(
51		AdminError::InvalidAction("bulk_delete".to_string()),
52		"Invalid action: bulk_delete"
53	)]
54	#[case::database_error(
55		AdminError::DatabaseError("connection timeout".to_string()),
56		"Database error: connection timeout"
57	)]
58	#[case::validation_error(
59		AdminError::ValidationError("email is required".to_string()),
60		"Validation error: email is required"
61	)]
62	#[case::template_error(
63		AdminError::TemplateError("missing variable".to_string()),
64		"Template rendering error: missing variable"
65	)]
66	fn test_admin_error_display_all_variants(#[case] error: AdminError, #[case] expected: &str) {
67		// Act
68		let display = error.to_string();
69
70		// Assert
71		assert_eq!(display, expected);
72	}
73
74	#[cfg(server)]
75	#[rstest]
76	#[case::model_not_registered(
77		AdminError::ModelNotRegistered("Article".to_string()),
78		"Article"
79	)]
80	#[case::permission_denied(
81		AdminError::PermissionDenied("no access".to_string()),
82		"no access"
83	)]
84	#[case::invalid_action(
85		AdminError::InvalidAction("export".to_string()),
86		"export"
87	)]
88	#[case::database_error(
89		AdminError::DatabaseError("deadlock".to_string()),
90		"deadlock"
91	)]
92	#[case::validation_error(
93		AdminError::ValidationError("too long".to_string()),
94		"too long"
95	)]
96	#[case::template_error(
97		AdminError::TemplateError("syntax error".to_string()),
98		"syntax error"
99	)]
100	fn test_admin_error_to_core_error_mapping(
101		#[case] admin_error: AdminError,
102		#[case] expected_msg: &str,
103	) {
104		// Act
105		let core_error: reinhardt_core::exception::Error = admin_error.into();
106
107		// Assert
108		let display = core_error.to_string();
109		assert!(
110			display.contains(expected_msg),
111			"Core error display '{}' should contain '{}'",
112			display,
113			expected_msg
114		);
115	}
116
117	#[rstest]
118	fn test_admin_error_model_not_registered_includes_model_name() {
119		// Arrange
120		let model_name = "CustomWidget";
121		let error = AdminError::ModelNotRegistered(model_name.to_string());
122
123		// Act
124		let display = error.to_string();
125
126		// Assert
127		assert!(
128			display.contains(model_name),
129			"Error message '{}' should contain model name '{}'",
130			display,
131			model_name
132		);
133	}
134
135	#[rstest]
136	fn test_admin_error_permission_denied_includes_reason() {
137		// Arrange
138		let reason = "user lacks admin privileges";
139		let error = AdminError::PermissionDenied(reason.to_string());
140
141		// Act
142		let display = error.to_string();
143
144		// Assert
145		assert!(
146			display.contains(reason),
147			"Error message '{}' should contain reason '{}'",
148			display,
149			reason
150		);
151	}
152
153	#[rstest]
154	#[case::model_not_registered(AdminError::ModelNotRegistered("M".to_string()))]
155	#[case::permission_denied(AdminError::PermissionDenied("P".to_string()))]
156	#[case::invalid_action(AdminError::InvalidAction("A".to_string()))]
157	#[case::database_error(AdminError::DatabaseError("D".to_string()))]
158	#[case::validation_error(AdminError::ValidationError("V".to_string()))]
159	#[case::template_error(AdminError::TemplateError("T".to_string()))]
160	fn test_admin_error_debug_format_differs_from_display(#[case] error: AdminError) {
161		// Act
162		let debug_output = format!("{:?}", error);
163		let display_output = format!("{}", error);
164
165		// Assert
166		assert_ne!(
167			debug_output, display_output,
168			"Debug '{debug_output}' and Display '{display_output}' should differ"
169		);
170	}
171}
172
173/// Convert AdminError to reinhardt_core::exception::Error for seamless error handling
174#[cfg(server)]
175impl From<AdminError> for reinhardt_core::exception::Error {
176	fn from(err: AdminError) -> Self {
177		match err {
178			AdminError::ModelNotRegistered(msg) => reinhardt_core::exception::Error::NotFound(msg),
179			AdminError::PermissionDenied(msg) => {
180				reinhardt_core::exception::Error::Authorization(msg)
181			}
182			AdminError::InvalidAction(msg) => reinhardt_core::exception::Error::Http(msg),
183			AdminError::DatabaseError(msg) => reinhardt_core::exception::Error::Database(msg),
184			AdminError::ValidationError(msg) => reinhardt_core::exception::Error::Validation(msg),
185			AdminError::TemplateError(msg) => {
186				reinhardt_core::exception::Error::Other(anyhow::anyhow!(msg))
187			}
188		}
189	}
190}