use std::{fmt::Debug, path::PathBuf};
use serde::{de::DeserializeOwned, Serialize};
use tokio::sync::{mpsc, oneshot};
use super::{
error::{InnerKvError, KvError::*, KvResult},
sled_bindings::{handle_delete, handle_exists, handle_get, handle_put, handle_reserve},
types::{
Command::{self, *},
KeyReservation, DEFAULT_KV_NAME, DEFAULT_KV_PATH,
},
};
use crate::encrypted_sled;
#[derive(Clone)]
pub struct Kv<V> {
sender: mpsc::UnboundedSender<Command<V>>,
}
impl<V: 'static> Kv<V>
where
V: Debug + Send + Sync + Serialize + DeserializeOwned,
{
pub fn new(root_path: PathBuf, key: [u8; 32]) -> KvResult<Self> {
let kv_path = root_path.join(DEFAULT_KV_PATH).join(DEFAULT_KV_NAME);
let kv_path = kv_path.to_string_lossy().to_string();
Self::with_db_name(kv_path, key)
}
pub fn with_db_name(full_db_name: String, encryption_key: [u8; 32]) -> KvResult<Self> {
let (sender, rx) = mpsc::unbounded_channel();
let kv = get_kv_store(&full_db_name, encryption_key)?;
tokio::spawn(kv_cmd_handler(rx, kv));
Ok(Self { sender })
}
pub async fn reserve_key(&self, key: String) -> KvResult<KeyReservation> {
let (resp_tx, resp_rx) = oneshot::channel();
self.sender
.send(ReserveKey { key, resp: resp_tx })
.map_err(|err| SendErr(err.to_string()))?;
resp_rx.await?.map_err(ReserveErr)
}
pub async fn unreserve_key(&self, reservation: KeyReservation) {
let _ = self.sender.send(UnreserveKey { reservation });
}
pub async fn put(&self, reservation: KeyReservation, value: V) -> KvResult<()> {
let (resp_tx, resp_rx) = oneshot::channel();
self.sender
.send(Put { reservation, value, resp: resp_tx })
.map_err(|e| SendErr(e.to_string()))?;
resp_rx.await?.map_err(PutErr)
}
pub async fn get(&self, key: &str) -> KvResult<V> {
let (resp_tx, resp_rx) = oneshot::channel();
self.sender
.send(Get { key: key.to_string(), resp: resp_tx })
.map_err(|e| SendErr(e.to_string()))?;
resp_rx.await?.map_err(GetErr)
}
pub async fn delete(&self, key: &str) -> KvResult<()> {
let (resp_tx, resp_rx) = oneshot::channel();
self.sender
.send(Delete { key: key.to_string(), resp: resp_tx })
.map_err(|e| SendErr(e.to_string()))?;
resp_rx.await?.map_err(DeleteErr)
}
pub async fn exists(&self, key: &str) -> KvResult<bool> {
let (resp_tx, resp_rx) = oneshot::channel();
self.sender
.send(Exists { key: key.to_string(), resp: resp_tx })
.map_err(|e| SendErr(e.to_string()))?;
resp_rx.await?.map_err(ExistsErr)
}
}
#[tracing::instrument(skip_all, fields(db_name))]
pub fn get_kv_store(db_name: &str, key: [u8; 32]) -> encrypted_sled::Result<encrypted_sled::Db> {
tracing::debug!("Decrypting KV store");
let kv = encrypted_sled::Db::open(db_name, key)?;
if kv.was_recovered() {
tracing::debug!("Found exisiting database");
} else {
tracing::debug!("No existing database found, creating a new one.");
}
Ok(kv)
}
async fn kv_cmd_handler<V>(mut rx: mpsc::UnboundedReceiver<Command<V>>, kv: encrypted_sled::Db)
where
V: Debug + Serialize + DeserializeOwned + 'static,
{
while let Some(cmd) = rx.recv().await {
match cmd {
ReserveKey { key, resp } => {
handle_response(handle_reserve(&kv, key), resp);
},
UnreserveKey { reservation } => {
let kv_resp = kv.remove(reservation.key);
match kv_resp {
Ok(_) => {},
Err(err) => tracing::warn!("Failed to remove key from database: {}", err),
}
},
Put { reservation, value, resp } => {
handle_response(handle_put(&kv, reservation, value), resp);
},
Get { key, resp } => {
handle_response(handle_get(&kv, key), resp);
},
Exists { key, resp } => {
handle_response(handle_exists(&kv, &key), resp);
},
Delete { key, resp } => {
handle_response(handle_delete(&kv, key), resp);
},
}
}
}
fn handle_response<T>(
kv_resp: Result<T, InnerKvError>,
resp: oneshot::Sender<Result<T, InnerKvError>>,
) where
T: Debug,
{
if let Err(err) = resp.send(kv_resp) {
tracing::warn!("KVDB response channel receiver to dropped with: {:?}", err);
};
}