block-db 0.2.0

Local, multi-threaded, durable byte DB.
Documentation
// Authors: Robert Lopez

use super::BlockDBOptions;
use crate::error::Error;
use std::{io::SeekFrom, ops::Deref, path::PathBuf};
use tokio::{
    fs::{try_exists, OpenOptions},
    io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
};

#[derive(Debug)]
pub struct OptionsStore {
    pub options: BlockDBOptions,
}

impl Deref for OptionsStore {
    type Target = BlockDBOptions;

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

impl OptionsStore {
    async fn new(
        options_file_path: PathBuf,
        options: Option<BlockDBOptions>,
    ) -> Result<Self, Error> {
        let options = options.unwrap_or_default();

        let mut file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(false)
            .open(&options_file_path)
            .await?;

        file.write_all(&serde_json::to_vec_pretty(&options)?)
            .await?;

        Ok(Self { options })
    }

    async fn load(
        options_file_path: PathBuf,
        new_options: Option<BlockDBOptions>,
    ) -> Result<Self, Error> {
        let mut file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(false)
            .open(&options_file_path)
            .await?;

        let mut options = {
            let mut buffer = vec![];
            file.read_to_end(&mut buffer).await?;

            serde_json::from_slice::<BlockDBOptions>(&buffer)?
        };

        if let Some(new_options) = new_options {
            if new_options.chunk_size != options.chunk_size {
                return Err(Error::InvalidOption(
                    "Cannot change Chunk Size after opening a BlockDB".into(),
                ));
            }

            options.max_file_size = new_options.max_file_size;

            file.seek(SeekFrom::Start(0)).await?;
            file.write_all(&serde_json::to_vec_pretty(&options)?)
                .await?;
        }

        Ok(Self { options })
    }

    pub(crate) async fn open(
        directory: PathBuf,
        options: Option<BlockDBOptions>,
    ) -> Result<Self, Error> {
        let options_file_path = directory.join("options.json");

        if try_exists(&options_file_path).await? {
            Self::load(options_file_path, options).await
        } else {
            Self::new(options_file_path, options).await
        }
    }
}