1use serde::{Serialize, Serializer};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
4#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5pub enum OwsErrorCode {
6 WalletNotFound,
7 ChainNotSupported,
8 InvalidPassphrase,
9 InvalidInput,
10 CaipParseError,
11}
12
13#[derive(Debug, Clone, thiserror::Error)]
14pub enum OwsError {
15 #[error("wallet not found: {id}")]
16 WalletNotFound { id: String },
17
18 #[error("chain not supported: {chain}")]
19 ChainNotSupported { chain: String },
20
21 #[error("invalid passphrase")]
22 InvalidPassphrase,
23
24 #[error("invalid input: {message}")]
25 InvalidInput { message: String },
26
27 #[error("CAIP parse error: {message}")]
28 CaipParseError { message: String },
29}
30
31impl OwsError {
32 pub fn code(&self) -> OwsErrorCode {
33 match self {
34 OwsError::WalletNotFound { .. } => OwsErrorCode::WalletNotFound,
35 OwsError::ChainNotSupported { .. } => OwsErrorCode::ChainNotSupported,
36 OwsError::InvalidPassphrase => OwsErrorCode::InvalidPassphrase,
37 OwsError::InvalidInput { .. } => OwsErrorCode::InvalidInput,
38 OwsError::CaipParseError { .. } => OwsErrorCode::CaipParseError,
39 }
40 }
41}
42
43#[derive(Serialize)]
44struct ErrorPayload {
45 code: OwsErrorCode,
46 message: String,
47}
48
49impl Serialize for OwsError {
50 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
51 let payload = ErrorPayload {
52 code: self.code(),
53 message: self.to_string(),
54 };
55 payload.serialize(serializer)
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_code_mapping_wallet_not_found() {
65 let err = OwsError::WalletNotFound {
66 id: "abc".to_string(),
67 };
68 assert_eq!(err.code(), OwsErrorCode::WalletNotFound);
69 }
70
71 #[test]
72 fn test_code_mapping_all_variants() {
73 assert_eq!(
74 OwsError::ChainNotSupported { chain: "x".into() }.code(),
75 OwsErrorCode::ChainNotSupported
76 );
77 assert_eq!(
78 OwsError::InvalidPassphrase.code(),
79 OwsErrorCode::InvalidPassphrase
80 );
81 assert_eq!(
82 OwsError::InvalidInput {
83 message: "x".into()
84 }
85 .code(),
86 OwsErrorCode::InvalidInput
87 );
88 assert_eq!(
89 OwsError::CaipParseError {
90 message: "x".into()
91 }
92 .code(),
93 OwsErrorCode::CaipParseError
94 );
95 }
96
97 #[test]
98 fn test_display_output() {
99 let err = OwsError::WalletNotFound {
100 id: "abc-123".to_string(),
101 };
102 assert_eq!(err.to_string(), "wallet not found: abc-123");
103 }
104
105 #[test]
106 fn test_json_serialization_shape() {
107 let err = OwsError::WalletNotFound {
108 id: "abc-123".to_string(),
109 };
110 let json = serde_json::to_value(&err).unwrap();
111 assert_eq!(json["code"], "WALLET_NOT_FOUND");
112 assert_eq!(json["message"], "wallet not found: abc-123");
113 }
114
115 #[test]
116 fn test_caip_parse_error_serialization() {
117 let err = OwsError::CaipParseError {
118 message: "bad format".to_string(),
119 };
120 let json = serde_json::to_value(&err).unwrap();
121 assert_eq!(json["code"], "CAIP_PARSE_ERROR");
122 assert!(json["message"].as_str().unwrap().contains("bad format"));
123 }
124}