Skip to main content

murk_cli/
error.rs

1//! Unified error type for the murk library.
2
3use crate::crypto::CryptoError;
4use crate::github::GitHubError;
5use crate::vault::VaultError;
6
7/// Top-level error type for murk operations.
8#[derive(Debug)]
9pub enum MurkError {
10    /// Vault file I/O or parsing.
11    Vault(VaultError),
12    /// Cryptographic operation (encrypt/decrypt/key parse).
13    Crypto(CryptoError),
14    /// Integrity check failed (MAC mismatch, tampering).
15    Integrity(String),
16    /// Key resolution or environment configuration.
17    Key(String),
18    /// Recipient management (authorize, revoke).
19    Recipient(String),
20    /// Secret management (add, remove, describe).
21    Secret(String),
22    /// GitHub key fetch.
23    GitHub(GitHubError),
24    /// General I/O.
25    Io(std::io::Error),
26}
27
28impl std::fmt::Display for MurkError {
29    #[allow(clippy::match_same_arms)]
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            MurkError::Vault(e) => write!(f, "{e}"),
33            MurkError::Crypto(e) => write!(f, "{e}"),
34            MurkError::Integrity(msg) => write!(f, "integrity check failed: {msg}"),
35            MurkError::Key(msg) => write!(f, "{msg}"),
36            MurkError::Recipient(msg) => write!(f, "{msg}"),
37            MurkError::Secret(msg) => write!(f, "{msg}"),
38            MurkError::GitHub(e) => write!(f, "{e}"),
39            MurkError::Io(e) => write!(f, "I/O error: {e}"),
40        }
41    }
42}
43
44impl std::error::Error for MurkError {
45    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
46        match self {
47            MurkError::Io(e) => Some(e),
48            _ => None,
49        }
50    }
51}
52
53impl From<VaultError> for MurkError {
54    fn from(e: VaultError) -> Self {
55        MurkError::Vault(e)
56    }
57}
58
59impl From<CryptoError> for MurkError {
60    fn from(e: CryptoError) -> Self {
61        MurkError::Crypto(e)
62    }
63}
64
65impl From<GitHubError> for MurkError {
66    fn from(e: GitHubError) -> Self {
67        MurkError::GitHub(e)
68    }
69}
70
71impl From<std::io::Error> for MurkError {
72    fn from(e: std::io::Error) -> Self {
73        MurkError::Io(e)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn display_integrity() {
83        let e = MurkError::Integrity("mac mismatch".into());
84        assert_eq!(e.to_string(), "integrity check failed: mac mismatch");
85    }
86
87    #[test]
88    fn display_key() {
89        let e = MurkError::Key("MURK_KEY not set".into());
90        assert_eq!(e.to_string(), "MURK_KEY not set");
91    }
92
93    #[test]
94    fn display_recipient() {
95        let e = MurkError::Recipient("not found".into());
96        assert_eq!(e.to_string(), "not found");
97    }
98
99    #[test]
100    fn display_secret() {
101        let e = MurkError::Secret("invalid".into());
102        assert_eq!(e.to_string(), "invalid");
103    }
104
105    #[test]
106    fn display_io() {
107        let e = MurkError::Io(std::io::Error::new(std::io::ErrorKind::NotFound, "gone"));
108        assert!(e.to_string().contains("I/O error"));
109    }
110
111    #[test]
112    fn from_vault_error() {
113        let ve = VaultError::Parse("bad json".into());
114        let e: MurkError = ve.into();
115        assert!(e.to_string().contains("bad json"));
116    }
117
118    #[test]
119    fn from_crypto_error() {
120        let ce = CryptoError::Decrypt("failed".into());
121        let e: MurkError = ce.into();
122        assert!(e.to_string().contains("failed"));
123    }
124
125    #[test]
126    fn from_io_error() {
127        let io = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
128        let e: MurkError = io.into();
129        assert!(e.to_string().contains("denied"));
130    }
131
132    #[test]
133    fn error_source_io() {
134        let e = MurkError::Io(std::io::Error::new(std::io::ErrorKind::Other, "test"));
135        assert!(std::error::Error::source(&e).is_some());
136    }
137
138    #[test]
139    fn error_source_non_io() {
140        let e = MurkError::Key("test".into());
141        assert!(std::error::Error::source(&e).is_none());
142    }
143}