Skip to main content

ferro_wallet/
error.rs

1//! `WalletError` — the single error type for the ferro-wallet crate.
2//!
3//! Each variant's `Display` impl prefixes its name (`"config: …"`, `"apple sign: …"`)
4//! so production log greps stay surgical. `Io(#[from] std::io::Error)` covers zip + io
5//! plumbing in the apple package path.
6
7#[derive(Debug, thiserror::Error)]
8pub enum WalletError {
9    /// Configuration error (missing env var or invalid value).
10    #[error("config: {0}")]
11    Config(String),
12
13    /// Apple PKCS#7 signing failed (cert/key parse, sign call, DER serialization).
14    #[error("apple sign: {0}")]
15    AppleSign(String),
16
17    /// Apple `.pkpass` packaging failed (manifest JSON build, zip assembly).
18    #[error("apple package: {0}")]
19    ApplePackage(String),
20
21    /// Google save JWT signing failed (private key parse, RS256 encode).
22    #[error("google jwt: {0}")]
23    GoogleJwt(String),
24
25    /// Image pipeline error (decode, resize, encode).
26    #[error("image: {0}")]
27    Image(String),
28
29    /// QR code generation error.
30    #[error("qr: {0}")]
31    Qr(String),
32
33    /// Caller supplied invalid input (out-of-range value, malformed string).
34    #[error("invalid input: {0}")]
35    InvalidInput(String),
36
37    /// I/O error — zip writer, file handles, etc.
38    #[error("io: {0}")]
39    Io(#[from] std::io::Error),
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn error_config_displays_message() {
48        let e = WalletError::Config("APP_NAME not set".into());
49        assert_eq!(e.to_string(), "config: APP_NAME not set");
50    }
51
52    #[test]
53    fn error_apple_sign_displays_message() {
54        let e = WalletError::AppleSign("cert parse failed".into());
55        assert_eq!(e.to_string(), "apple sign: cert parse failed");
56    }
57
58    #[test]
59    fn error_apple_package_displays_message() {
60        let e = WalletError::ApplePackage("zip write failed".into());
61        assert_eq!(e.to_string(), "apple package: zip write failed");
62    }
63
64    #[test]
65    fn error_google_jwt_displays_message() {
66        let e = WalletError::GoogleJwt("private key parse failed".into());
67        assert_eq!(e.to_string(), "google jwt: private key parse failed");
68    }
69
70    #[test]
71    fn error_image_displays_message() {
72        let e = WalletError::Image("decode failed".into());
73        assert_eq!(e.to_string(), "image: decode failed");
74    }
75
76    #[test]
77    fn error_qr_displays_message() {
78        let e = WalletError::Qr("encode failed".into());
79        assert_eq!(e.to_string(), "qr: encode failed");
80    }
81
82    #[test]
83    fn error_invalid_input_displays_message() {
84        let e = WalletError::InvalidInput("hex must be 6 chars".into());
85        assert_eq!(e.to_string(), "invalid input: hex must be 6 chars");
86    }
87
88    #[test]
89    fn error_io_displays_message() {
90        let e = WalletError::Io(std::io::Error::other("disk full"));
91        assert_eq!(e.to_string(), "io: disk full");
92    }
93
94    #[test]
95    fn io_from_std_io_error() {
96        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
97        let wallet_err: WalletError = WalletError::from(io_err);
98        assert!(matches!(wallet_err, WalletError::Io(_)));
99    }
100}