sqlite-parser-nom 1.0.0

SQLite database file parser
Documentation
use crate::error::SQLiteError;

pub struct Database<'a> {
    pub header: DbHeader,
    pub pages: Vec<Page<'a>>,
}

pub struct DbHeader {
    pub page_size: PageSize,
    pub write_version: u8,
    pub read_version: u8,
    pub max_payload_fraction: u8,
    pub min_payload_fraction: u8,
    pub leaf_payload_fraction: u8,
    pub file_change_counter: u32,
    pub db_size: u32,
    pub first_freelist_page_no: u32,
    pub total_freelist_pages: u32,
    pub schema_cookie: u32,
    pub schema_format_no: u32,
    pub default_page_cache_size: u32,
    pub no_largest_root_b_tree: u32,
    pub db_text_encoding: TextEncoding,
    pub user_version: u32,
    pub incremental_vacuum_mode: u32,
    pub application_id: u32,
    pub version_valid_for_no: u32,
    pub sqlite_version_number: u32,
}

pub struct PageSize(pub u16);

impl PageSize {
    pub fn real_size(&self) -> usize {
        match self.0 {
            1 => 0x1_00_00,
            _ => self.0.into(),
        }
    }
}

#[derive(Copy, Clone)]
pub enum TextEncoding {
    Utf8,
    Utf16Le,
    Utf16Be,
}

impl TryFrom<u32> for TextEncoding {
    type Error = SQLiteError;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        use TextEncoding::*;

        match value {
            1 => Ok(Utf8),
            2 => Ok(Utf16Le),
            3 => Ok(Utf16Be),
            _ => Err(SQLiteError::UnknownTextEncodingError(value)),
        }
    }
}

pub enum Page<'a> {
    InteriorIndex(InteriorIndexPage<'a>),
    LeafIndex(LeafIndexPage<'a>),
    InteriorTable(InteriorTablePage),
    LeafTable(LeafTablePage<'a>),
}

pub struct InteriorPageHeader {
    pub first_freeblock_offset: Option<u16>,
    pub no_cells: u16,
    pub cell_content_offset: CellOffset,
    pub no_fragmented_bytes: u8,
    pub rightmost_pointer: u32,
}

pub struct InteriorIndexPage<'a> {
    pub header: InteriorPageHeader,
    pub cell_pointers: Vec<u16>,
    pub cells: Vec<InteriorIndexCell<'a>>,
}

pub struct InteriorTablePage {
    pub header: InteriorPageHeader,
    pub cell_pointers: Vec<u16>,
    pub cells: Vec<InteriorTableCell>,
}

pub struct IndexCellPayload<'a> {
    pub header_size: u64,
    pub column_types: Vec<SerialType>,
    pub column_values: Vec<Option<Payload<'a>>>,
    pub rowid: u64,
}

pub struct InteriorIndexCell<'a> {
    pub left_child_page_no: u32,
    pub payload_size: u64,
    pub payload: IndexCellPayload<'a>,
    pub overflow_page_no: Option<u32>,
}

pub struct InteriorTableCell {
    pub left_child_page_no: u32,
    pub integer_key: u64,
}

pub struct CellOffset(pub u16);

impl CellOffset {
    pub fn real_offset(&self) -> u32 {
        match self.0 {
            0 => 0x1_00_00,
            _ => self.0.into(),
        }
    }
}

pub struct LeafPageHeader {
    pub first_freeblock_offset: Option<u16>,
    pub no_cells: u16,
    pub cell_content_offset: CellOffset,
    pub no_fragmented_bytes: u8,
}

pub struct LeafIndexPage<'a> {
    pub header: LeafPageHeader,
    pub cell_pointers: Vec<u16>,
    pub cells: Vec<LeafIndexCell<'a>>,
}

pub struct LeafIndexCell<'a> {
    pub payload_size: u64,
    pub payload: IndexCellPayload<'a>,
    pub overflow_page_no: Option<u32>,
}

