jomini 0.35.0

Low level, performance oriented parser for save and game files from EU4, CK3, HOI4, Vic3, Imperator, and other PDS titles
Documentation
/// Error type for envelope operations
#[derive(Debug)]
pub struct EnvelopeError {
    kind: EnvelopeErrorKind,
}

impl EnvelopeError {
    /// Returns true if this error is due to a missing ZIP entry
    pub fn is_missing_entry(&self) -> bool {
        matches!(self.kind, EnvelopeErrorKind::ZipMissingEntry(_))
    }

    /// Returns the specific kind of this error.
    pub fn kind(&self) -> &EnvelopeErrorKind {
        &self.kind
    }
}

impl From<EnvelopeErrorKind> for EnvelopeError {
    fn from(kind: EnvelopeErrorKind) -> Self {
        EnvelopeError { kind }
    }
}

impl From<std::io::Error> for EnvelopeError {
    fn from(error: std::io::Error) -> Self {
        EnvelopeError {
            kind: EnvelopeErrorKind::Io(error),
        }
    }
}

/// Specific kind of envelope error
#[derive(Debug)]
pub enum EnvelopeErrorKind {
    /// IO error during file operations
    Io(std::io::Error),
    /// Error from ZIP archive processing
    Zip(rawzip::Error),
    /// Invalid or corrupted save file header
    InvalidHeader,
    /// ZIP archive missing a requested entry
    ZipMissingEntry(String),
    /// ZIP entry uses unsupported compression method
    ZipUnsupportedCompression,
    /// Decompressed data did not match the expected CRC32 checksum
    ChecksumMismatch {
        /// Checksum recorded in the archive
        expected: u32,
        /// Checksum computed over the decompressed data
        actual: u32,
    },
    /// Decompressed data did not match the expected uncompressed size
    SizeMismatch {
        /// Size recorded in the archive
        expected: u64,
        /// Number of bytes actually decompressed
        actual: u64,
    },
}

impl std::error::Error for EnvelopeError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match &self.kind {
            EnvelopeErrorKind::Io(err) => Some(err),
            EnvelopeErrorKind::Zip(err) => Some(err),
            _ => None,
        }
    }
}

impl std::fmt::Display for EnvelopeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.kind {
            EnvelopeErrorKind::Io(err) => write!(f, "IO error: {}", err),
            EnvelopeErrorKind::Zip(err) => write!(f, "Zip error: {}", err),
            EnvelopeErrorKind::InvalidHeader => write!(f, "Invalid header"),
            EnvelopeErrorKind::ZipMissingEntry(path) => write!(f, "Zip missing entry: {}", path),
            EnvelopeErrorKind::ZipUnsupportedCompression => {
                write!(f, "Zip unsupported compression method")
            }
            EnvelopeErrorKind::ChecksumMismatch { expected, actual } => write!(
                f,
                "checksum mismatch: expected {:#010x}, computed {:#010x}",
                expected, actual
            ),
            EnvelopeErrorKind::SizeMismatch { expected, actual } => write!(
                f,
                "size mismatch: expected {} bytes, decompressed {} bytes",
                expected, actual
            ),
        }
    }
}