1pub mod command;
7pub mod error;
8pub mod value;
9
10use serde::{Deserialize, Serialize};
11
12use crux_core::capability::{CapabilityContext, Operation};
13
14use error::KeyValueError;
15use value::Value;
16
17#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
19pub enum KeyValueOperation {
20 Get { key: String },
22 Set {
24 key: String,
25 #[serde(with = "serde_bytes")]
26 value: Vec<u8>,
27 },
28 Delete { key: String },
30 Exists { key: String },
32 ListKeys {
34 prefix: String,
36 cursor: u64,
44 },
45}
46
47impl std::fmt::Debug for KeyValueOperation {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 KeyValueOperation::Get { key } => f.debug_struct("Get").field("key", key).finish(),
51 KeyValueOperation::Set { key, value } => {
52 let body_repr = if let Ok(s) = std::str::from_utf8(value) {
53 if s.len() < 50 {
54 format!("\"{s}\"")
55 } else {
56 format!("\"{}\"...", s.chars().take(50).collect::<String>())
57 }
58 } else {
59 format!("<binary data - {} bytes>", value.len())
60 };
61 f.debug_struct("Set")
62 .field("key", key)
63 .field("value", &format_args!("{}", body_repr))
64 .finish()
65 }
66 KeyValueOperation::Delete { key } => {
67 f.debug_struct("Delete").field("key", key).finish()
68 }
69 KeyValueOperation::Exists { key } => {
70 f.debug_struct("Exists").field("key", key).finish()
71 }
72 KeyValueOperation::ListKeys { prefix, cursor } => f
73 .debug_struct("ListKeys")
74 .field("prefix", prefix)
75 .field("cursor", cursor)
76 .finish(),
77 }
78 }
79}
80
81#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
86pub enum KeyValueResult {
87 Ok { response: KeyValueResponse },
88 Err { error: KeyValueError },
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
92pub enum KeyValueResponse {
93 Get { value: Value },
96 Set { previous: Value },
99 Delete { previous: Value },
102 Exists { is_present: bool },
105 ListKeys {
111 keys: Vec<String>,
112 next_cursor: u64,
116 },
117}
118
119impl Operation for KeyValueOperation {
120 type Output = KeyValueResult;
121
122 #[cfg(feature = "typegen")]
123 fn register_types(generator: &mut crux_core::typegen::TypeGen) -> crux_core::typegen::Result {
124 generator.register_type::<KeyValueResponse>()?;
125 generator.register_type::<KeyValueError>()?;
126 generator.register_type::<Value>()?;
127 generator.register_type::<Self>()?;
128 generator.register_type::<Self::Output>()?;
129 Ok(())
130 }
131}
132
133pub struct KeyValue<Ev> {
134 context: CapabilityContext<KeyValueOperation, Ev>,
135}
136
137impl<Ev> crux_core::Capability<Ev> for KeyValue<Ev> {
138 type Operation = KeyValueOperation;
139
140 type MappedSelf<MappedEv> = KeyValue<MappedEv>;
141
142 fn map_event<F, NewEv>(&self, f: F) -> Self::MappedSelf<NewEv>
143 where
144 F: Fn(NewEv) -> Ev + Send + Sync + 'static,
145 Ev: 'static,
146 NewEv: 'static + Send,
147 {
148 KeyValue::new(self.context.map_event(f))
149 }
150}
151
152impl<Ev> Clone for KeyValue<Ev> {
153 fn clone(&self) -> Self {
154 Self {
155 context: self.context.clone(),
156 }
157 }
158}
159
160impl<Ev> KeyValue<Ev>
161where
162 Ev: 'static,
163{
164 pub fn new(context: CapabilityContext<KeyValueOperation, Ev>) -> Self {
165 Self { context }
166 }
167
168 pub fn get<F>(&self, key: String, make_event: F)
171 where
172 F: FnOnce(Result<Option<Vec<u8>>, KeyValueError>) -> Ev + Send + Sync + 'static,
173 {
174 self.context.spawn({
175 let context = self.context.clone();
176 async move {
177 let response = get(&context, key).await;
178 context.update_app(make_event(response));
179 }
180 });
181 }
182
183 pub async fn get_async(&self, key: String) -> Result<Option<Vec<u8>>, KeyValueError> {
188 get(&self.context, key).await
189 }
190
191 pub fn set<F>(&self, key: String, value: Vec<u8>, make_event: F)
196 where
197 F: FnOnce(Result<Option<Vec<u8>>, KeyValueError>) -> Ev + Send + Sync + 'static,
198 {
199 self.context.spawn({
200 let context = self.context.clone();
201 async move {
202 let response = set(&context, key, value).await;
203 context.update_app(make_event(response))
204 }
205 });
206 }
207
208 pub async fn set_async(
213 &self,
214 key: String,
215 value: Vec<u8>,
216 ) -> Result<Option<Vec<u8>>, KeyValueError> {
217 set(&self.context, key, value).await
218 }
219
220 pub fn delete<F>(&self, key: String, make_event: F)
223 where
224 F: FnOnce(Result<Option<Vec<u8>>, KeyValueError>) -> Ev + Send + Sync + 'static,
225 {
226 self.context.spawn({
227 let context = self.context.clone();
228 async move {
229 let response = delete(&context, key).await;
230 context.update_app(make_event(response))
231 }
232 });
233 }
234
235 pub async fn delete_async(&self, key: String) -> Result<Option<Vec<u8>>, KeyValueError> {
240 delete(&self.context, key).await
241 }
242
243 pub fn exists<F>(&self, key: String, make_event: F)
246 where
247 F: FnOnce(Result<bool, KeyValueError>) -> Ev + Send + Sync + 'static,
248 {
249 self.context.spawn({
250 let context = self.context.clone();
251 async move {
252 let response = exists(&context, key).await;
253 context.update_app(make_event(response))
254 }
255 });
256 }
257
258 pub async fn exists_async(&self, key: String) -> Result<bool, KeyValueError> {
263 exists(&self.context, key).await
264 }
265
266 pub fn list_keys<F>(&self, prefix: String, cursor: u64, make_event: F)
278 where
279 F: FnOnce(Result<(Vec<String>, u64), KeyValueError>) -> Ev + Send + Sync + 'static,
280 {
281 self.context.spawn({
282 let context = self.context.clone();
283 async move {
284 let response = list_keys(&context, prefix, cursor).await;
285 context.update_app(make_event(response))
286 }
287 });
288 }
289
290 pub async fn list_keys_async(
301 &self,
302 prefix: String,
303 cursor: u64,
304 ) -> Result<(Vec<String>, u64), KeyValueError> {
305 list_keys(&self.context, prefix, cursor).await
306 }
307}
308
309async fn get<Ev: 'static>(
310 context: &CapabilityContext<KeyValueOperation, Ev>,
311 key: String,
312) -> Result<Option<Vec<u8>>, KeyValueError> {
313 context
314 .request_from_shell(KeyValueOperation::Get { key })
315 .await
316 .unwrap_get()
317}
318
319async fn set<Ev: 'static>(
320 context: &CapabilityContext<KeyValueOperation, Ev>,
321 key: String,
322 value: Vec<u8>,
323) -> Result<Option<Vec<u8>>, KeyValueError> {
324 context
325 .request_from_shell(KeyValueOperation::Set { key, value })
326 .await
327 .unwrap_set()
328}
329
330async fn delete<Ev: 'static>(
331 context: &CapabilityContext<KeyValueOperation, Ev>,
332 key: String,
333) -> Result<Option<Vec<u8>>, KeyValueError> {
334 context
335 .request_from_shell(KeyValueOperation::Delete { key })
336 .await
337 .unwrap_delete()
338}
339
340async fn exists<Ev: 'static>(
341 context: &CapabilityContext<KeyValueOperation, Ev>,
342 key: String,
343) -> Result<bool, KeyValueError> {
344 context
345 .request_from_shell(KeyValueOperation::Exists { key })
346 .await
347 .unwrap_exists()
348}
349
350async fn list_keys<Ev: 'static>(
351 context: &CapabilityContext<KeyValueOperation, Ev>,
352 prefix: String,
353 cursor: u64,
354) -> Result<(Vec<String>, u64), KeyValueError> {
355 context
356 .request_from_shell(KeyValueOperation::ListKeys { prefix, cursor })
357 .await
358 .unwrap_list_keys()
359}
360
361impl KeyValueResult {
362 fn unwrap_get(self) -> Result<Option<Vec<u8>>, KeyValueError> {
363 match self {
364 KeyValueResult::Ok { response } => match response {
365 KeyValueResponse::Get { value } => Ok(value.into()),
366 _ => {
367 panic!("attempt to convert KeyValueResponse other than Get to Option<Vec<u8>>")
368 }
369 },
370 KeyValueResult::Err { error } => Err(error.clone()),
371 }
372 }
373
374 fn unwrap_set(self) -> Result<Option<Vec<u8>>, KeyValueError> {
375 match self {
376 KeyValueResult::Ok { response } => match response {
377 KeyValueResponse::Set { previous } => Ok(previous.into()),
378 _ => {
379 panic!("attempt to convert KeyValueResponse other than Set to Option<Vec<u8>>")
380 }
381 },
382 KeyValueResult::Err { error } => Err(error.clone()),
383 }
384 }
385
386 fn unwrap_delete(self) -> Result<Option<Vec<u8>>, KeyValueError> {
387 match self {
388 KeyValueResult::Ok { response } => match response {
389 KeyValueResponse::Delete { previous } => Ok(previous.into()),
390 _ => panic!(
391 "attempt to convert KeyValueResponse other than Delete to Option<Vec<u8>>"
392 ),
393 },
394 KeyValueResult::Err { error } => Err(error.clone()),
395 }
396 }
397
398 fn unwrap_exists(self) -> Result<bool, KeyValueError> {
399 match self {
400 KeyValueResult::Ok { response } => match response {
401 KeyValueResponse::Exists { is_present } => Ok(is_present),
402 _ => panic!("attempt to convert KeyValueResponse other than Exists to bool"),
403 },
404 KeyValueResult::Err { error } => Err(error.clone()),
405 }
406 }
407
408 fn unwrap_list_keys(self) -> Result<(Vec<String>, u64), KeyValueError> {
409 match self {
410 KeyValueResult::Ok { response } => match response {
411 KeyValueResponse::ListKeys {
412 keys,
413 next_cursor: cursor,
414 } => Ok((keys, cursor)),
415 _ => panic!(
416 "attempt to convert KeyValueResponse other than ListKeys to (Vec<String>, u64)"
417 ),
418 },
419 KeyValueResult::Err { error } => Err(error.clone()),
420 }
421 }
422}
423
424#[cfg(test)]
425mod tests;