pub struct LeafTablePage<'a> {
    pub header: LeafPageHeader,
    pub cell_pointers: Vec<u16>,
    pub cells: Vec<LeafTableCell<'a>>,
}

pub struct TableCellPayload<'a> {
    pub header_size: u64,
    pub column_types: Vec<SerialType>,
    pub column_values: Vec<Option<Payload<'a>>>,
}

pub struct LeafTableCell<'a> {
    pub payload_size: u64,
    pub rowid: u64,
    pub payload: TableCellPayload<'a>,
    pub overflow_page_no: Option<u32>,
}

#[derive(Debug, Eq, PartialEq)]
pub enum SerialType {
    Null,
    I8,
    I16,
    I24,
    I32,
    I48,
    I64,
    F64,
    Const0,
    Const1,
    Reserved,
    Blob(u64),
    Text(u64),
}

impl From<u64> for SerialType {
    fn from(value: u64) -> Self {
        use SerialType::*;
        match value {
            0 => Null,
            1 => I8,
            2 => I16,
            3 => I24,
            4 => I32,
            5 => I48,
            6 => I64,
            7 => F64,
            8 => Const0,
            9 => Const1,
            10 | 11 => Reserved,
            n if n >= 12 && n % 2 == 0 => Blob(n),
            n if n >= 13 && n % 2 == 1 => Text(n),
            _ => unreachable!(),
        }
    }
}

impl SerialType {
    pub fn size(&self) -> usize {
        match self {
            SerialType::Null => 0,
            SerialType::I8 => 1,
            SerialType::I16 => 2,
            SerialType::I24 => 3,
            SerialType::I32 => 4,
            SerialType::I48 => 6,
            SerialType::I64 => 8,
            SerialType::F64 => 8,
            SerialType::Const0 => 0,
            SerialType::Const1 => 0,
            SerialType::Reserved => unimplemented!("reserved"),
            SerialType::Blob(n) => ((n - 12) / 2).try_into().unwrap(),
            SerialType::Text(n) => ((n - 13) / 2).try_into().unwrap(),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct RawText<'a>(&'a [u8]);

impl<'a> RawText<'a> {
    pub fn new(v: &'a [u8]) -> Self {
        RawText(v)
    }

    pub fn decode(&self, text_encoding: TextEncoding) -> String {
        match text_encoding {
            TextEncoding::Utf8 => String::from_utf8_lossy(self.0).to_string(),
            TextEncoding::Utf16Le => unimplemented!("utf16 not supported yet"),
            TextEncoding::Utf16Be => unimplemented!("utf16 not supported yet"),
        }
    }
}

impl<'a> From<&'a str> for RawText<'a> {
    fn from(value: &'a str) -> Self {
        RawText(value.as_bytes())
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Payload<'a> {
    I8(i8),
    I16(i16),
    I32(i32),
    I64(i64),
    F64(f64),
    Blob(&'a [u8]),
    Text(RawText<'a>),
}

impl<'a> From<&'a str> for Payload<'a> {
    fn from(value: &'a str) -> Self {
        Payload::Text(value.into())
    }
}

impl<'a> From<&'a [u8]> for Payload<'a> {
    fn from(value: &'a [u8]) -> Self {
        Payload::Blob(value)
    }
}

impl<'a> From<i8> for Payload<'a> {
    fn from(value: i8) -> Self {
        Payload::I8(value)
    }
}

impl<'a> From<i16> for Payload<'a> {
    fn from(value: i16) -> Self {
        Payload::I16(value)
    }
}

impl<'a> From<i32> for Payload<'a> {
    fn from(value: i32) -> Self {
        Payload::I32(value)
    }
}

impl<'a> From<i64> for Payload<'a> {
    fn from(value: i64) -> Self {
        Payload::I64(value)
    }
}

impl<'a> From<f64> for Payload<'a> {
    fn from(value: f64) -> Self {
        Payload::F64(value)
    }
}

#[cfg(test)]
impl<'a> Eq for Payload<'a> {}