1use thiserror::Error;
2
3#[derive(Error, Debug)]
9pub enum SignerError {
10 #[error("LWK signing operation failed: {0}")]
18 Lwk(String),
19
20 #[error("Invalid mnemonic phrase: {0}")]
28 InvalidMnemonic(String),
29
30 #[error("Hex parsing failed: {0}")]
38 HexParse(#[from] hex::FromHexError),
39
40 #[error("Invalid transaction structure: {0}")]
49 InvalidTransaction(String),
50
51 #[error("Network communication failed: {0}")]
56 Network(#[from] reqwest::Error),
57
58 #[error("JSON serialization failed: {0}")]
65 Serialization(#[from] serde_json::Error),
66
67 #[error("File I/O operation failed: {0}")]
75 FileIo(#[from] std::io::Error),
76}
77
78impl From<bip39::Error> for SignerError {
89 fn from(err: bip39::Error) -> Self {
90 Self::InvalidMnemonic(format!("BIP39 validation failed: {err}"))
91 }
92}
93
94impl From<elements::encode::Error> for SignerError {
102 fn from(err: elements::encode::Error) -> Self {
103 Self::InvalidTransaction(format!("Elements transaction encoding failed: {err}"))
104 }
105}
106
107#[cfg(test)]
113mod tests {
114 use super::*;
115 use std::io::{Error as IoError, ErrorKind};
116
117 #[test]
118 fn test_lwk_error_variant() {
119 let error_msg = "SwSigner creation failed";
120 let error = SignerError::Lwk(error_msg.to_string());
121
122 let formatted = format!("{}", error);
124 assert_eq!(
125 formatted,
126 "LWK signing operation failed: SwSigner creation failed"
127 );
128
129 let debug_formatted = format!("{:?}", error);
131 assert!(debug_formatted.contains("Lwk"));
132 assert!(debug_formatted.contains(error_msg));
133
134 assert!(std::error::Error::source(&error).is_none());
136 }
137
138 #[test]
139 fn test_invalid_mnemonic_error_variant() {
140 let error_msg = "Invalid word count: expected 12, got 5";
141 let error = SignerError::InvalidMnemonic(error_msg.to_string());
142
143 let formatted = format!("{}", error);
145 assert_eq!(
146 formatted,
147 "Invalid mnemonic phrase: Invalid word count: expected 12, got 5"
148 );
149
150 let debug_formatted = format!("{:?}", error);
152 assert!(debug_formatted.contains("InvalidMnemonic"));
153 assert!(debug_formatted.contains(error_msg));
154
155 assert!(std::error::Error::source(&error).is_none());
157 }
158
159 #[test]
160 fn test_hex_parse_error_conversion() {
161 let hex_result = hex::decode("invalid_hex_zz");
163 let hex_error = hex_result.unwrap_err();
164 let signer_error = SignerError::from(hex_error);
165
166 match signer_error {
168 SignerError::HexParse(_) => {} other => panic!("Expected HexParse variant, got: {:?}", other),
170 }
171
172 let formatted = format!("{}", signer_error);
174 assert!(formatted.starts_with("Hex parsing failed:"));
175
176 let source = std::error::Error::source(&signer_error);
178 assert!(source.is_some());
179 }
180
181 #[test]
182 fn test_invalid_transaction_error_variant() {
183 let error_msg = "Transaction deserialization failed: invalid input count";
184 let error = SignerError::InvalidTransaction(error_msg.to_string());
185
186 let formatted = format!("{}", error);
188 assert_eq!(formatted, "Invalid transaction structure: Transaction deserialization failed: invalid input count");
189
190 let debug_formatted = format!("{:?}", error);
192 assert!(debug_formatted.contains("InvalidTransaction"));
193 assert!(debug_formatted.contains(error_msg));
194
195 assert!(std::error::Error::source(&error).is_none());
197 }
198
199 #[test]
200 fn test_network_error_conversion() {
201 let client = reqwest::Client::new();
203 let request_result = client.get("http://").build();
204 let reqwest_error = request_result.unwrap_err();
205 let signer_error = SignerError::from(reqwest_error);
206
207 match signer_error {
209 SignerError::Network(_) => {} other => panic!("Expected Network variant, got: {:?}", other),
211 }
212
213 let formatted = format!("{}", signer_error);
215 assert!(formatted.starts_with("Network communication failed:"));
216
217 let source = std::error::Error::source(&signer_error);
219 assert!(source.is_some());
220 }
221
222 #[test]
223 fn test_serialization_error_conversion() {
224 let invalid_json = r#"{"invalid": json syntax"#;
226 let json_error = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
227 let signer_error = SignerError::from(json_error);
228
229 match signer_error {
231 SignerError::Serialization(_) => {} other => panic!("Expected Serialization variant, got: {:?}", other),
233 }
234
235 let formatted = format!("{}", signer_error);
237 assert!(formatted.starts_with("JSON serialization failed:"));
238
239 let source = std::error::Error::source(&signer_error);
241 assert!(source.is_some());
242 }
243
244 #[test]
245 fn test_file_io_error_conversion() {
246 let io_error = IoError::new(ErrorKind::NotFound, "File not found");
248 let signer_error = SignerError::from(io_error);
249
250 match signer_error {
252 SignerError::FileIo(_) => {} other => panic!("Expected FileIo variant, got: {:?}", other),
254 }
255
256 let formatted = format!("{}", signer_error);
258 assert!(formatted.starts_with("File I/O operation failed:"));
259 assert!(formatted.contains("File not found"));
260
261 let source = std::error::Error::source(&signer_error);
263 assert!(source.is_some());
264 assert_eq!(source.unwrap().to_string(), "File not found");
265 }
266
267 #[test]
268 fn test_bip39_error_conversion() {
269 let bip39_error = bip39::Error::BadWordCount(5);
271 let signer_error = SignerError::from(bip39_error);
272
273 match &signer_error {
275 SignerError::InvalidMnemonic(msg) => {
276 assert!(msg.contains("BIP39 validation failed"));
277 assert!(msg.contains("word count"));
278 }
279 other => panic!("Expected InvalidMnemonic variant, got: {:?}", other),
280 }
281
282 let formatted = format!("{}", signer_error);
284 assert!(formatted.starts_with("Invalid mnemonic phrase:"));
285 assert!(formatted.contains("BIP39 validation failed"));
286 }
287
288 #[test]
289 fn test_elements_encode_error_conversion() {
290 let elements_error = elements::encode::Error::ParseFailed("Invalid transaction format");
292 let signer_error = SignerError::from(elements_error);
293
294 match &signer_error {
296 SignerError::InvalidTransaction(msg) => {
297 assert!(msg.contains("Elements transaction encoding failed"));
298 assert!(msg.contains("Invalid transaction format"));
299 }
300 other => panic!("Expected InvalidTransaction variant, got: {:?}", other),
301 }
302
303 let formatted = format!("{}", signer_error);
305 assert!(formatted.starts_with("Invalid transaction structure:"));
306 assert!(formatted.contains("Elements transaction encoding failed"));
307 }
308
309 #[test]
310 fn test_error_chain_preservation() {
311 let inner_error = IoError::new(ErrorKind::PermissionDenied, "Access denied");
315 let signer_error = SignerError::from(inner_error);
316
317 let source = std::error::Error::source(&signer_error);
319 assert!(source.is_some());
320 assert_eq!(source.unwrap().to_string(), "Access denied");
321
322 let formatted = format!("{}", signer_error);
324 assert!(formatted.contains("File I/O operation failed"));
325 assert!(formatted.contains("Access denied"));
326 }
327
328 #[test]
329 fn test_error_send_sync_traits() {
330 fn assert_send<T: Send>() {}
332 fn assert_sync<T: Sync>() {}
333
334 assert_send::<SignerError>();
335 assert_sync::<SignerError>();
336 }
337
338 #[test]
339 fn test_error_static_lifetime() {
340 fn assert_static<T: 'static>() {}
342 assert_static::<SignerError>();
343 }
344
345 #[test]
346 fn test_all_error_variants_display() {
347 let errors = vec![
349 SignerError::Lwk("test lwk error".to_string()),
350 SignerError::InvalidMnemonic("test mnemonic error".to_string()),
351 SignerError::HexParse(hex::decode("invalid_hex_zz").unwrap_err()),
352 SignerError::InvalidTransaction("test transaction error".to_string()),
353 SignerError::Network(reqwest::Client::new().get("http://").build().unwrap_err()),
354 SignerError::Serialization(
355 serde_json::from_str::<serde_json::Value>(r#"{"invalid": json"#).unwrap_err(),
356 ),
357 SignerError::FileIo(IoError::new(ErrorKind::NotFound, "test file error")),
358 ];
359
360 for error in errors {
361 let display_str = format!("{}", error);
362 assert!(
363 !display_str.is_empty(),
364 "Error display should not be empty: {:?}",
365 error
366 );
367
368 let debug_str = format!("{:?}", error);
369 assert!(
370 !debug_str.is_empty(),
371 "Error debug should not be empty: {:?}",
372 error
373 );
374 }
375 }
376
377 #[test]
378 fn test_error_message_preservation() {
379 let test_cases = vec![
381 (
382 "LWK operation failed with code 123",
383 SignerError::Lwk("LWK operation failed with code 123".to_string()),
384 ),
385 (
386 "Mnemonic has invalid checksum",
387 SignerError::InvalidMnemonic("Mnemonic has invalid checksum".to_string()),
388 ),
389 (
390 "Transaction missing required inputs",
391 SignerError::InvalidTransaction("Transaction missing required inputs".to_string()),
392 ),
393 ];
394
395 for (expected_msg, error) in test_cases {
396 let formatted = format!("{}", error);
397 assert!(
398 formatted.contains(expected_msg),
399 "Error message should contain '{}', got: '{}'",
400 expected_msg,
401 formatted
402 );
403 }
404 }
405
406 #[test]
407 fn test_error_propagation_through_result() {
408 fn level_3() -> Result<(), SignerError> {
410 Err(SignerError::Lwk("Deep error".to_string()))
411 }
412
413 fn level_2() -> Result<(), SignerError> {
414 level_3()?;
415 Ok(())
416 }
417
418 fn level_1() -> Result<(), SignerError> {
419 level_2()?;
420 Ok(())
421 }
422
423 let result = level_1();
424 assert!(result.is_err());
425
426 match result.unwrap_err() {
427 SignerError::Lwk(msg) => assert_eq!(msg, "Deep error"),
428 other => panic!("Expected Lwk error, got: {:?}", other),
429 }
430 }
431
432 #[test]
433 fn test_error_conversion_preserves_context() {
434 let hex_result: Result<Vec<u8>, hex::FromHexError> = hex::decode("invalid_hex_zz");
438 let hex_error = hex_result.unwrap_err();
439 let signer_error = SignerError::from(hex_error);
440
441 let formatted = format!("{}", signer_error);
442 assert!(formatted.contains("Hex parsing failed"));
443
444 let io_error = IoError::new(
446 ErrorKind::PermissionDenied,
447 "Cannot write to read-only file",
448 );
449 let signer_error = SignerError::from(io_error);
450
451 let formatted = format!("{}", signer_error);
452 assert!(formatted.contains("File I/O operation failed"));
453 assert!(formatted.contains("Cannot write to read-only file"));
454 }
455
456 #[test]
457 fn test_error_debug_format_completeness() {
458 let error = SignerError::InvalidMnemonic("Test mnemonic error with details".to_string());
460 let debug_str = format!("{:#?}", error);
461
462 assert!(debug_str.contains("InvalidMnemonic"));
464 assert!(debug_str.contains("Test mnemonic error with details"));
465 }
466
467 #[test]
468 fn test_multiple_error_conversions() {
469 let test_cases = vec![
471 "invalid_hex_zz", "abc", ];
474
475 for invalid_hex in test_cases {
476 let hex_error = hex::decode(invalid_hex).unwrap_err();
477 let signer_error = SignerError::from(hex_error);
478 match signer_error {
479 SignerError::HexParse(_) => {} other => panic!("Expected HexParse variant, got: {:?}", other),
481 }
482
483 let formatted = format!("{}", signer_error);
485 assert!(formatted.starts_with("Hex parsing failed:"));
486 }
487 }
488
489 #[test]
490 fn test_error_equality_and_comparison() {
491 let error1 = SignerError::Lwk("same message".to_string());
493 let error2 = SignerError::Lwk("same message".to_string());
494 let error3 = SignerError::Lwk("different message".to_string());
495
496 let debug1 = format!("{:?}", error1);
499 let debug2 = format!("{:?}", error2);
500 let debug3 = format!("{:?}", error3);
501
502 assert_eq!(debug1, debug2);
503 assert_ne!(debug1, debug3);
504 }
505}