1use thiserror::Error;
2
3#[derive(Debug, Error)]
4#[allow(dead_code)]
5pub enum Error {
6 #[error(transparent)]
7 Anyhow(#[from] anyhow::Error),
8
9 #[error("I/O error: {0}")]
10 Io(#[from] std::io::Error),
11
12 #[error("serialization error: {0}")]
13 Serde(&'static str),
14
15 #[error("invalid input: {0}")]
16 Invalid(&'static str),
17
18 #[error("{0}")]
19 Message(String),
20
21 #[error(transparent)]
22 Csv(#[from] csv::Error),
23
24 #[error(transparent)]
25 Persist(#[from] tempfile::PersistError),
26
27 #[error(transparent)]
28 Hex(#[from] hex::FromHexError),
29
30 #[error(transparent)]
31 SystemTime(#[from] std::time::SystemTimeError),
32
33 #[error(transparent)]
34 FromUtf8(#[from] std::string::FromUtf8Error),
35}
36
37#[allow(dead_code)]
38pub type Result<T> = std::result::Result<T, Error>;
39
40#[macro_export]
41macro_rules! bail {
42 ($($arg:tt)*) => { return Err($crate::error::Error::Message(format!($($arg)*))) };
43}
44
45pub fn exit_code(err: &Error) -> i32 {
49 match err {
50 Error::Io(_) | Error::Csv(_) | Error::Persist(_) | Error::SystemTime(_) => 5,
51 Error::Hex(_) | Error::FromUtf8(_) | Error::Serde(_) | Error::Invalid(_) => 2,
52 Error::Anyhow(e) => classify_message(&format!("{}", e)),
53 Error::Message(msg) => classify_message(msg),
54 }
55}
56
57fn classify_message(msg: &str) -> i32 {
58 let lower = msg.to_ascii_lowercase();
59 if lower.contains("integrity")
60 || lower.contains("tamper")
61 || lower.contains("mismatch")
62 || lower.contains("aead_auth_fail")
63 || lower.contains("header_mac_mismatch")
64 {
65 return 3;
66 }
67 if lower.contains("unlock") || lower.contains("failed to unlock vault") {
68 return 4;
69 }
70 if lower.contains("resource temporarily unavailable") || lower.contains("lock file") || lower.contains("locked") {
71 return 6;
72 }
73 if lower.contains("passphrase_policy_violation")
74 || lower.contains("requires")
75 || lower.contains("unsafe-stdout")
76 || lower.contains("mlock support not compiled")
77 || lower.contains("mlock failed")
78 || lower.contains("refusing")
79 || lower.contains("denied")
80 || lower.contains("tty")
81 {
82 return 7;
83 }
84 if lower.contains("invalid") || lower.contains("too ") || lower.contains("must be") || lower.contains("missing") {
85 return 2;
86 }
87 10
88}