1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
9pub enum VtxError {
10 DatabaseError(String),
12
13 SerializationError(String),
15
16 AuthDenied(u16),
18
19 PermissionDenied(String),
21
22 NotFound(String),
24
25 Internal(String),
27}
28
29impl VtxError {
30 pub fn from_host_message(message: impl Into<String>) -> Self {
32 let msg = message.into();
33 let lower = msg.to_lowercase();
34
35 if lower.contains("permission denied") {
36 return VtxError::PermissionDenied(msg);
37 }
38
39 if lower.contains("uuid not found") || lower.contains("not found") {
40 return VtxError::NotFound(msg);
41 }
42
43 VtxError::Internal(msg)
44 }
45}
46
47impl fmt::Display for VtxError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 VtxError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
51 VtxError::SerializationError(msg) => write!(f, "Data serialization error: {}", msg),
52 VtxError::AuthDenied(code) => write!(f, "Authentication denied (Code: {})", code),
53 VtxError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
54 VtxError::NotFound(msg) => write!(f, "Resource not found: {}", msg),
55 VtxError::Internal(msg) => write!(f, "Internal error: {}", msg),
56 }
57 }
58}
59
60impl std::error::Error for VtxError {}
61
62pub type Error = VtxError;
64
65pub type VtxResult<T> = std::result::Result<T, VtxError>;
69
70pub type Result<T> = VtxResult<T>;
72
73#[cfg(test)]
74mod tests {
75 use super::VtxError;
76
77 #[test]
78 fn from_host_message_maps_permission_denied() {
79 let err = VtxError::from_host_message("Permission denied: read only");
80 match err {
81 VtxError::PermissionDenied(msg) => {
82 assert!(msg.contains("Permission denied"));
83 }
84 _ => panic!("expected PermissionDenied"),
85 }
86 }
87
88 #[test]
89 fn from_host_message_maps_not_found() {
90 let err = VtxError::from_host_message("UUID not found: 1234");
91 match err {
92 VtxError::NotFound(msg) => {
93 assert!(msg.contains("not found"));
94 }
95 _ => panic!("expected NotFound"),
96 }
97 }
98
99 #[test]
100 fn from_host_message_defaults_to_internal() {
101 let err = VtxError::from_host_message("something unexpected");
102 match err {
103 VtxError::Internal(msg) => {
104 assert!(msg.contains("unexpected"));
105 }
106 _ => panic!("expected Internal"),
107 }
108 }
109
110 #[test]
111 fn display_formats_errors() {
112 let err = VtxError::AuthDenied(401);
113 assert_eq!(err.to_string(), "Authentication denied (Code: 401)");
114
115 let err = VtxError::DatabaseError("boom".to_string());
116 assert_eq!(err.to_string(), "Database error: boom");
117 }
118}