pub mod error;
use crux_core::capability::{CapabilityContext, Operation};
use crux_core::macros::Capability;
use error::KeyValueError;
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum KeyValueOperation {
Get { key: String },
Set { key: String, value: Vec<u8> },
Delete { key: String },
Exists { key: String },
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum KeyValueResult {
Ok { response: KeyValueResponse },
Err { error: KeyValueError },
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum KeyValueResponse {
Get { value: Vec<u8> },
Set { previous: Vec<u8> },
Delete { previous: Vec<u8> },
Exists { is_present: bool },
}
impl Operation for KeyValueOperation {
type Output = KeyValueResult;
}
#[derive(Capability)]
pub struct KeyValue<Ev> {
context: CapabilityContext<KeyValueOperation, Ev>,
}
impl<Ev> Clone for KeyValue<Ev> {
fn clone(&self) -> Self {
Self {
context: self.context.clone(),
}
}
}
impl<Ev> KeyValue<Ev>
where
Ev: 'static,
{
pub fn new(context: CapabilityContext<KeyValueOperation, Ev>) -> Self {
Self { context }
}
pub fn get<F>(&self, key: String, make_event: F)
where
F: FnOnce(KeyValueResult) -> Ev + Send + Sync + 'static,
{
self.context.spawn({
let context = self.context.clone();
let this = self.clone();
async move {
let response = this.get_async(key).await;
context.update_app(make_event(response));
}
});
}
pub async fn get_async(&self, key: String) -> KeyValueResult {
let response = self
.context
.request_from_shell(KeyValueOperation::Get { key })
.await;
if let KeyValueResult::Ok { response } = &response {
let KeyValueResponse::Get { .. } = response else {
panic!("unexpected response: {:?}", response)
};
}
response
}
pub fn set<F>(&self, key: String, value: Vec<u8>, make_event: F)
where
F: FnOnce(KeyValueResult) -> Ev + Send + Sync + 'static,
{
self.context.spawn({
let context = self.context.clone();
let this = self.clone();
async move {
let response = this.set_async(key, value).await;
context.update_app(make_event(response))
}
});
}
pub async fn set_async(&self, key: String, value: Vec<u8>) -> KeyValueResult {
let response = self
.context
.request_from_shell(KeyValueOperation::Set { key, value })
.await;
if let KeyValueResult::Ok { response } = &response {
let KeyValueResponse::Set { .. } = response else {
panic!("unexpected response: {:?}", response)
};
}
response
}
pub fn delete<F>(&self, key: String, make_event: F)
where
F: Fn(KeyValueResult) -> Ev + Send + Sync + 'static,
{
let context = self.context.clone();
let this = self.clone();
self.context.spawn(async move {
let response = this.delete_async(key).await;
context.update_app(make_event(response))
});
}
pub async fn delete_async(&self, key: String) -> KeyValueResult {
let response = self
.context
.request_from_shell(KeyValueOperation::Delete { key })
.await;
if let KeyValueResult::Ok { response } = &response {
let KeyValueResponse::Delete { .. } = response else {
panic!("unexpected response: {:?}", response)
};
}
response
}
pub fn exists<F>(&self, key: String, make_event: F)
where
F: Fn(KeyValueResult) -> Ev + Send + Sync + 'static,
{
let context = self.context.clone();
let this = self.clone();
self.context.spawn(async move {
let response = this.exists_async(key).await;
context.update_app(make_event(response))
});
}
pub async fn exists_async(&self, key: String) -> KeyValueResult {
let response = self
.context
.request_from_shell(KeyValueOperation::Exists { key })
.await;
if let KeyValueResult::Ok { response } = &response {
let KeyValueResponse::Exists { .. } = response else {
panic!("unexpected response: {:?}", response)
};
}
response
}
}
#[cfg(test)]
mod tests;