use std::collections::BTreeMap;
use std::fmt;
use serde::{Deserialize, Serialize};
use crate::proto::LogIndex;
use crate::state_machine::StateMachine;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum KvCommand {
Set {
key: String,
value: String,
},
Delete {
key: String,
},
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum KvQuery {
Get {
key: String,
},
Len,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum KvResponse {
Set {
previous: Option<String>,
},
Deleted {
existed: bool,
},
Value(Option<String>),
Len(u64),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KvError;
impl fmt::Display for KvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("kv error")
}
}
impl std::error::Error for KvError {}
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Kv {
map: BTreeMap<String, String>,
}
pub type KvMachine = Kv;
impl StateMachine for Kv {
type Command = KvCommand;
type Query = KvQuery;
type Response = KvResponse;
type Error = KvError;
fn apply(&mut self, _index: LogIndex, command: &KvCommand) -> Result<KvResponse, KvError> {
Ok(match command {
KvCommand::Set { key, value } => {
let previous = self.map.insert(key.clone(), value.clone());
KvResponse::Set { previous }
}
KvCommand::Delete { key } => {
let existed = self.map.remove(key).is_some();
KvResponse::Deleted { existed }
}
})
}
fn query(&self, query: &KvQuery) -> Result<KvResponse, KvError> {
Ok(match query {
KvQuery::Get { key } => KvResponse::Value(self.map.get(key).cloned()),
KvQuery::Len => KvResponse::Len(self.map.len() as u64),
})
}
fn snapshot(&self) -> Result<Vec<u8>, KvError> {
crate::proto::encode(self).map_err(|_| KvError)
}
fn restore(&mut self, snapshot: &[u8]) -> Result<(), KvError> {
*self = crate::proto::decode(snapshot).map_err(|_| KvError)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_get_and_snapshot_round_trip() {
let mut kv = Kv::default();
assert_eq!(
kv.apply(
LogIndex(1),
&KvCommand::Set {
key: "a".into(),
value: "1".into(),
},
)
.unwrap(),
KvResponse::Set { previous: None }
);
assert_eq!(
kv.query(&KvQuery::Get { key: "a".into() }).unwrap(),
KvResponse::Value(Some("1".into()))
);
let snap = kv.snapshot().unwrap();
let mut restored = Kv::default();
restored.restore(&snap).unwrap();
assert_eq!(restored, kv);
}
}