start 0.4.4

StartDB – Embedded NoSQL Database in Rust
Documentation
use std::{cell::{RefCell, RefMut}, rc::Rc};

use crate::HandleResult;

use super::{
    catalog::collection::RawDocument,
    collection::{_SYSTEM_MASTER, _SYSTEM_TRASH},
    operation_context::{ensure_capacity, OperationContext},
    ops::insert::insert_one_by_offset,
    storage::start_storage::StartStorage,
};

pub fn get_header(op_ctx: &mut OperationContext) -> HandleResult<Header> {
    let storage = op_ctx.storage();

    if storage.borrow().len() == 0 {
        insert_one_by_offset(op_ctx, _SYSTEM_MASTER.offset, RawDocument {
            flag_deleted: false,
            next_document: 0,
            content_length: 40,
            content: _SYSTEM_MASTER.to_bytes(),
        })?;

        insert_one_by_offset(op_ctx, _SYSTEM_TRASH.offset, RawDocument {
            flag_deleted: false,
            next_document: 0,
            content_length: 40,
            content: _SYSTEM_TRASH.to_bytes(),
        })?;

        match Header::create(&storage) {
            Ok(header) => Ok(header),
            Err(HeaderError::DatabaseError(err)) => panic!("DBerr: {}", err),
            Err(err) => panic!("Header parsing error: {:?}", err),
        }
    } else {
        match Header::parse(&storage) {
            Ok(header) => Ok(header),
            Err(HeaderError::DatabaseError(err)) => panic!("DBerr: {}", err),
            Err(err) => panic!("Header parsing error: {:?}", err),
        }
    }
}

#[derive(Debug)]
pub struct Header {
    pub magic_number: MagicNumber,
    pub version: Version,
}

#[derive(Debug)]
pub struct MagicNumber(pub u32);

#[derive(Debug)]
pub struct Version(pub String);

const MAGIC_NUMBER: u32 = 0x14841488;
const MAGIC_NUMBER_OFFSET: usize = 0;

const VERSION_OFFSET: usize = 4;
const CURRENT_VERSION: &str = "0.0.1";

impl Header {
    pub fn parse(storage: &Rc<RefCell<StartStorage>>) -> Result<Self, HeaderError> {
        if storage.borrow().len() < VERSION_OFFSET + 8 {
            return Err(HeaderError::MagicNumberParsingError("File is too short".to_string()));
        }

        let magic_number = MagicNumber::get(storage)?;
        Self::ensure_capacity(&mut storage.borrow_mut())?;

        Ok(Header {
            magic_number,
            version: Version::get(storage)?,
        })
    }

    pub fn create(storage: &Rc<RefCell<StartStorage>>) -> Result<Self, HeaderError> {
        Self::ensure_capacity(&mut storage.borrow_mut())?;

        Ok(Header {
            magic_number: MagicNumber::create(storage)?,
            version: Version::create(storage)?,
        })
    }

    fn ensure_capacity(ss: &mut RefMut<'_, StartStorage>) -> Result<(), HeaderError> {
        ensure_capacity(ss, 100).map_err(|err| {
            HeaderError::DatabaseError(format!("Ensure capacity error: {:?}", err))
        })
    }
}

impl MagicNumber {
    fn get(storage: &Rc<RefCell<StartStorage>>) -> Result<Self, HeaderError> {
        let ss = storage.borrow();
        let mut bytes = [0u8; 4];
        bytes.copy_from_slice(&ss[MAGIC_NUMBER_OFFSET..MAGIC_NUMBER_OFFSET + 4]);
        let magic_number = u32::from_le_bytes(bytes);
        if magic_number != MAGIC_NUMBER {
            return Err(HeaderError::MagicNumberParsingError(
                format!("Invalid database file! ({})", magic_number),
            ));
        }
        Ok(MagicNumber(magic_number))
    }

    fn create(storage: &Rc<RefCell<StartStorage>>) -> Result<Self, HeaderError> {
        let mut ss = storage.borrow_mut();
        ss[MAGIC_NUMBER_OFFSET..MAGIC_NUMBER_OFFSET + 4]
            .copy_from_slice(&MAGIC_NUMBER.to_le_bytes());
        Ok(MagicNumber(MAGIC_NUMBER))
    }
}

impl Default for MagicNumber {
    fn default() -> Self {
        Self(Default::default())
    }
}

impl Version {
    fn get(storage: &Rc<RefCell<StartStorage>>) -> Result<Self, HeaderError> {
        let ss = storage.borrow();
        let mut bytes = [0u8; 8];
        bytes.copy_from_slice(&ss[VERSION_OFFSET..VERSION_OFFSET + 8]);
        String::from_utf8(bytes.to_vec())
            .map(|v| Version(v.trim_matches(char::from(0)).to_string()))
            .map_err(|_| HeaderError::VersionParsingError("Cannot parse string version".into()))
    }

    fn create(storage: &Rc<RefCell<StartStorage>>) -> Result<Self, HeaderError> {
        let mut ss = storage.borrow_mut();
        let mut bytes = [0u8; 8];
        bytes[..CURRENT_VERSION.len()].copy_from_slice(CURRENT_VERSION.as_bytes());
        ss[VERSION_OFFSET..VERSION_OFFSET + 8].copy_from_slice(&bytes);
        Ok(Version(CURRENT_VERSION.to_string()))
    }
}

impl Default for Version {
    fn default() -> Self {
        Self(Default::default())
    }
}

#[derive(Debug)]
pub enum HeaderError {
    MagicNumberParsingError(String),
    VersionParsingError(String),
    CollectionsParsingError(String),
    DatabaseError(String),
}