bitcasky 0.1.2

Bitcasky is a Rust implementation of the Bitcask key-value store
Documentation
use core::panic;
use std::path::{Path, PathBuf};

use crate::storage_id::StorageId;

const LOCK_FILE_EXTENSION: &str = "lock";
const MERGE_META_FILE_EXTENSION: &str = "meta";
const DATA_FILE_EXTENSION: &str = "data";
const HINT_FILE_EXTENSION: &str = "hint";

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum FileType {
    Unknown,
    LockFile,
    MergeMeta,
    DataFile,
    HintFile,
}

impl FileType {
    pub fn get_path<P: AsRef<Path>>(&self, base_dir: P, storage_id: Option<StorageId>) -> PathBuf {
        base_dir.as_ref().join(match self {
            Self::LockFile => format!("bitcask.{}", LOCK_FILE_EXTENSION),
            Self::MergeMeta => format!("merge.{}", MERGE_META_FILE_EXTENSION),
            Self::DataFile => format!("{}.{}", storage_id.unwrap(), DATA_FILE_EXTENSION),
            Self::HintFile => format!("{}.{}", storage_id.unwrap(), HINT_FILE_EXTENSION),
            Self::Unknown => panic!("get path for unknown data type"),
        })
    }

    pub fn check_file_belongs_to_type(&self, file_path: &Path) -> bool {
        let ft = match file_path.extension() {
            None => FileType::Unknown,
            Some(os_str) => match os_str.to_str() {
                Some(LOCK_FILE_EXTENSION) => FileType::LockFile,
                Some(MERGE_META_FILE_EXTENSION) => FileType::MergeMeta,
                Some(DATA_FILE_EXTENSION) => FileType::DataFile,
                Some(HINT_FILE_EXTENSION) => FileType::HintFile,
                _ => FileType::Unknown,
            },
        };
        *self == ft
    }

    pub fn parse_storage_id_from_file_name(&self, file_path: &Path) -> Option<StorageId> {
        let binding = file_path.file_name().unwrap().to_string_lossy();
        let (storage_id_str, _) = binding.split_at(binding.len() - self.extension().len() - 1);
        match self {
            Self::LockFile => None,
            Self::MergeMeta => None,
            Self::DataFile => Some(storage_id_str),
            Self::HintFile => Some(storage_id_str),
            Self::Unknown => panic!("get path for unknown data type"),
        }
        .map(|storage_id_str| storage_id_str.parse::<StorageId>())
        .transpose()
        .unwrap_or(None)
    }

    fn extension(&self) -> &str {
        match self {
            Self::LockFile => LOCK_FILE_EXTENSION,
            Self::MergeMeta => MERGE_META_FILE_EXTENSION,
            Self::DataFile => DATA_FILE_EXTENSION,
            Self::HintFile => HINT_FILE_EXTENSION,
            Self::Unknown => panic!("get path for unknown data type"),
        }
    }
}

impl std::fmt::Display for FileType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FileType::Unknown => f.write_str("Unknown"),
            FileType::LockFile => f.write_str("LockFile"),
            FileType::MergeMeta => f.write_str("MergeMetaFile"),
            FileType::DataFile => f.write_str("DataFile"),
            FileType::HintFile => f.write_str("HintFile"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::get_temporary_directory_path;

    #[test]
    fn test_file_type() {
        let dir = get_temporary_directory_path();

        let p = FileType::LockFile.get_path(&dir, None);
        assert!(FileType::LockFile.check_file_belongs_to_type(&p));
        let p = FileType::HintFile.get_path(&dir, Some(123));
        assert!(FileType::HintFile.check_file_belongs_to_type(&p));
        let p = FileType::DataFile.get_path(&dir, Some(100));
        assert!(FileType::DataFile.check_file_belongs_to_type(&p));
        let p = FileType::MergeMeta.get_path(&dir, Some(100));
        assert!(FileType::MergeMeta.check_file_belongs_to_type(&p));

        assert!(!FileType::LockFile.check_file_belongs_to_type(&dir.join("")));
        assert!(!FileType::DataFile.check_file_belongs_to_type(&dir.join("")));
        assert!(!FileType::HintFile.check_file_belongs_to_type(&dir.join("")));
        assert!(!FileType::MergeMeta.check_file_belongs_to_type(&dir.join("")));

        assert!(!FileType::LockFile.check_file_belongs_to_type(&dir.join(".abc")));
        assert!(!FileType::DataFile.check_file_belongs_to_type(&dir.join(".abc")));
        assert!(!FileType::HintFile.check_file_belongs_to_type(&dir.join(".abc")));
        assert!(!FileType::MergeMeta.check_file_belongs_to_type(&dir.join(".abc")));
    }
}