use pdk_core::classy::extract::context::ConfigureContext;
use pdk_core::classy::extract::{Extract, FromContext};
use pdk_core::classy::proxy_wasm::types::Status;
use std::convert::Infallible;
use std::rc::Rc;
use thiserror::Error;
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum StoreMode {
Always,
Absent,
Cas(u32),
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum LocalStorageError {
#[error("Cas mismatch.")]
CasMismatch,
#[error("Low level error.")]
ProxyWasm(Status),
}
impl From<Status> for LocalStorageError {
fn from(value: Status) -> Self {
Self::ProxyWasm(value)
}
}
pub trait LocalStorage {
fn set(&self, key: &str, value: &[u8], mode: StoreMode) -> Result<(), LocalStorageError>;
fn get(&self, key: &str) -> Result<Option<(Vec<u8>, u32)>, LocalStorageError>;
fn delete(&self, key: &str) -> Result<(), LocalStorageError>;
fn keys(&self) -> Vec<String>;
}
#[derive(Clone)]
pub struct SharedData {
shared_data: Rc<dyn pdk_core::classy::SharedData>,
}
impl SharedData {
fn store_mode_to_cas(
&self,
key: &str,
mode: StoreMode,
) -> Result<Option<u32>, LocalStorageError> {
match mode {
StoreMode::Always => Ok(None),
StoreMode::Absent => {
match self.shared_data.shared_data_get(key) {
(Some(data), cas) => {
if data.is_empty() {
Ok(Some(cas.unwrap_or(u32::MAX))) } else {
Err(LocalStorageError::CasMismatch)
}
}
(None, Some(cas)) => Ok(Some(cas)), (None, None) => Ok(Some(u32::MAX)),
}
}
StoreMode::Cas(cas) => Ok(Some(cas)),
}
}
}
impl FromContext<ConfigureContext> for SharedData {
type Error = Infallible;
fn from_context(context: &ConfigureContext) -> Result<Self, Self::Error> {
let shared_data: Rc<dyn pdk_core::classy::SharedData> = context.extract()?;
Ok(Self { shared_data })
}
}
impl LocalStorage for SharedData {
fn set(&self, key: &str, value: &[u8], mode: StoreMode) -> Result<(), LocalStorageError> {
let cas = self.store_mode_to_cas(key, mode)?;
self.shared_data
.shared_data_set(key, value, cas)
.map_err(|e| match e {
Status::CasMismatch => LocalStorageError::CasMismatch,
v => LocalStorageError::ProxyWasm(v),
})
}
fn get(&self, key: &str) -> Result<Option<(Vec<u8>, u32)>, LocalStorageError> {
match self.shared_data.shared_data_get(key) {
(Some(data), Some(cas)) => Ok(Some((data, cas))),
_ => Ok(None),
}
}
fn delete(&self, key: &str) -> Result<(), LocalStorageError> {
let cas = self.store_mode_to_cas(key, StoreMode::Always)?;
let _ = self.shared_data.shared_data_remove(key, cas)?;
Ok(())
}
fn keys(&self) -> Vec<String> {
self.shared_data.shared_data_keys()
}
}