error.rs (7705B)
1 use std::error::Error; 2 use std::fmt::{self, Debug, Display, Formatter}; 3 4 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 5 pub enum SafeErrorCode { 6 InvalidPublicKey, 7 InvalidSecretKey, 8 InvalidIdentityMetadata, 9 InvalidProfileMetadata, 10 InvalidApplicationState, 11 IdentityAlreadyExists, 12 IdentityNotFound, 13 KeyringUnavailable, 14 CredentialMissing, 15 StorageUnavailable, 16 StorageCorrupt, 17 StorageQuarantined, 18 StorageBackupInvalid, 19 UnsupportedSchemaVersion, 20 RepairUnauthorized, 21 PendingOperationRecoveryRequired, 22 InvalidRelayConfiguration, 23 RelayConnectionFailed, 24 ProfileRefreshFailed, 25 ObserverRegistrationFailed, 26 NativeLibraryLoadFailed, 27 AvailabilityInvalidInput, 28 AvailabilityUnsupportedProfile, 29 AvailabilityScopeMismatch, 30 AvailabilityStaleQuery, 31 AvailabilityCapacity, 32 AvailabilityUnavailable, 33 } 34 35 /// Finite availability failures with fixed public diagnostic messages. 36 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 37 pub enum AvailabilityFailure { 38 InvalidInput, 39 UnsupportedProfile, 40 ScopeMismatch, 41 StaleQuery, 42 Capacity, 43 Unavailable, 44 } 45 46 #[derive(Clone, Copy, Eq, PartialEq)] 47 pub struct SafeMessage(&'static str); 48 49 impl SafeMessage { 50 #[must_use] 51 pub const fn new(message: &'static str) -> Self { 52 Self(message) 53 } 54 55 #[must_use] 56 pub const fn as_str(self) -> &'static str { 57 self.0 58 } 59 } 60 61 impl Debug for SafeMessage { 62 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 63 formatter.debug_tuple("SafeMessage").field(&self.0).finish() 64 } 65 } 66 67 impl Display for SafeMessage { 68 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 69 formatter.write_str(self.0) 70 } 71 } 72 73 #[derive(Clone, Copy, Eq, PartialEq)] 74 pub struct SafeError { 75 code: SafeErrorCode, 76 message: SafeMessage, 77 } 78 79 impl SafeError { 80 #[must_use] 81 pub const fn new(code: SafeErrorCode, message: SafeMessage) -> Self { 82 Self { code, message } 83 } 84 85 #[must_use] 86 pub const fn code(self) -> SafeErrorCode { 87 self.code 88 } 89 90 #[must_use] 91 pub const fn message(self) -> SafeMessage { 92 self.message 93 } 94 } 95 96 impl Debug for SafeError { 97 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 98 formatter 99 .debug_struct("SafeError") 100 .field("code", &self.code) 101 .field("message", &self.message) 102 .finish() 103 } 104 } 105 106 impl Display for SafeError { 107 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 108 Display::fmt(&self.message, formatter) 109 } 110 } 111 112 impl Error for SafeError {} 113 114 impl From<AvailabilityFailure> for SafeError { 115 fn from(failure: AvailabilityFailure) -> Self { 116 let (code, message) = match failure { 117 AvailabilityFailure::InvalidInput => ( 118 SafeErrorCode::AvailabilityInvalidInput, 119 "The availability request is invalid.", 120 ), 121 AvailabilityFailure::UnsupportedProfile => ( 122 SafeErrorCode::AvailabilityUnsupportedProfile, 123 "This availability profile is unsupported.", 124 ), 125 AvailabilityFailure::ScopeMismatch => ( 126 SafeErrorCode::AvailabilityScopeMismatch, 127 "The availability request belongs to another scope.", 128 ), 129 AvailabilityFailure::StaleQuery => ( 130 SafeErrorCode::AvailabilityStaleQuery, 131 "The availability query is stale.", 132 ), 133 AvailabilityFailure::Capacity => ( 134 SafeErrorCode::AvailabilityCapacity, 135 "The availability operation reached its capacity.", 136 ), 137 AvailabilityFailure::Unavailable => ( 138 SafeErrorCode::AvailabilityUnavailable, 139 "Availability discovery is unavailable.", 140 ), 141 }; 142 Self::new(code, SafeMessage::new(message)) 143 } 144 } 145 146 impl From<crate::AvailabilityQueryError> for SafeError { 147 fn from(error: crate::AvailabilityQueryError) -> Self { 148 use crate::AvailabilityQueryError; 149 150 let failure = match error { 151 AvailabilityQueryError::InvalidInput | AvailabilityQueryError::InputTooLarge => { 152 AvailabilityFailure::InvalidInput 153 } 154 AvailabilityQueryError::ScopeMismatch => AvailabilityFailure::ScopeMismatch, 155 AvailabilityQueryError::StaleQuery => AvailabilityFailure::StaleQuery, 156 AvailabilityQueryError::Capacity => AvailabilityFailure::Capacity, 157 }; 158 failure.into() 159 } 160 } 161 162 #[cfg(test)] 163 mod tests { 164 use super::{SafeError, SafeErrorCode, SafeMessage}; 165 166 #[test] 167 fn safe_error_formats_only_a_static_public_message() { 168 let error = SafeError::new( 169 SafeErrorCode::InvalidSecretKey, 170 SafeMessage::new("The secret key is invalid."), 171 ); 172 173 assert_eq!(error.to_string(), "The secret key is invalid."); 174 assert_eq!(error.code(), SafeErrorCode::InvalidSecretKey); 175 assert_eq!(error.message().as_str(), "The secret key is invalid."); 176 assert!(!format!("{error:?}").contains("nsec1unsafe-test-value")); 177 } 178 179 #[test] 180 fn availability_failures_and_query_errors_are_static() { 181 use super::AvailabilityFailure; 182 use crate::AvailabilityQueryError; 183 184 for (failure, code, message) in [ 185 ( 186 AvailabilityFailure::InvalidInput, 187 SafeErrorCode::AvailabilityInvalidInput, 188 "The availability request is invalid.", 189 ), 190 ( 191 AvailabilityFailure::UnsupportedProfile, 192 SafeErrorCode::AvailabilityUnsupportedProfile, 193 "This availability profile is unsupported.", 194 ), 195 ( 196 AvailabilityFailure::ScopeMismatch, 197 SafeErrorCode::AvailabilityScopeMismatch, 198 "The availability request belongs to another scope.", 199 ), 200 ( 201 AvailabilityFailure::StaleQuery, 202 SafeErrorCode::AvailabilityStaleQuery, 203 "The availability query is stale.", 204 ), 205 ( 206 AvailabilityFailure::Capacity, 207 SafeErrorCode::AvailabilityCapacity, 208 "The availability operation reached its capacity.", 209 ), 210 ( 211 AvailabilityFailure::Unavailable, 212 SafeErrorCode::AvailabilityUnavailable, 213 "Availability discovery is unavailable.", 214 ), 215 ] { 216 let error = SafeError::from(failure); 217 assert_eq!(error.code(), code); 218 assert_eq!(error.message().as_str(), message); 219 assert_eq!(error.to_string(), message); 220 assert!(!format!("{error:?}").contains("HCAV_PRIVATE_REMOTE_PAYLOAD")); 221 } 222 for (query, code) in [ 223 ( 224 AvailabilityQueryError::InvalidInput, 225 SafeErrorCode::AvailabilityInvalidInput, 226 ), 227 ( 228 AvailabilityQueryError::InputTooLarge, 229 SafeErrorCode::AvailabilityInvalidInput, 230 ), 231 ( 232 AvailabilityQueryError::ScopeMismatch, 233 SafeErrorCode::AvailabilityScopeMismatch, 234 ), 235 ( 236 AvailabilityQueryError::StaleQuery, 237 SafeErrorCode::AvailabilityStaleQuery, 238 ), 239 ( 240 AvailabilityQueryError::Capacity, 241 SafeErrorCode::AvailabilityCapacity, 242 ), 243 ] { 244 assert_eq!(SafeError::from(query).code(), code); 245 } 246 } 247 }