minkv 0.3.0

一个轻量级持久化键值存储,支持内存和文件后端,提供 CLI 和 TCP 服务器
Documentation
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fs;
use std::path::{Path, PathBuf};

/// 键值存储可能产生的错误类型。
#[derive(Debug)]
pub enum KvError {
    /// 请求的键不存在。
    KeyNotFound,
    /// 无效的命令字符串(通常由 CLI 产生)。
    InvalidCommand(String),
    /// 底层 I/O 错误。
    Io(std::io::Error),
    /// JSON 序列化/反序列化错误。
    Json(serde_json::Error),
}

impl fmt::Display for KvError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            KvError::KeyNotFound => write!(f, "键不存在"),
            KvError::InvalidCommand(cmd) => write!(f, "无效命令: {}", cmd),
            KvError::Io(e) => write!(f, "I/O 错误: {}", e),
            KvError::Json(e) => write!(f, "JSON 错误: {}", e),
        }
    }
}

impl Error for KvError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            KvError::Io(e) => Some(e),
            KvError::Json(e) => Some(e),
            _ => None,
        }
    }
}

impl From<std::io::Error> for KvError {
    fn from(e: std::io::Error) -> Self {
        KvError::Io(e)
    }
}

impl From<serde_json::Error> for KvError {
    fn from(e: serde_json::Error) -> Self {
        KvError::Json(e)
    }
}

/// 自定义 `Result` 类型,错误固定为 [`KvError`]。
pub type Result<T> = std::result::Result<T, KvError>;

// ---------- Storage trait ----------

/// 键值存储后端的统一接口。
///
/// 所有后端必须提供:
/// - 键值查询 (`get`)
/// - 写入 (`set`)
/// - 删除 (`remove`)
/// - 访问底层数据 (`data`),用于实现 `scan` 等操作
pub trait Storage {
    /// 获取键对应的值,若不存在则返回 `None`。
    fn get(&self, key: &str) -> Option<&str>;
    /// 插入或更新键值对。
    fn set(&mut self, key: String, value: String);
    /// 删除键,返回被删除的旧值(如果存在)。
    fn remove(&mut self, key: &str) -> Option<String>;
    /// 返回整个内部 HashMap 的引用,供扫描等只读操作使用。
    fn data(&self) -> &HashMap<String, String>;
}

// ---------- MemoryStorage ----------

/// 纯内存存储,基于 `HashMap`,**不进行持久化**。
///
/// # 示例
///
/// ```rust
/// use minkv::{KvStore, MemoryStorage};
///
/// let mut store = KvStore::new(MemoryStorage::new());
/// store.set("key".into(), "value".into());
/// assert_eq!(store.get("key"), Some("value"));
/// ```
pub struct MemoryStorage {
    store: HashMap<String, String>,
}

impl MemoryStorage {
    /// 创建一个空的 `MemoryStorage`。
    pub fn new() -> Self {
        MemoryStorage {
            store: HashMap::new(),
        }
    }
}

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

impl Storage for MemoryStorage {
    fn get(&self, key: &str) -> Option<&str> {
        self.store.get(key).map(|s| s.as_str())
    }

    fn set(&mut self, key: String, value: String) {
        self.store.insert(key, value);
    }

    fn remove(&mut self, key: &str) -> Option<String> {
        self.store.remove(key)
    }

    fn data(&self) -> &HashMap<String, String> {
        &self.store
    }
}

// ---------- FileStorage ----------

/// 基于 JSON 文件的持久化存储后端。
///
/// 数据保存在一个 JSON 文件中,每次 `set`、`remove` 操作后可立即持久化,
/// 并在 `drop` 时自动保存。
///
/// # 示例
///
/// ```rust
/// use minkv::FileStorage;
/// let storage = FileStorage::open("data.json");
/// // 与 KvStore 配合使用
/// ```
pub struct FileStorage {
    store: HashMap<String, String>,
    path: PathBuf,
}

impl FileStorage {
    /// 打开或创建一个文件存储。
    ///
    /// 如果文件已存在,则加载其中的 JSON 数据;否则创建一个新的空存储。
    pub fn open<P: AsRef<Path>>(path: P) -> Self {
        let path = path.as_ref().to_path_buf();
        if path.exists() {
            match Self::load_from_path(&path) {
                Ok(fs) => fs,
                Err(e) => {
                    eprintln!("警告:加载存储文件失败,将创建新存储:{}", e);
                    FileStorage {
                        store: HashMap::new(),
                        path,
                    }
                }
            }
        } else {
            FileStorage {
                store: HashMap::new(),
                path,
            }
        }
    }

    /// 将给定的数据快照写入到指定路径,用于异步持久化。
    ///
    /// 这允许我们在释放写锁之后再进行磁盘 I/O。
    pub fn save_from_snapshot(data: &HashMap<String, String>, path: &Path) -> Result<()> {
        let json = serde_json::to_string_pretty(data)?;
        fs::write(path, json)?;
        Ok(())
    }

