use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum EncryptionError {
NeedPassword,
WrongPassword,
NotDecryptable(String),
Malformed(String),
}
impl EncryptionError {
pub fn needs_password(&self) -> bool {
matches!(self, Self::NeedPassword | Self::WrongPassword)
}
pub fn code(&self) -> &'static str {
match self {
Self::NeedPassword => "password_required",
Self::WrongPassword => "wrong_password",
Self::NotDecryptable(_) => "not_decryptable",
Self::Malformed(_) => "malformed_encryption",
}
}
}
impl fmt::Display for EncryptionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NeedPassword => {
f.write_str("document is encrypted (a password is required to open it)")
}
Self::WrongPassword => f.write_str("document is encrypted and the password is wrong"),
Self::NotDecryptable(what) => {
write!(
f,
"document is encrypted with an unsupported scheme ({what})"
)
}
Self::Malformed(what) => write!(
f,
"document is encrypted, but its encryption header is damaged ({what})"
),
}
}
}
impl std::error::Error for EncryptionError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_the_password_cases_need_a_password() {
assert!(EncryptionError::NeedPassword.needs_password());
assert!(EncryptionError::WrongPassword.needs_password());
assert!(!EncryptionError::NotDecryptable("ODF".into()).needs_password());
assert!(!EncryptionError::Malformed("keyBits".into()).needs_password());
}
#[test]
fn codes_are_distinct_and_stable() {
let all = [
EncryptionError::NeedPassword,
EncryptionError::WrongPassword,
EncryptionError::NotDecryptable("x".into()),
EncryptionError::Malformed("y".into()),
];
let codes: std::collections::BTreeSet<_> = all.iter().map(|e| e.code()).collect();
assert_eq!(codes.len(), all.len());
assert_eq!(EncryptionError::NeedPassword.code(), "password_required");
assert_eq!(EncryptionError::WrongPassword.code(), "wrong_password");
}
#[test]
fn display_is_the_office_wording() {
assert_eq!(
EncryptionError::NeedPassword.to_string(),
"document is encrypted (a password is required to open it)"
);
assert_eq!(
EncryptionError::WrongPassword.to_string(),
"document is encrypted and the password is wrong"
);
assert_eq!(
EncryptionError::NotDecryptable("XOR obfuscation".into()).to_string(),
"document is encrypted with an unsupported scheme (XOR obfuscation)"
);
assert_eq!(
EncryptionError::Malformed("keyBits".into()).to_string(),
"document is encrypted, but its encryption header is damaged (keyBits)"
);
}
}