1use crate::{
5 dynamic_value::DynamicValue,
6 js::{from_js_value, to_js_value},
7 JsBlockStorage,
8};
9use cid::Cid;
10use co_primitives::{CoMap, CoMapTransaction};
11use wasm_bindgen::prelude::*;
12use web_sys::js_sys::Uint8Array;
13
14#[wasm_bindgen(js_name = "CoMap")]
15pub struct JsCoMap {
16 root: Option<Cid>,
17}
18#[wasm_bindgen(js_class = "CoMap")]
19impl JsCoMap {
20 #[wasm_bindgen(constructor)]
21 pub fn new(cid: JsValue) -> Result<Self, JsValue> {
22 let root = if cid.is_null_or_undefined() { None } else { Some(from_js_value(cid)?) };
23 Ok(JsCoMap { root })
24 }
25
26 #[allow(clippy::should_implement_trait)]
27 pub fn default() -> Self {
28 JsCoMap { root: None }
29 }
30
31 pub async fn open(&self, storage: &JsBlockStorage) -> Result<JsCoMapTransaction, JsValue> {
32 let transaction = self
33 .map()
34 .open(storage)
35 .await
36 .map_err(|err| format!("Open failed: {:?}", err))?;
37 Ok(JsCoMapTransaction(transaction))
38 }
39
40 pub async fn commit(&mut self, transaction: JsCoMapTransaction) -> Result<(), JsValue> {
41 let mut map = self.map();
42 map.commit(transaction.0)
43 .await
44 .map_err(|err| format!("Commit transaction failed: {:?}", err))?;
45 self.root = Into::<Option<Cid>>::into(&map);
46 Ok(())
47 }
48
49 pub fn is_empty(&self) -> bool {
50 self.map().is_empty()
51 }
52
53 pub async fn get(&self, storage: &JsBlockStorage, key: JsValue) -> Result<Option<JsValue>, JsValue> {
54 self.open(storage).await?.get(key).await
55 }
56
57 pub async fn contains_key(&self, storage: &JsBlockStorage, key: JsValue) -> Result<bool, JsValue> {
58 self.open(storage).await?.contains_key(key).await
59 }
60
61 pub async fn insert(&mut self, storage: &JsBlockStorage, key: JsValue, value: JsValue) -> Result<(), JsValue> {
62 let mut transaction = self.open(storage).await?;
63 transaction.insert(key, value).await?;
64 self.commit(transaction).await
65 }
66
67 pub fn stream(&self, storage: &JsBlockStorage) -> web_sys::ReadableStream {
68 let map = self.map();
69 let storage = storage.clone();
70 let stream = async_stream::try_stream! {
71 let tree = map.open(&storage).await
72 .map_err(|err| format!("open failed: {:?}", err))?;
73 let stream = tree.stream();
74 for await item in stream {
75 let value = item
76 .map_err(|err| format!("read failed: {:?}", err))?;
77 let js_value = to_js_value(&value)?;
78 yield js_value;
79 }
80 };
81 wasm_streams::ReadableStream::from_stream(stream).into_raw()
82 }
83
84 pub fn cid(&self) -> Result<Option<Uint8Array>, JsValue> {
85 self.root.as_ref().map(|cid| to_js_value(cid).map(Uint8Array::from)).transpose()
86 }
87}
88impl JsCoMap {
89 fn map(&self) -> CoMap<DynamicValue, DynamicValue> {
90 CoMap::from(self.root)
91 }
92}
93impl From<Option<Cid>> for JsCoMap {
94 fn from(value: Option<Cid>) -> Self {
95 Self { root: value }
96 }
97}
98
99#[wasm_bindgen(js_name = "CoMapTransaction")]
100pub struct JsCoMapTransaction(CoMapTransaction<JsBlockStorage, DynamicValue, DynamicValue>);
101
102#[wasm_bindgen(js_class = "CoMapTransaction")]
103impl JsCoMapTransaction {
104 pub async fn store(&mut self) -> Result<JsCoMap, JsValue> {
105 let co_map = self.0.store().await.map_err(|err| format!("Store failed: {:?}", err))?;
106 Ok(Into::<Option<Cid>>::into(&co_map).into())
107 }
108 pub async fn get(&self, key: JsValue) -> Result<Option<JsValue>, JsValue> {
109 let key: DynamicValue = from_js_value(key)?;
110 let result = self.0.get(&key).await.map_err(|err| format!("Get failed: {:?}", err))?;
111 result.as_ref().map(to_js_value).transpose()
112 }
113 pub async fn contains_key(&self, key: JsValue) -> Result<bool, JsValue> {
114 let key: DynamicValue = from_js_value(key)?;
115 self.0
116 .contains_key(&key)
117 .await
118 .map_err(|err| format!("Contains key failed: {:?}", err).into())
119 }
120 pub async fn insert(&mut self, key: JsValue, value: JsValue) -> Result<(), JsValue> {
121 let key: DynamicValue = from_js_value(key)?;
122 let value: DynamicValue = from_js_value(value)?;
123 self.0
124 .insert(key, value)
125 .await
126 .map_err(|err| format!("insert failed: {:?}", err))?;
127 Ok(())
128 }
129 pub fn stream(&self) -> web_sys::ReadableStream {
130 let transaction = self.0.clone();
131 let stream = async_stream::try_stream! {
132 let stream = transaction.stream();
133 for await item in stream {
134 let value = item
135 .map_err(|err| format!("read failed: {:?}", err))?;
136 let js_value = to_js_value(&value)?;
137 yield js_value;
138 }
139 };
140 wasm_streams::ReadableStream::from_stream(stream).into_raw()
141 }
142}