use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ExitCode {
Success = 0,
General = 1,
Usage = 2,
WrongPassword = 3,
Corrupted = 4,
UnsupportedVersion = 5,
MissingShard = 6,
Permission = 7,
Cancelled = 8,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("not an omy file: magic mismatch (got {got:02x?})")]
BadMagic {
got: [u8; 8],
},
#[error("unsupported format version {found_major}.{found_minor}, this build supports up to {supported_major}.x")]
UnsupportedVersion {
found_major: u16,
found_minor: u16,
supported_major: u16,
},
#[error("truncated input while reading {context}: need {need} bytes, got {got}")]
Truncated {
context: &'static str,
need: usize,
got: usize,
},
#[error("malformed header: {reason}")]
MalformedHeader {
reason: &'static str,
},
#[error("no key slot could be unwrapped with the provided credentials")]
NoMatchingSlot,
#[error("header MAC verification failed: file tampered or truncated")]
HeaderMacMismatch,
#[error("chunk {index} failed authentication: data corrupted or tampered")]
ChunkAuthFailed {
index: u64,
},
#[error("unknown critical TLV type 0x{tlv_type:04x}: cannot safely open this file")]
UnknownCriticalTlv {
tlv_type: u16,
},
#[error("required TLV type 0x{tlv_type:04x} is missing")]
MissingTlv {
tlv_type: u16,
},
#[error("malformed TLV type 0x{tlv_type:04x}: {reason}")]
MalformedTlv {
tlv_type: u16,
reason: &'static str,
},
#[error("chunk index {index} out of range (file has {total} chunks)")]
ChunkOutOfRange {
index: u64,
total: u64,
},
#[error("too many key slots: {got} exceeds maximum {max}")]
TooManySlots {
got: usize,
max: usize,
},
#[error("chunk size {got} out of allowed range [{min}, {max}]")]
InvalidChunkSize {
got: u32,
min: u32,
max: u32,
},
#[error("shard {index} CRC mismatch: expected {expected:08x}, computed {computed:08x}")]
ShardCrcMismatch {
index: u32,
expected: u32,
computed: u32,
},
#[error("missing shards: {missing:?} (expected {total} total)")]
MissingShards {
missing: Vec<u32>,
total: u32,
},
#[error("shard {index} belongs to a different file")]
ShardUuidMismatch {
index: u32,
},
#[error("content hash mismatch after decryption")]
ContentHashMismatch,
#[error("compression error: {reason}")]
Compression {
reason: String,
},
#[error("key derivation failed: {reason}")]
KeyDerivation {
reason: &'static str,
},
#[error("io error: {0}")]
Io(#[from] std::io::Error),
}
impl Error {
#[must_use]
pub const fn exit_code(&self) -> ExitCode {
match self {
Self::NoMatchingSlot => ExitCode::WrongPassword,
Self::BadMagic { .. }
| Self::HeaderMacMismatch
| Self::ChunkAuthFailed { .. }
| Self::ShardCrcMismatch { .. }
| Self::ContentHashMismatch
| Self::Truncated { .. }
| Self::MalformedHeader { .. }
| Self::MalformedTlv { .. } => ExitCode::Corrupted,
Self::UnsupportedVersion { .. } | Self::UnknownCriticalTlv { .. } => {
ExitCode::UnsupportedVersion
}
Self::MissingShards { .. } | Self::ShardUuidMismatch { .. } => ExitCode::MissingShard,
Self::InvalidChunkSize { .. } | Self::TooManySlots { .. } => ExitCode::Usage,
Self::Io(_) => ExitCode::Permission,
_ => ExitCode::General,
}
}
#[must_use]
pub const fn code(&self) -> &'static str {
match self {
Self::BadMagic { .. } => "BAD_MAGIC",
Self::UnsupportedVersion { .. } => "UNSUPPORTED_VERSION",
Self::Truncated { .. } => "TRUNCATED",
Self::MalformedHeader { .. } => "MALFORMED_HEADER",
Self::NoMatchingSlot => "WRONG_PASSWORD",
Self::HeaderMacMismatch => "HEADER_MAC_MISMATCH",
Self::ChunkAuthFailed { .. } => "CHUNK_AUTH_FAILED",
Self::UnknownCriticalTlv { .. } => "UNKNOWN_CRITICAL_TLV",
Self::MissingTlv { .. } => "MISSING_TLV",
Self::MalformedTlv { .. } => "MALFORMED_TLV",
Self::ChunkOutOfRange { .. } => "CHUNK_OUT_OF_RANGE",
Self::TooManySlots { .. } => "TOO_MANY_SLOTS",
Self::InvalidChunkSize { .. } => "INVALID_CHUNK_SIZE",
Self::ShardCrcMismatch { .. } => "SHARD_CRC_MISMATCH",
Self::MissingShards { .. } => "MISSING_SHARDS",
Self::ShardUuidMismatch { .. } => "SHARD_UUID_MISMATCH",
Self::ContentHashMismatch => "CONTENT_HASH_MISMATCH",
Self::Compression { .. } => "COMPRESSION_ERROR",
Self::KeyDerivation { .. } => "KEY_DERIVATION_FAILED",
Self::Io(_) => "IO_ERROR",
}
}
}
impl fmt::Display for ExitCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", *self as u8)
}
}
pub type Result<T> = core::result::Result<T, Error>;