    /// 将当前存储内容保存到文件。
    pub fn save(&self) -> Result<()> {
        Self::save_from_snapshot(&self.store, &self.path)
    }

    /// 返回存储文件的路径。
    pub fn path(&self) -> &Path {
        &self.path
    }

    fn load_from_path(path: &Path) -> Result<Self> {
        let content = fs::read_to_string(path)?;
        let store: HashMap<String, String> = serde_json::from_str(&content)?;
        Ok(FileStorage {
            store,
            path: path.to_path_buf(),
        })
    }
}

impl Storage for FileStorage {
    fn get(&self, key: &str) -> Option<&str> {
        self.store.get(key).map(|s| s.as_str())
    }

    fn set(&mut self, key: String, value: String) {
        self.store.insert(key, value);
    }

    fn remove(&mut self, key: &str) -> Option<String> {
        self.store.remove(key)
    }

    fn data(&self) -> &HashMap<String, String> {
        &self.store
    }
}

impl Drop for FileStorage {
    fn drop(&mut self) {
        if let Err(e) = self.save() {
            eprintln!(
                "警告:保存存储到文件 {} 失败: {}",
                self.path.display(),
                e
            );
        }
    }
}

// ---------- 自定义迭代器 ScanIter ----------

/// 按前缀扫描键值对的迭代器。
///
/// 由 [`KvStore::scan`] 返回,实现 `Iterator<Item = (String, String)>`。
pub struct ScanIter<'a> {
    data: &'a HashMap<String, String>,
    prefix: String,
    keys: Vec<&'a String>,
    index: usize,
}

impl<'a> Iterator for ScanIter<'a> {
    type Item = (String, String);

    fn next(&mut self) -> Option<Self::Item> {
        while self.index < self.keys.len() {
            let key = self.keys[self.index];
            self.index += 1;
            if key.starts_with(&self.prefix) {
                if let Some(value) = self.data.get(key) {
                    return Some((key.clone(), value.clone()));
                }
            }
        }
        None
    }
}

// ---------- 泛型 KvStore ----------

/// 泛型键值存储,封装任意实现了 [`Storage`] trait 的后端。
///
/// 提供统一的 `get`、`set`、`remove`、`scan` 接口。
///
/// # 泛型参数
/// - `S: Storage` – 存储后端类型,例如 [`MemoryStorage`] 或 [`FileStorage`]。
pub struct KvStore<S: Storage> {
    backend: S,
}

impl<S: Storage> KvStore<S> {
    /// 使用给定的后端创建一个新的 `KvStore`。
    pub fn new(backend: S) -> Self {
        KvStore { backend }
    }

    /// 根据键获取值的字符串切片。
    ///
    /// 返回 `Option<&str>`,生命周期与 `self` 绑定。
    pub fn get(&self, key: &str) -> Option<&str> {
        self.backend.get(key)
    }

    /// 插入或更新键值对。
    pub fn set(&mut self, key: String, value: String) {
        self.backend.set(key, value)
    }

    /// 删除一个键,返回被删除的值(如果存在)。
    pub fn remove(&mut self, key: &str) -> Option<String> {
        self.backend.remove(key)
    }

    /// 返回一个前缀扫描迭代器。
    ///
    /// 迭代器将产生所有键以 `prefix` 开头的 `(key, value)` 对。
    ///
    /// # 示例
    ///
    /// ```rust
    /// # use minkv::{KvStore, MemoryStorage};
    /// let mut store = KvStore::new(MemoryStorage::new());
    /// store.set("user_1".into(), "Alice".into());
    /// store.set("user_2".into(), "Bob".into());
    /// let items: Vec<_> = store.scan("user_").collect();
    /// assert_eq!(items.len(), 2);
    /// ```
    pub fn scan(&self, prefix: &str) -> ScanIter<'_> {
        let data = self.backend.data();
        let keys: Vec<&String> = data.keys().collect();
        ScanIter {
            data,
            prefix: prefix.to_owned(),
            keys,
            index: 0,
        }
    }
}

/// 为 `FileStorage` 后端额外提供的便捷方法。
impl KvStore<FileStorage> {
    /// 打开一个文件持久化存储,等同于 `KvStore::new(FileStorage::open(path))`。
    pub fn open<P: AsRef<Path>>(path: P) -> Self {
        KvStore {
            backend: FileStorage::open(path),
        }
    }

    /// 立即将数据保存到文件。
    pub fn save(&self) -> Result<()> {
        self.backend.save()
    }

    /// 返回存储文件路径。
    pub fn path(&self) -> &Path {
        self.backend.path()
    }

    /// 获取整个数据的快照(HashMap 克隆),可用于在移出锁后进行持久化。
    pub fn clone_data(&self) -> HashMap<String, String> {
        self.backend.data().clone()
    }
}