block-db 0.2.0

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

use crate::{size::ByteSize, BlockDB};
use std::collections::HashMap;

#[derive(Debug, Clone, Default)]
pub struct ChunkCount {
    pub used: usize,
    pub free: usize,
}

#[derive(Debug, Clone, Default)]
pub struct TestState {
    pub data_file_count: usize,
    pub block_db_byte_size: ByteSize,
    pub data_file_byte_size_map: HashMap<String, ByteSize>,
    pub data_file_chunk_count_map: HashMap<String, ChunkCount>,
}

impl TestState {
    pub async fn assert_against_block_db(&self, block_db: &BlockDB) -> Result<(), String> {
        let free_chunk_count = block_db.free_chunk_count().await;
        let expected_free_chunk_count = self
            .data_file_chunk_count_map
            .iter()
            .map(|(_, ChunkCount { free, .. })| *free)
            .sum::<usize>();

        if free_chunk_count != expected_free_chunk_count {
            return Err(format!("{free_chunk_count} != {expected_free_chunk_count} \n Invalid BlockDB free chunk count"));
        }

        let used_chunk_count = block_db.used_chunk_count().await;
        let expected_used_chunk_count = self
            .data_file_chunk_count_map
            .iter()
            .map(|(_, ChunkCount { used, .. })| *used)
            .sum::<usize>();

        if used_chunk_count != expected_used_chunk_count {
            return Err(format!("{used_chunk_count} != {expected_used_chunk_count} \n Invalid BlockDB used chunk count"));
        }

        let expected_data_file_count = block_db.data_files.read().await.len();
        if self.data_file_count != expected_data_file_count {
            return Err(format!(
                "{} != {expected_data_file_count} \n Invalid DataFiles count",
                self.data_file_count
            ));
        }

        let expected_block_db_byte_size = block_db.byte_size().await;
        if self.block_db_byte_size != expected_block_db_byte_size {
            return Err(format!(
                "{:?} != {expected_block_db_byte_size:?} \n Invalid BlockDB ByteSize",
                self.block_db_byte_size
            ));
        }

        for (data_file_id, data_file_byte_size) in self.data_file_byte_size_map.iter() {
            let data_files = block_db.data_files.read().await;
            let data_file = data_files.get(data_file_id).unwrap().clone();

            let expected_data_file_byte_size = data_file.read().await.byte_size();
            if data_file_byte_size != &expected_data_file_byte_size {
                return Err(format!(
                    "{data_file_byte_size:?} != {expected_data_file_byte_size:?} \n Invalid DataFile {data_file_id} ByteSize"
                ));
            }
        }

        for (data_file_id, ChunkCount { used, free }) in self.data_file_chunk_count_map.iter() {
            let data_files = block_db.data_files.read().await;
            let data_file = data_files.get(data_file_id).unwrap().clone();

            let expected_used_chunk_count = data_file.read().await.used_chunk_count();

            if *used != expected_used_chunk_count {
                return Err(format!("{used} != {expected_used_chunk_count} \n Invalid DataFile {data_file_id} used chunk count"));
            }

            let expected_free_chunk_count = data_file.read().await.free_chunk_count();

            if *free != expected_free_chunk_count {
                return Err(format!("{free} != {expected_free_chunk_count} \n Invalid DataFile {data_file_id} free chunk count"));
            }
        }

        Ok(())
    }
}