sq3-rs 0.2.10

SQLite reader with no dependencies
Documentation
use std::num::NonZeroU32;
use std::ops::Deref;

use sq3_derive::Name;

use crate::{
    result::{SqliteError, SqliteResult},
    traits::{ParseBytes, TypeName},
};

/// # In-header database size (4 Bytes)
///
///  The in-header database size is a 4-byte big-endian integer at offset 28
/// into the header stores the size of the database file in pages. If this
/// in-header datasize size is not valid (see the next paragraph), then the
/// database size is computed by looking at the actual size of the database
/// file. Older versions of Sqlite ignored the in-header database size and used
/// the actual file size exclusively. Newer versions of Sqlite use the in-header
/// database size if it is available but fall back to the actual file size if
/// the in-header database size is not valid.
///
///  The in-header database size is only considered to be valid if it is
/// non-zero and if the 4-byte change counter at offset 24 exactly matches the
/// 4-byte version-valid-for number at offset 92. The in-header database size is
/// always valid when the database is only modified using recent versions of
/// Sqlite, versions 3.7.0 (2010-07-21) and later. If a legacy version of Sqlite
/// writes to the database, it will not know to update the in-header database
/// size and so the in-header database size could be incorrect. But legacy
/// versions of Sqlite will also leave the version-valid-for number at offset 92
/// unchanged so it will not match the change-counter. Hence, invalid in-header
/// database sizes can be detected (and ignored) by observing when the
/// change-counter does not match the version-valid-for number.
#[derive(Debug, Name)]
pub struct DatabaseFileSizeInPages(u32);

impl Default for DatabaseFileSizeInPages {
    fn default() -> Self {
        Self(1)
    }
}
impl Deref for DatabaseFileSizeInPages {
    type Target = u32;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl ParseBytes for DatabaseFileSizeInPages {
    const LENGTH_BYTES: usize = 4;

    fn parsing_handler(bytes: &[u8]) -> SqliteResult<Self> {
        let buf: [u8; Self::LENGTH_BYTES] = bytes.try_into()?;

        let database_size = NonZeroU32::new(u32::from_be_bytes(buf)).ok_or(SqliteError::Custom(
            "DatabaseFileSizeInPages can't be `0`".into(),
        ))?;

        Ok(Self(database_size.get()))
    }
}