1#![deny(clippy::pedantic)]
2pub mod command;
8pub mod error;
9pub mod value;
10
11use serde::{Deserialize, Serialize};
12
13use crux_core::capability::{CapabilityContext, Operation};
14
15use error::KeyValueError;
16use value::Value;
17
18#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
20pub enum KeyValueOperation {
21 Get { key: String },
23 Set {
25 key: String,
26 #[serde(with = "serde_bytes")]
27 value: Vec<u8>,
28 },
29 Delete { key: String },
31 Exists { key: String },
33 ListKeys {
35 prefix: String,
37 cursor: u64,
45 },
46}
47
48impl std::fmt::Debug for KeyValueOperation {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 KeyValueOperation::Get { key } => f.debug_struct("Get").field("key", key).finish(),
52 KeyValueOperation::Set { key, value } => {
53 let body_repr = if let Ok(s) = std::str::from_utf8(value) {
54 if s.len() < 50 {
55 format!("\"{s}\"")
56 } else {
57 format!("\"{}\"...", s.chars().take(50).collect::<String>())
58 }
59 } else {
60 format!("<binary data - {} bytes>", value.len())
61 };
62 f.debug_struct("Set")
63 .field("key", key)
64 .field("value", &format_args!("{body_repr}"))
65 .finish()
66 }
67 KeyValueOperation::Delete { key } => {
68 f.debug_struct("Delete").field("key", key).finish()
69 }
70 KeyValueOperation::Exists { key } => {
71 f.debug_struct("Exists").field("key", key).finish()
72 }
73 KeyValueOperation::ListKeys { prefix, cursor } => f
74 .debug_struct("ListKeys")
75 .field("prefix", prefix)
76 .field("cursor", cursor)
77 .finish(),
78 }
79 }
80}
81
82#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
87pub enum KeyValueResult {
88 Ok { response: KeyValueResponse },
89 Err { error: KeyValueError },
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub enum KeyValueResponse {
94 Get { value: Value },
97 Set { previous: Value },
100 Delete { previous: Value },
103 Exists { is_present: bool },
106 ListKeys {
112 keys: Vec<String>,
113 next_cursor: u64,
117 },
118}
119
120impl Operation for KeyValueOperation {
121 type Output = KeyValueResult;
122
123 #[cfg(feature = "typegen")]
124 fn register_types(generator: &mut crux_core::typegen::TypeGen) -> crux_core::typegen::Result {
125 generator.register_type::<KeyValueResponse>()?;
126 generator.register_type::<KeyValueError>()?;
127 generator.register_type::<Value>()?;
128 generator.register_type::<Self>()?;
129 generator.register_type::<Self::Output>()?;
130 Ok(())
131 }
132}
133
134pub struct KeyValue<Ev> {
135 context: CapabilityContext<KeyValueOperation, Ev>,
136}
137
138impl<Ev> crux_core::Capability<Ev> for KeyValue<Ev> {
139 type Operation = KeyValueOperation;
140
141 type MappedSelf<MappedEv> = KeyValue<MappedEv>;
142
143 fn map_event<F, NewEv>(&self, f: F) -> Self::MappedSelf<NewEv>
144 where
145 F: Fn(NewEv) -> Ev + Send + Sync + 'static,
146 Ev: 'static,
147 NewEv: 'static + Send,
148 {
149 KeyValue::new(self.context.map_event(f))
150 }
151}
152
153impl<Ev> Clone for KeyValue<Ev> {
154 fn clone(&self) -> Self {
155 Self {
156 context: self.context.clone(),
157 }
158 }
159}
160
161impl<Ev> KeyValue<Ev>
162where
163 Ev: 'static,
164{
165 #[must_use]
166 pub fn new(context: CapabilityContext<KeyValueOperation, Ev>) -> Self {
167 Self { context }
168 }
169
170 pub fn get<F>(&self, key: String, make_event: F)
173 where
174 F: FnOnce(Result<Option<Vec<u8>>, KeyValueError>) -> Ev + Send + Sync + 'static,
175 {
176 self.context.spawn({
177 let context = self.context.clone();
178 async move {
179 let response = get(&context, key).await;
180 context.update_app(make_event(response));
181 }
182 });
183 }
184
185 pub async fn get_async(&self, key: String) -> Result<Option<Vec<u8>>, KeyValueError> {
192 get(&self.context, key).await
193 }
194
195 pub fn set<F>(&self, key: String, value: Vec<u8>, make_event: F)
200 where
201 F: FnOnce(Result<Option<Vec<u8>>, KeyValueError>) -> Ev + Send + Sync + 'static,
202 {
203 self.context.spawn({
204 let context = self.context.clone();
205 async move {
206 let response = set(&context, key, value).await;
207 context.update_app(make_event(response));
208 }
209 });
210 }
211
212 pub async fn set_async(
219 &self,
220 key: String,
221 value: Vec<u8>,
222 ) -> Result<Option<Vec<u8>>, KeyValueError> {
223 set(&self.context, key, value).await
224 }
225
226 pub fn delete<F>(&self, key: String, make_event: F)
229 where
230 F: FnOnce(Result<Option<Vec<u8>>, KeyValueError>) -> Ev + Send + Sync + 'static,
231 {
232 self.context.spawn({
233 let context = self.context.clone();
234 async move {
235 let response = delete(&context, key).await;
236 context.update_app(make_event(response));
237 }
238 });
239 }
240
241 pub async fn delete_async(&self, key: String) -> Result<Option<Vec<u8>>, KeyValueError> {
248 delete(&self.context, key).await
249 }
250
251 pub fn exists<F>(&self, key: String, make_event: F)
254 where
255 F: FnOnce(Result<bool, KeyValueError>) -> Ev + Send + Sync + 'static,
256 {
257 self.context.spawn({
258 let context = self.context.clone();
259 async move {
260 let response = exists(&context, key).await;
261 context.update_app(make_event(response));
262 }
263 });
264 }
265
266 pub async fn exists_async(&self, key: String) -> Result<bool, KeyValueError> {
274 exists(&self.context, key).await
275 }
276
277 pub fn list_keys<F>(&self, prefix: String, cursor: u64, make_event: F)
289 where
290 F: FnOnce(Result<(Vec<String>, u64), KeyValueError>) -> Ev + Send + Sync + 'static,
291 {
292 self.context.spawn({
293 let context = self.context.clone();
294 async move {
295 let response = list_keys(&context, prefix, cursor).await;
296 context.update_app(make_event(response));
297 }
298 });
299 }
300
301 pub async fn list_keys_async(
315 &self,
316 prefix: String,
317 cursor: u64,
318 ) -> Result<(Vec<String>, u64), KeyValueError> {
319 list_keys(&self.context, prefix, cursor).await
320 }
321}
322
323async fn get<Ev: 'static>(
324 context: &CapabilityContext<KeyValueOperation, Ev>,
325 key: String,
326) -> Result<Option<Vec<u8>>, KeyValueError> {
327 context
328 .request_from_shell(KeyValueOperation::Get { key })
329 .await
330 .unwrap_get()
331}
332
333async fn set<Ev: 'static>(
334 context: &CapabilityContext<KeyValueOperation, Ev>,
335 key: String,
336 value: Vec<u8>,
337) -> Result<Option<Vec<u8>>, KeyValueError> {
338 context
339 .request_from_shell(KeyValueOperation::Set { key, value })
340 .await
341 .unwrap_set()
342}
343
344async fn delete<Ev: 'static>(
345 context: &CapabilityContext<KeyValueOperation, Ev>,
346 key: String,
347) -> Result<Option<Vec<u8>>, KeyValueError> {
348 context
349 .request_from_shell(KeyValueOperation::Delete { key })
350 .await
351 .unwrap_delete()
352}
353
354async fn exists<Ev: 'static>(
355 context: &CapabilityContext<KeyValueOperation, Ev>,
356 key: String,
357) -> Result<bool, KeyValueError> {
358 context
359 .request_from_shell(KeyValueOperation::Exists { key })
360 .await
361 .unwrap_exists()
362}
363
364async fn list_keys<Ev: 'static>(
365 context: &CapabilityContext<KeyValueOperation, Ev>,
366 prefix: String,
367 cursor: u64,
368) -> Result<(Vec<String>, u64), KeyValueError> {
369 context
370 .request_from_shell(KeyValueOperation::ListKeys { prefix, cursor })
371 .await
372 .unwrap_list_keys()
373}
374
375impl KeyValueResult {
376 fn unwrap_get(self) -> Result<Option<Vec<u8>>, KeyValueError> {
377 match self {
378 KeyValueResult::Ok { response } => match response {
379 KeyValueResponse::Get { value } => Ok(value.into()),
380 _ => {
381 panic!("attempt to convert KeyValueResponse other than Get to Option<Vec<u8>>")
382 }
383 },
384 KeyValueResult::Err { error } => Err(error.clone()),
385 }
386 }
387
388 fn unwrap_set(self) -> Result<Option<Vec<u8>>, KeyValueError> {
389 match self {
390 KeyValueResult::Ok { response } => match response {
391 KeyValueResponse::Set { previous } => Ok(previous.into()),
392 _ => {
393 panic!("attempt to convert KeyValueResponse other than Set to Option<Vec<u8>>")
394 }
395 },
396 KeyValueResult::Err { error } => Err(error.clone()),
397 }
398 }
399
400 fn unwrap_delete(self) -> Result<Option<Vec<u8>>, KeyValueError> {
401 match self {
402 KeyValueResult::Ok { response } => match response {
403 KeyValueResponse::Delete { previous } => Ok(previous.into()),
404 _ => panic!(
405 "attempt to convert KeyValueResponse other than Delete to Option<Vec<u8>>"
406 ),
407 },
408 KeyValueResult::Err { error } => Err(error.clone()),
409 }
410 }
411
412 fn unwrap_exists(self) -> Result<bool, KeyValueError> {
413 match self {
414 KeyValueResult::Ok { response } => match response {
415 KeyValueResponse::Exists { is_present } => Ok(is_present),
416 _ => panic!("attempt to convert KeyValueResponse other than Exists to bool"),
417 },
418 KeyValueResult::Err { error } => Err(error.clone()),
419 }
420 }
421
422 fn unwrap_list_keys(self) -> Result<(Vec<String>, u64), KeyValueError> {
423 match self {
424 KeyValueResult::Ok { response } => match response {
425 KeyValueResponse::ListKeys {
426 keys,
427 next_cursor: cursor,
428 } => Ok((keys, cursor)),
429 _ => panic!(
430 "attempt to convert KeyValueResponse other than ListKeys to (Vec<String>, u64)"
431 ),
432 },
433 KeyValueResult::Err { error } => Err(error.clone()),
434 }
435 }
436}
437
438#[cfg(test)]
439mod tests;