Skip to main content

age_crypto/errors/
encrypt.rs

1use std::io;
2use thiserror::Error;
3#[derive(Debug, Error)]
4pub enum EncryptError {
5    #[error("No recipients provided")]
6    NoRecipients,
7    #[error("Invalid recipient '{recipient}': {reason}")]
8    InvalidRecipient { recipient: String, reason: String },
9    #[error("Encryption failed: {0}")]
10    Failed(String),
11    #[error("I/O error: {0}")]
12    Io(#[from] io::Error),
13}
14impl EncryptError {
15    #[must_use]
16    pub fn is_user_correctable(&self) -> bool {
17        matches!(
18            self,
19            EncryptError::NoRecipients | EncryptError::InvalidRecipient { .. }
20        )
21    }
22    #[must_use]
23    pub fn invalid_recipient(&self) -> Option<&str> {
24        match self {
25            EncryptError::InvalidRecipient { recipient, .. } => Some(recipient),
26            _ => None,
27        }
28    }
29}
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use std::io::{self, ErrorKind};
34    #[test]
35    fn test_no_recipients_display() {
36        let err = EncryptError::NoRecipients;
37        assert_eq!(format!("{}", err), "No recipients provided");
38    }
39    #[test]
40    fn test_invalid_recipient_display() {
41        let err = EncryptError::InvalidRecipient {
42            recipient: "age1badkey".into(),
43            reason: "invalid checksum".into(),
44        };
45        assert_eq!(
46            format!("{}", err),
47            "Invalid recipient 'age1badkey': invalid checksum"
48        );
49    }
50    #[test]
51    fn test_failed_display() {
52        let err = EncryptError::Failed("internal crypto error".into());
53        assert_eq!(
54            format!("{}", err),
55            "Encryption failed: internal crypto error"
56        );
57    }
58    #[test]
59    fn test_io_error_display() {
60        let io_err = io::Error::new(ErrorKind::OutOfMemory, "alloc failed");
61        let err = EncryptError::Io(io_err);
62        assert_eq!(format!("{}", err), "I/O error: alloc failed");
63    }
64    #[test]
65    fn test_from_io_error_conversion() {
66        let io_err: io::Error = ErrorKind::PermissionDenied.into();
67        let encrypt_err: EncryptError = io_err.into();
68        assert!(matches!(encrypt_err, EncryptError::Io(_)));
69    }
70    #[test]
71    fn test_io_error_preserves_kind() {
72        let io_err = io::Error::new(ErrorKind::UnexpectedEof, "test");
73        let encrypt_err = EncryptError::Io(io_err);
74        if let EncryptError::Io(e) = encrypt_err {
75            assert_eq!(e.kind(), ErrorKind::UnexpectedEof);
76        } else {
77            panic!("Expected Io variant");
78        }
79    }
80    #[test]
81    fn test_is_user_correctable_true() {
82        assert!(EncryptError::NoRecipients.is_user_correctable());
83        let invalid = EncryptError::InvalidRecipient {
84            recipient: "bad".into(),
85            reason: "x".into(),
86        };
87        assert!(invalid.is_user_correctable());
88    }
89    #[test]
90    fn test_is_user_correctable_false() {
91        assert!(!EncryptError::Failed("x".into()).is_user_correctable());
92        assert!(!EncryptError::Io(io::Error::last_os_error()).is_user_correctable());
93    }
94    #[test]
95    fn test_invalid_recipient_some() {
96        let err = EncryptError::InvalidRecipient {
97            recipient: "age1test".into(),
98            reason: "bad".into(),
99        };
100        assert_eq!(err.invalid_recipient(), Some("age1test"));
101    }
102    #[test]
103    fn test_invalid_recipient_none() {
104        assert_eq!(EncryptError::NoRecipients.invalid_recipient(), None);
105        assert_eq!(EncryptError::Failed("x".into()).invalid_recipient(), None);
106    }
107    #[test]
108    fn test_error_is_send_sync() {
109        fn assert_send_sync<T: Send + Sync>() {}
110        assert_send_sync::<EncryptError>();
111    }
112    #[test]
113    fn test_error_implements_std_error() {
114        fn assert_error<T: std::error::Error>() {}
115        assert_error::<EncryptError>();
116    }
117    #[test]
118    fn test_error_source_chain() {
119        use std::error::Error as StdError;
120        let io_err = io::Error::other("underlying cause");
121        let encrypt_err = EncryptError::Io(io_err);
122        assert!(encrypt_err.source().is_some());
123    }
124    #[test]
125    fn test_debug_format_contains_variant_name() {
126        let err = EncryptError::InvalidRecipient {
127            recipient: "key".into(),
128            reason: "bad".into(),
129        };
130        let debug = format!("{:?}", err);
131        assert!(debug.contains("InvalidRecipient"));
132        assert!(debug.contains("key"));
133    }
134}