Skip to content

Commit e0a401e

Browse files
maxbrunsfeldas-cii
andcommitted
Add basic support for sqlite-backed durable objects
Fixes #645 Co-authored-by: Antonio Scandurra <[email protected]>
1 parent a16090a commit e0a401e

File tree

4 files changed

+151
-4
lines changed

4 files changed

+151
-4
lines changed

worker-sys/src/types/durable_object.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ use wasm_bindgen::prelude::*;
22

33
mod id;
44
mod namespace;
5+
mod sql_storage;
56
mod state;
67
mod storage;
78
mod transaction;
89

910
pub use id::*;
1011
pub use namespace::*;
12+
pub use sql_storage::*;
1113
pub use state::*;
1214
pub use storage::*;
1315
pub use transaction::*;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use js_sys::JsString;
2+
use wasm_bindgen::prelude::*;
3+
4+
#[wasm_bindgen]
5+
extern "C" {
6+
#[wasm_bindgen(extends=js_sys::Object)]
7+
pub type DurableObjectSqlStorage;
8+
9+
#[wasm_bindgen(method, catch, js_name=exec, variadic)]
10+
pub fn exec(
11+
this: &DurableObjectSqlStorage,
12+
query: JsString,
13+
args: Vec<JsValue>,
14+
) -> Result<DurableObjectSqlStorageCursor, JsValue>;
15+
}
16+
17+
#[wasm_bindgen]
18+
extern "C" {
19+
#[wasm_bindgen(extends=js_sys::Object)]
20+
pub type DurableObjectSqlStorageCursor;
21+
22+
#[wasm_bindgen(method, catch, js_name=one)]
23+
pub fn one(this: &DurableObjectSqlStorageCursor) -> Result<JsValue, JsValue>;
24+
}

worker-sys/src/types/durable_object/storage.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use wasm_bindgen::prelude::*;
22

33
use crate::types::DurableObjectTransaction;
44

5+
use super::DurableObjectSqlStorage;
6+
57
#[wasm_bindgen]
68
extern "C" {
79
#[wasm_bindgen(extends=js_sys::Object)]
@@ -74,4 +76,7 @@ extern "C" {
7476
this: &DurableObjectStorage,
7577
options: js_sys::Object,
7678
) -> Result<js_sys::Promise, JsValue>;
79+
80+
#[wasm_bindgen(method, catch, js_name=sql, getter)]
81+
pub fn sql(this: &DurableObjectStorage) -> Result<DurableObjectSqlStorage, JsValue>;
7782
}

