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,
InvalidCommand(String),
Io(std::io::Error),
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)
}
}
pub type Result<T> = std::result::Result<T, KvError>;
pub trait Storage {
fn get(&self, key: &str) -> Option<&str>;
fn set(&mut self, key: String, value: String);
fn remove(&mut self, key: &str) -> Option<String>;
fn data(&self) -> &HashMap<String, String>;
}
pub struct MemoryStorage {
store: HashMap<String, String>,
}
impl 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
}
}
pub struct FileStorage {
store: HashMap<String, String>,
path: PathBuf,
}
impl FileStorage {
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,
}
}
}
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
);
}
}
}
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
}
}
pub struct KvStore<S: Storage> {
backend: S,
}
impl<S: Storage> KvStore<S> {
pub fn new(backend: S) -> Self {
KvStore { backend }
}
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)
}
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,
}
}
}
impl KvStore<FileStorage> {
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()
}
pub fn clone_data(&self) -> HashMap<String, String> {
self.backend.data().clone()
}
}