eidetica/transaction/
errors.rs1use thiserror::Error;
8
9#[non_exhaustive]
16#[derive(Debug, Error)]
17pub enum TransactionError {
18 #[error("Transaction has already been committed")]
20 TransactionAlreadyCommitted,
21
22 #[error("Empty tips array not allowed for transaction")]
24 EmptyTipsNotAllowed,
25
26 #[error("Invalid tip for transaction: {tip_id}")]
28 InvalidTip { tip_id: String },
29
30 #[error("Entry construction failed: {reason}")]
32 EntryConstructionFailed { reason: String },
33
34 #[error("Entry signing failed for key '{key_name}': {reason}")]
36 EntrySigningFailed { key_name: String, reason: String },
37
38 #[error("Signing key not found: {key_name}")]
40 SigningKeyNotFound { key_name: String },
41
42 #[error("Authentication required but not configured")]
44 AuthenticationRequired,
45
46 #[error("No authentication configuration found")]
48 NoAuthConfiguration,
49
50 #[error("Authentication configuration is corrupted or malformed")]
52 CorruptedAuthConfiguration,
53
54 #[error("Insufficient permissions for operation")]
56 InsufficientPermissions,
57
58 #[error("Entry signature verification failed")]
60 SignatureVerificationFailed,
61
62 #[error("Entry validation failed")]
64 EntryValidationFailed,
65
66 #[error("Store data deserialization failed for '{store}': {reason}")]
68 StoreDeserializationFailed { store: String, reason: String },
69
70 #[error("Backend operation failed during commit: {reason}")]
72 BackendOperationFailed { reason: String },
73}
74
75impl TransactionError {
76 pub fn is_already_committed(&self) -> bool {
78 matches!(self, TransactionError::TransactionAlreadyCommitted)
79 }
80
81 pub fn is_authentication_error(&self) -> bool {
83 matches!(
84 self,
85 TransactionError::SigningKeyNotFound { .. }
86 | TransactionError::AuthenticationRequired
87 | TransactionError::NoAuthConfiguration
88 | TransactionError::CorruptedAuthConfiguration
89 | TransactionError::InsufficientPermissions
90 | TransactionError::SignatureVerificationFailed
91 | TransactionError::EntrySigningFailed { .. }
92 | TransactionError::EntryValidationFailed
93 )
94 }
95
96 pub fn is_entry_error(&self) -> bool {
98 matches!(
99 self,
100 TransactionError::EntryConstructionFailed { .. }
101 | TransactionError::EntrySigningFailed { .. }
102 | TransactionError::SignatureVerificationFailed
103 | TransactionError::EntryValidationFailed
104 )
105 }
106
107 pub fn is_store_error(&self) -> bool {
109 matches!(self, TransactionError::StoreDeserializationFailed { .. })
110 }
111
112 pub fn is_backend_error(&self) -> bool {
114 matches!(self, TransactionError::BackendOperationFailed { .. })
115 }
116
117 pub fn is_validation_error(&self) -> bool {
119 matches!(
120 self,
121 TransactionError::InvalidTip { .. } | TransactionError::EmptyTipsNotAllowed
122 )
123 }
124
125 pub fn store_name(&self) -> Option<&str> {
127 match self {
128 TransactionError::StoreDeserializationFailed { store, .. } => Some(store),
129 _ => None,
130 }
131 }
132
133 pub fn key_name(&self) -> Option<&str> {
135 match self {
136 TransactionError::SigningKeyNotFound { key_name }
137 | TransactionError::EntrySigningFailed { key_name, .. } => Some(key_name),
138 _ => None,
139 }
140 }
141}
142
143impl From<TransactionError> for crate::Error {
145 fn from(err: TransactionError) -> Self {
146 crate::Error::Transaction(err)
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_error_classification() {
156 let auth_err = TransactionError::AuthenticationRequired;
158 assert!(auth_err.is_authentication_error());
159 assert!(!auth_err.is_entry_error());
160
161 let entry_err = TransactionError::EntryConstructionFailed {
163 reason: "test".to_owned(),
164 };
165 assert!(entry_err.is_entry_error());
166 assert!(!entry_err.is_authentication_error());
167
168 let store_err = TransactionError::StoreDeserializationFailed {
170 store: "test_store".to_owned(),
171 reason: "test".to_owned(),
172 };
173 assert!(store_err.is_store_error());
174 assert_eq!(store_err.store_name(), Some("test_store"));
175
176 let validation_err = TransactionError::EmptyTipsNotAllowed;
178 assert!(validation_err.is_validation_error());
179 assert!(!validation_err.is_backend_error());
180 }
181
182 #[test]
183 fn test_already_committed() {
184 let err = TransactionError::TransactionAlreadyCommitted;
185 assert!(err.is_already_committed());
186 }
187
188 #[test]
189 fn test_key_name_extraction() {
190 let err = TransactionError::SigningKeyNotFound {
191 key_name: "test_key".to_owned(),
192 };
193 assert_eq!(err.key_name(), Some("test_key"));
194
195 let other_err = TransactionError::AuthenticationRequired;
196 assert_eq!(other_err.key_name(), None);
197 }
198}