neo3/neo_error/
mod.rs

1use thiserror::Error;
2
3/// Comprehensive error types for the Neo3 SDK
4#[derive(Error, Debug)]
5pub enum Neo3Error {
6	/// Cryptographic operation errors
7	#[error("Cryptographic error: {0}")]
8	Crypto(#[from] CryptoError),
9
10	/// Wallet operation errors
11	#[error("Wallet error: {0}")]
12	Wallet(#[from] WalletError),
13
14	/// Network/RPC communication errors
15	#[error("Network error: {0}")]
16	Network(#[from] NetworkError),
17
18	/// Transaction building/validation errors
19	#[error("Transaction error: {0}")]
20	Transaction(#[from] TransactionError),
21
22	/// Smart contract interaction errors
23	#[error("Contract error: {0}")]
24	Contract(#[from] ContractError),
25
26	/// Serialization/deserialization errors
27	#[error("Serialization error: {0}")]
28	Serialization(#[from] SerializationError),
29
30	/// Configuration errors
31	#[error("Configuration error: {0}")]
32	Config(String),
33
34	/// Generic errors with context
35	#[error("Error: {message}")]
36	Generic { message: String },
37
38	/// Unsupported operation error
39	#[error("Unsupported operation: {0}")]
40	UnsupportedOperation(String),
41}
42
43#[derive(Error, Debug)]
44pub enum CryptoError {
45	#[error("Invalid private key: {0}")]
46	InvalidPrivateKey(String),
47
48	#[error("Invalid public key: {0}")]
49	InvalidPublicKey(String),
50
51	#[error("Signature verification failed")]
52	SignatureVerificationFailed,
53
54	#[error("Key generation failed: {0}")]
55	KeyGenerationFailed(String),
56
57	#[error("Hash operation failed: {0}")]
58	HashFailed(String),
59
60	#[error("Encryption failed: {0}")]
61	EncryptionFailed(String),
62
63	#[error("Decryption failed: {0}")]
64	DecryptionFailed(String),
65}
66
67#[derive(Error, Debug)]
68pub enum WalletError {
69	#[error("Wallet not found: {0}")]
70	NotFound(String),
71
72	#[error("Invalid password")]
73	InvalidPassword,
74
75	#[error("Account not found: {0}")]
76	AccountNotFound(String),
77
78	#[error("Wallet is locked")]
79	WalletLocked,
80
81	#[error("Backup operation failed: {0}")]
82	BackupFailed(String),
83
84	#[error("Recovery operation failed: {0}")]
85	RecoveryFailed(String),
86
87	#[error("Invalid wallet format: {0}")]
88	InvalidFormat(String),
89
90	#[error("IO error: {0}")]
91	Io(#[from] std::io::Error),
92}
93
94#[derive(Error, Debug)]
95pub enum NetworkError {
96	#[error("Connection failed: {0}")]
97	ConnectionFailed(String),
98
99	#[error("Request timeout")]
100	Timeout,
101
102	#[error("Invalid response: {0}")]
103	InvalidResponse(String),
104
105	#[error("RPC error: {code} - {message}")]
106	RpcError { code: i32, message: String },
107
108	#[error("Network unreachable: {0}")]
109	NetworkUnreachable(String),
110
111	#[error("Rate limit exceeded")]
112	RateLimitExceeded,
113
114	#[error("HTTP error: {0}")]
115	Http(#[from] reqwest::Error),
116}
117
118#[derive(Error, Debug)]
119pub enum TransactionError {
120	#[error("Invalid transaction: {0}")]
121	Invalid(String),
122
123	#[error("Insufficient funds: required {required}, available {available}")]
124	InsufficientFunds { required: u64, available: u64 },
125
126	#[error("Transaction too large: {size} bytes (max: {max})")]
127	TooLarge { size: usize, max: usize },
128
129	#[error("Invalid signature")]
130	InvalidSignature,
131
132	#[error("Transaction expired")]
133	Expired,
134
135	#[error("Nonce too low: {provided} (expected: {expected})")]
136	NonceTooLow { provided: u64, expected: u64 },
137
138	#[error("Gas limit exceeded: {used} (limit: {limit})")]
139	GasLimitExceeded { used: u64, limit: u64 },
140}
141
142#[derive(Error, Debug)]
143pub enum ContractError {
144	#[error("Contract not found: {0}")]
145	NotFound(String),
146
147	#[error("Method not found: {0}")]
148	MethodNotFound(String),
149
150	#[error("Invalid parameters: {0}")]
151	InvalidParameters(String),
152
153	#[error("Execution failed: {0}")]
154	ExecutionFailed(String),
155
156	#[error("Insufficient gas: {0}")]
157	InsufficientGas(String),
158
159	#[error("Contract deployment failed: {0}")]
160	DeploymentFailed(String),
161}
162
163#[derive(Error, Debug)]
164pub enum SerializationError {
165	#[error("JSON error: {0}")]
166	Json(#[from] serde_json::Error),
167
168	#[error("Invalid format: {0}")]
169	InvalidFormat(String),
170
171	#[error("Encoding error: {0}")]
172	Encoding(String),
173
174	#[error("Decoding error: {0}")]
175	Decoding(String),
176}
177
178/// Result type alias for Neo3 operations
179pub type Neo3Result<T> = Result<T, Neo3Error>;
180
181/// Legacy alias for backward compatibility
182pub type NeoError = Neo3Error;
183
184// Additional From implementations for common error types
185impl From<std::io::Error> for Neo3Error {
186	fn from(err: std::io::Error) -> Self {
187		Neo3Error::Wallet(WalletError::Io(err))
188	}
189}
190
191impl From<serde_json::Error> for Neo3Error {
192	fn from(err: serde_json::Error) -> Self {
193		Neo3Error::Serialization(SerializationError::Json(err))
194	}
195}
196
197/// Trait for adding context to errors
198pub trait ErrorContext<T> {
199	fn with_context<F>(self, f: F) -> Neo3Result<T>
200	where
201		F: FnOnce() -> String;
202}
203
204impl<T, E> ErrorContext<T> for Result<T, E>
205where
206	E: Into<Neo3Error>,
207{
208	fn with_context<F>(self, f: F) -> Neo3Result<T>
209	where
210		F: FnOnce() -> String,
211	{
212		self.map_err(|e| {
213			let base_error = e.into();
214			Neo3Error::Generic { message: format!("{}: {}", f(), base_error) }
215		})
216	}
217}
218
219/// Macro for creating context-aware errors
220#[macro_export]
221macro_rules! neo3_error {
222    ($msg:expr) => {
223        Neo3Error::Generic {
224            message: $msg.to_string(),
225        }
226    };
227    ($fmt:expr, $($arg:tt)*) => {
228        Neo3Error::Generic {
229            message: format!($fmt, $($arg)*),
230        }
231    };
232}
233
234/// Macro for early return with context
235#[macro_export]
236macro_rules! ensure {
237    ($cond:expr, $msg:expr) => {
238        if !$cond {
239            return Err(neo3_error!($msg));
240        }
241    };
242    ($cond:expr, $fmt:expr, $($arg:tt)*) => {
243        if !$cond {
244            return Err(neo3_error!($fmt, $($arg)*));
245        }
246    };
247}
248
249#[cfg(test)]
250mod tests {
251	use super::*;
252
253	#[test]
254	fn test_error_context() {
255		let result: Result<(), std::io::Error> =
256			Err(std::io::Error::new(std::io::ErrorKind::NotFound, "file not found"));
257
258		let with_context = result.with_context(|| "Failed to read configuration file".to_string());
259
260		assert!(with_context.is_err());
261		let error_msg = with_context.unwrap_err().to_string();
262		assert!(error_msg.contains("Failed to read configuration file"));
263		assert!(error_msg.contains("file not found"));
264	}
265
266	#[test]
267	fn test_error_macros() {
268		let error = neo3_error!("Something went wrong");
269		assert_eq!(error.to_string(), "Error: Something went wrong");
270
271		let error = neo3_error!("Value {} is invalid", 42);
272		assert_eq!(error.to_string(), "Error: Value 42 is invalid");
273	}
274}