1use thiserror::Error;
2
3#[derive(Error, Debug)]
9pub enum SignerError {
10 #[error("LWK signing operation failed: {0}")]
18 Lwk(String),
19 #[error("LWK signing operation failed: {error_message}\n\nOperation: {operation}\n\nContext: {context}")]
20 LwkDetailed {
21 operation: String,
23 context: String,
25 error_message: String,
27 },
28
29 #[error("Invalid mnemonic phrase: {0}")]
37 InvalidMnemonic(String),
38
39 #[error("Hex parsing failed: {0}")]
47 HexParse(#[from] hex::FromHexError),
48 #[error("Hex parsing failed: {hex_error}\n\nParsing: {parsing_context}\nHex String (first 100 chars): {hex_preview}")]
49 HexParseDetailed {
50 parsing_context: String,
52 hex_preview: String,
54 hex_error: String,
56 },
57
58 #[error("Invalid transaction structure: {0}")]
67 InvalidTransaction(String),
68 #[error("Invalid transaction: {error_message}\n\nTransaction ID: {txid}\nValidation Details: {validation_details}")]
69 InvalidTransactionDetailed {
70 txid: String,
72 validation_details: String,
74 error_message: String,
76 },
77
78 #[error("Network communication failed: {0}")]
83 Network(#[from] reqwest::Error),
84
85 #[error("JSON serialization failed: {0}")]
92 Serialization(#[from] serde_json::Error),
93 #[error("Serialization error: {serde_error}\n\nOperation: {operation}\nData Type: {data_type}\n\nContext: {context}")]
94 SerializationDetailed {
95 operation: String,
97 data_type: String,
99 context: String,
101 serde_error: String,
103 },
104
105 #[error("File I/O operation failed: {0}")]
113 FileIo(#[from] std::io::Error),
114}
115
116impl From<bip39::Error> for SignerError {
127 fn from(err: bip39::Error) -> Self {
128 Self::InvalidMnemonic(format!("BIP39 validation failed: {err}"))
129 }
130}
131
132impl From<elements::encode::Error> for SignerError {
140 fn from(err: elements::encode::Error) -> Self {
141 Self::InvalidTransaction(format!("Elements transaction encoding failed: {err}"))
142 }
143}
144
145#[cfg(test)]
151mod tests {
152 use super::*;
153 use std::io::{Error as IoError, ErrorKind};
154
155 #[test]
156 fn test_lwk_error_variant() {
157 let error_msg = "SwSigner creation failed";
158 let error = SignerError::Lwk(error_msg.to_string());
159
160 let formatted = format!("{}", error);
162 assert_eq!(
163 formatted,
164 "LWK signing operation failed: SwSigner creation failed"
165 );
166
167 let debug_formatted = format!("{:?}", error);
169 assert!(debug_formatted.contains("Lwk"));
170 assert!(debug_formatted.contains(error_msg));
171
172 assert!(std::error::Error::source(&error).is_none());
174 }
175
176 #[test]
177 fn test_invalid_mnemonic_error_variant() {
178 let error_msg = "Invalid word count: expected 12, got 5";
179 let error = SignerError::InvalidMnemonic(error_msg.to_string());
180
181 let formatted = format!("{}", error);
183 assert_eq!(
184 formatted,
185 "Invalid mnemonic phrase: Invalid word count: expected 12, got 5"
186 );
187
188 let debug_formatted = format!("{:?}", error);
190 assert!(debug_formatted.contains("InvalidMnemonic"));
191 assert!(debug_formatted.contains(error_msg));
192
193 assert!(std::error::Error::source(&error).is_none());
195 }
196
197 #[test]
198 fn test_hex_parse_error_conversion() {
199 let hex_result = hex::decode("invalid_hex_zz");
201 let hex_error = hex_result.unwrap_err();
202 let signer_error = SignerError::from(hex_error);
203
204 match signer_error {
206 SignerError::HexParse(_) => {} other => panic!("Expected HexParse variant, got: {:?}", other),
208 }
209
210 let formatted = format!("{}", signer_error);
212 assert!(formatted.starts_with("Hex parsing failed:"));
213
214 let source = std::error::Error::source(&signer_error);
216 assert!(source.is_some());
217 }
218
219 #[test]
220 fn test_invalid_transaction_error_variant() {
221 let error_msg = "Transaction deserialization failed: invalid input count";
222 let error = SignerError::InvalidTransaction(error_msg.to_string());
223
224 let formatted = format!("{}", error);
226 assert_eq!(formatted, "Invalid transaction structure: Transaction deserialization failed: invalid input count");
227
228 let debug_formatted = format!("{:?}", error);
230 assert!(debug_formatted.contains("InvalidTransaction"));
231 assert!(debug_formatted.contains(error_msg));
232
233 assert!(std::error::Error::source(&error).is_none());
235 }
236
237 #[test]
238 fn test_network_error_conversion() {
239 let client = reqwest::Client::new();
241 let request_result = client.get("http://").build();
242 let reqwest_error = request_result.unwrap_err();
243 let signer_error = SignerError::from(reqwest_error);
244
245 match signer_error {
247 SignerError::Network(_) => {} other => panic!("Expected Network variant, got: {:?}", other),
249 }
250
251 let formatted = format!("{}", signer_error);
253 assert!(formatted.starts_with("Network communication failed:"));
254
255 let source = std::error::Error::source(&signer_error);
257 assert!(source.is_some());
258 }
259
260 #[test]
261 fn test_serialization_error_conversion() {
262 let invalid_json = r#"{"invalid": json syntax"#;
264 let json_error = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
265 let signer_error = SignerError::from(json_error);
266
267 match signer_error {
269 SignerError::Serialization(_) => {} other => panic!("Expected Serialization variant, got: {:?}", other),
271 }
272
273 let formatted = format!("{}", signer_error);
275 assert!(formatted.starts_with("JSON serialization failed:"));
276
277 let source = std::error::Error::source(&signer_error);
279 assert!(source.is_some());
280 }
281
282 #[test]
283 fn test_file_io_error_conversion() {
284 let io_error = IoError::new(ErrorKind::NotFound, "File not found");
286 let signer_error = SignerError::from(io_error);
287
288 match signer_error {
290 SignerError::FileIo(_) => {} other => panic!("Expected FileIo variant, got: {:?}", other),
292 }
293
294 let formatted = format!("{}", signer_error);
296 assert!(formatted.starts_with("File I/O operation failed:"));
297 assert!(formatted.contains("File not found"));
298
299 let source = std::error::Error::source(&signer_error);
301 assert!(source.is_some());
302 assert_eq!(source.unwrap().to_string(), "File not found");
303 }
304
305 #[test]
306 fn test_bip39_error_conversion() {
307 let bip39_error = bip39::Error::BadWordCount(5);
309 let signer_error = SignerError::from(bip39_error);
310
311 match &signer_error {
313 SignerError::InvalidMnemonic(msg) => {
314 assert!(msg.contains("BIP39 validation failed"));
315 assert!(msg.contains("word count"));
316 }
317 other => panic!("Expected InvalidMnemonic variant, got: {:?}", other),
318 }
319
320 let formatted = format!("{}", signer_error);
322 assert!(formatted.starts_with("Invalid mnemonic phrase:"));
323 assert!(formatted.contains("BIP39 validation failed"));
324 }
325
326 #[test]
327 fn test_elements_encode_error_conversion() {
328 let elements_error = elements::encode::Error::ParseFailed("Invalid transaction format");
330 let signer_error = SignerError::from(elements_error);
331
332 match &signer_error {
334 SignerError::InvalidTransaction(msg) => {
335 assert!(msg.contains("Elements transaction encoding failed"));
336 assert!(msg.contains("Invalid transaction format"));
337 }
338 other => panic!("Expected InvalidTransaction variant, got: {:?}", other),
339 }
340
341 let formatted = format!("{}", signer_error);
343 assert!(formatted.starts_with("Invalid transaction structure:"));
344 assert!(formatted.contains("Elements transaction encoding failed"));
345 }
346
347 #[test]
348 fn test_error_chain_preservation() {
349 let inner_error = IoError::new(ErrorKind::PermissionDenied, "Access denied");
353 let signer_error = SignerError::from(inner_error);
354
355 let source = std::error::Error::source(&signer_error);
357 assert!(source.is_some());
358 assert_eq!(source.unwrap().to_string(), "Access denied");
359
360 let formatted = format!("{}", signer_error);
362 assert!(formatted.contains("File I/O operation failed"));
363 assert!(formatted.contains("Access denied"));
364 }
365
366 #[test]
367 fn test_error_send_sync_traits() {
368 fn assert_send<T: Send>() {}
370 fn assert_sync<T: Sync>() {}
371
372 assert_send::<SignerError>();
373 assert_sync::<SignerError>();
374 }
375
376 #[test]
377 fn test_error_static_lifetime() {
378 fn assert_static<T: 'static>() {}
380 assert_static::<SignerError>();
381 }
382
383 #[test]
384 fn test_all_error_variants_display() {
385 let errors = vec![
387 SignerError::Lwk("test lwk error".to_string()),
388 SignerError::InvalidMnemonic("test mnemonic error".to_string()),
389 SignerError::HexParse(hex::decode("invalid_hex_zz").unwrap_err()),
390 SignerError::InvalidTransaction("test transaction error".to_string()),
391 SignerError::Network(reqwest::Client::new().get("http://").build().unwrap_err()),
392 SignerError::Serialization(
393 serde_json::from_str::<serde_json::Value>(r#"{"invalid": json"#).unwrap_err(),
394 ),
395 SignerError::FileIo(IoError::new(ErrorKind::NotFound, "test file error")),
396 ];
397
398 for error in errors {
399 let display_str = format!("{}", error);
400 assert!(
401 !display_str.is_empty(),
402 "Error display should not be empty: {:?}",
403 error
404 );
405
406 let debug_str = format!("{:?}", error);
407 assert!(
408 !debug_str.is_empty(),
409 "Error debug should not be empty: {:?}",
410 error
411 );
412 }
413 }
414
415 #[test]
416 fn test_error_message_preservation() {
417 let test_cases = vec![
419 (
420 "LWK operation failed with code 123",
421 SignerError::Lwk("LWK operation failed with code 123".to_string()),
422 ),
423 (
424 "Mnemonic has invalid checksum",
425 SignerError::InvalidMnemonic("Mnemonic has invalid checksum".to_string()),
426 ),
427 (
428 "Transaction missing required inputs",
429 SignerError::InvalidTransaction("Transaction missing required inputs".to_string()),
430 ),
431 ];
432
433 for (expected_msg, error) in test_cases {
434 let formatted = format!("{}", error);
435 assert!(
436 formatted.contains(expected_msg),
437 "Error message should contain '{}', got: '{}'",
438 expected_msg,
439 formatted
440 );
441 }
442 }
443
444 #[test]
445 fn test_error_propagation_through_result() {
446 fn level_3() -> Result<(), SignerError> {
448 Err(SignerError::Lwk("Deep error".to_string()))
449 }
450
451 fn level_2() -> Result<(), SignerError> {
452 level_3()?;
453 Ok(())
454 }
455
456 fn level_1() -> Result<(), SignerError> {
457 level_2()?;
458 Ok(())
459 }
460
461 let result = level_1();
462 assert!(result.is_err());
463
464 match result.unwrap_err() {
465 SignerError::Lwk(msg) => assert_eq!(msg, "Deep error"),
466 other => panic!("Expected Lwk error, got: {:?}", other),
467 }
468 }
469
470 #[test]
471 fn test_error_conversion_preserves_context() {
472 let hex_result: Result<Vec<u8>, hex::FromHexError> = hex::decode("invalid_hex_zz");
476 let hex_error = hex_result.unwrap_err();
477 let signer_error = SignerError::from(hex_error);
478
479 let formatted = format!("{}", signer_error);
480 assert!(formatted.contains("Hex parsing failed"));
481
482 let io_error = IoError::new(
484 ErrorKind::PermissionDenied,
485 "Cannot write to read-only file",
486 );
487 let signer_error = SignerError::from(io_error);
488
489 let formatted = format!("{}", signer_error);
490 assert!(formatted.contains("File I/O operation failed"));
491 assert!(formatted.contains("Cannot write to read-only file"));
492 }
493
494 #[test]
495 fn test_error_debug_format_completeness() {
496 let error = SignerError::InvalidMnemonic("Test mnemonic error with details".to_string());
498 let debug_str = format!("{:#?}", error);
499
500 assert!(debug_str.contains("InvalidMnemonic"));
502 assert!(debug_str.contains("Test mnemonic error with details"));
503 }
504
505 #[test]
506 fn test_multiple_error_conversions() {
507 let test_cases = vec![
509 "invalid_hex_zz", "abc", ];
512
513 for invalid_hex in test_cases {
514 let hex_error = hex::decode(invalid_hex).unwrap_err();
515 let signer_error = SignerError::from(hex_error);
516 match signer_error {
517 SignerError::HexParse(_) => {} other => panic!("Expected HexParse variant, got: {:?}", other),
519 }
520
521 let formatted = format!("{}", signer_error);
523 assert!(formatted.starts_with("Hex parsing failed:"));
524 }
525 }
526
527 #[test]
528 fn test_error_equality_and_comparison() {
529 let error1 = SignerError::Lwk("same message".to_string());
531 let error2 = SignerError::Lwk("same message".to_string());
532 let error3 = SignerError::Lwk("different message".to_string());
533
534 let debug1 = format!("{:?}", error1);
537 let debug2 = format!("{:?}", error2);
538 let debug3 = format!("{:?}", error3);
539
540 assert_eq!(debug1, debug2);
541 assert_ne!(debug1, debug3);
542 }
543}