worker/src/durable.rs

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! [Learn more](https://developers.cloudflare.com/workers/learning/using-durable-objects) about
1111
//! using Durable Objects.
1212
13-
use std::{fmt::Display, ops::Deref, time::Duration};
13+
use std::{convert::Into, fmt::Display, marker::PhantomData, ops::Deref, time::Duration};
1414

1515
use crate::{
1616
date::Date,
@@ -24,12 +24,13 @@ use crate::{
2424
use async_trait::async_trait;
2525
use chrono::{DateTime, Utc};
2626
use futures_util::Future;
27-
use js_sys::{Map, Number, Object};
27+
use js_sys::{IntoIter, Map, Number, Object};
2828
use serde::{de::DeserializeOwned, Serialize};
2929
use wasm_bindgen::{prelude::*, JsCast};
3030
use worker_sys::{
3131
DurableObject as EdgeDurableObject, DurableObjectId,
32-
DurableObjectNamespace as EdgeObjectNamespace, DurableObjectState, DurableObjectStorage,
32+
DurableObjectNamespace as EdgeObjectNamespace, DurableObjectSqlStorage,
33+
DurableObjectSqlStorageCursor, DurableObjectState, DurableObjectStorage,
3334
DurableObjectTransaction,
3435
};
3536
// use wasm_bindgen_futures::future_to_promise;
@@ -501,6 +502,121 @@ impl Storage {
501502
.map_err(Error::from)
502503
.map(|_| ())
503504
}
505+
506+
pub fn sql(&self) -> Result<SqlStorage> {
507+
let sql = self.inner.sql()?;
508+
Ok(SqlStorage { inner: sql })
509+
}
510+
}
511+
512+
pub enum SqlStorageValue {
513+
Null,
514+
Boolean(bool),
515+
Integer(i32),
516+
Float(f64),
517+
String(String),
518+
Blob(Vec<u8>),
519+
}
520+
521+
impl<T: Into<SqlStorageValue>> From<Option<T>> for SqlStorageValue {
522+
fn from(value: Option<T>) -> Self {
523+
match value {
524+
Some(v) => v.into(),
525+
None => SqlStorageValue::Null,
526+
}
527+
}
528+
}
529+
530+
impl From<bool> for SqlStorageValue {
531+
fn from(value: bool) -> Self {
532+
Self::Boolean(value)
533+
}
534+
}
535+
536+
impl From<f64> for SqlStorageValue {
537+
fn from(value: f64) -> Self {
538+
Self::Float(value)
539+
}
540+
}
541+
542+
impl From<String> for SqlStorageValue {
543+
fn from(value: String) -> Self {
544+
Self::String(value)
545+
}
546+
}
547+
548+
impl From<Vec<u8>> for SqlStorageValue {
549+
fn from(value: Vec<u8>) -> Self {
550+
Self::Blob(value)
551+
}
552+
}
553+
554+
impl From<i32> for SqlStorageValue {
555+
fn from(value: i32) -> Self {
556+
Self::Integer(value)
557+
}
558+
}
559+
560+
impl Into<JsValue> for SqlStorageValue {
561+
fn into(self) -> JsValue {
562+
match self {
563+
SqlStorageValue::Null => JsValue::NULL,
564+
SqlStorageValue::Boolean(b) => b.into(),
565+
SqlStorageValue::Integer(i) => i.into(),
566+
SqlStorageValue::Float(f) => f.into(),
567+
SqlStorageValue::String(s) => s.into(),
568+
SqlStorageValue::Blob(b) => b.into(),
569+
}
570+
}
571+
}
572+
573+
pub struct SqlStorage {
574+
inner: DurableObjectSqlStorage,
575+
}
576+
577+
impl SqlStorage {
578+
pub fn exec<T: DeserializeOwned>(
579+
&self,
580+
query: &str,
581+
params: Vec<SqlStorageValue>,
582+
) -> Result<SqlStorageCursor<T>> {
583+
let cursor = self
584+
.inner
585+
.exec(query.into(), params.into_iter().map(Into::into).collect())?;
586+
let iter = js_sys::try_iter(&cursor)?;
587+
Ok(SqlStorageCursor {
588+
inner: cursor,
589+
iter,
590+
_row_type: PhantomData,
591+
})
592+
}
593+
}
594+
595+
pub struct SqlStorageCursor<T: DeserializeOwned> {
596+
inner: DurableObjectSqlStorageCursor,
597+
iter: Option<IntoIter>,
598+
_row_type: PhantomData<T>,
599+
}
600+
601+
impl<T: DeserializeOwned> SqlStorageCursor<T> {
602+
pub fn one(&self) -> Result<T> {
603+
self.inner
604+
.one()
605+
.and_then(|row| serde_wasm_bindgen::from_value(row).map_err(JsValue::from))
606+
.map_err(Error::from)
607+
}
608+
}
609+
610+
impl<T: DeserializeOwned> Iterator for SqlStorageCursor<T> {
611+
type Item = Result<T>;
612+
613+
fn next(&mut self) -> Option<Self::Item> {
614+
let row = self.iter.as_mut()?.next()?;
615+
Some(
616+
row.and_then(|row| serde_wasm_bindgen::from_value(row).map_err(JsValue::from))
617+
.map_err(Error::from),
618+
)
619+
}
504620
}
505621

506622
pub struct Transaction {
@@ -515,7 +631,7 @@ impl Transaction {
515631
if val.is_undefined() {
516632
Err(JsValue::from("No such value in storage."))
517633
} else {
518-
serde_wasm_bindgen::from_value(val).map_err(std::convert::Into::into)
634+
serde_wasm_bindgen::from_value(val).map_err(Into::into)
519635
}
520636
})
521637
.map_err(Error::from)

0 commit comments

Comments
 (0)