1use crate::actor::JsLocalTaskSpawner;
5use anyhow::anyhow;
6use async_trait::async_trait;
7use cid::Cid;
8use co_actor::{ActorError, ActorHandle, LocalActor, Response};
9use co_primitives::{Block, BlockStorage, DefaultParams, StorageError, StoreParams};
10use wasm_bindgen::prelude::*;
11use wasm_bindgen_futures::JsFuture;
12use web_sys::js_sys::{Function, Promise, Uint8Array};
13
14#[wasm_bindgen]
15extern "C" {
16 #[wasm_bindgen(typescript_type = "(cid: Uint8Array) => Promise<Uint8Array | undefined>")]
17 pub type JsBlockStorageGet;
18
19 #[wasm_bindgen(typescript_type = "(cid: Uint8Array, data: Uint8Array) => void")]
20 pub type JsBlockStorageSet;
21}
22
23#[wasm_bindgen(js_name = "BlockStorage")]
24#[derive(Debug, Clone)]
25pub struct JsBlockStorage {
26 handle: ActorHandle<JsBlockStorageMessage>,
27}
28#[wasm_bindgen(js_class = "BlockStorage")]
29impl JsBlockStorage {
30 #[wasm_bindgen(constructor)]
31 pub fn new(get: JsBlockStorageGet, set: JsBlockStorageSet) -> Result<Self, JsValue> {
32 Ok(Self {
33 handle: LocalActor::spawn_with(
34 JsLocalTaskSpawner::default(),
35 Default::default(),
36 JsBlockStorageActor { get: get.dyn_into()?, set: set.dyn_into()? },
37 (),
38 )
39 .map_err(|err| format!("block storage failed: {:?}", err))?
40 .handle(),
41 })
42 }
43}
44#[async_trait]
45impl BlockStorage for JsBlockStorage {
46 async fn get(&self, cid: &Cid) -> Result<Block, StorageError> {
47 Ok(self
48 .handle
49 .request(|response| JsBlockStorageMessage::Get(*cid, response))
50 .await
51 .map_err(|err| StorageError::Internal(err.into()))??)
52 }
53
54 async fn set(&self, block: Block) -> Result<Cid, StorageError> {
55 Ok(self
56 .handle
57 .request(|response| JsBlockStorageMessage::Set(block, response))
58 .await
59 .map_err(|err| StorageError::Internal(err.into()))??)
60 }
61
62 async fn remove(&self, _cid: &Cid) -> Result<(), StorageError> {
63 Err(StorageError::Internal(anyhow!("Unsupported")))
64 }
65
66 fn max_block_size(&self) -> usize {
67 DefaultParams::MAX_BLOCK_SIZE
68 }
69}
70
71enum JsBlockStorageMessage {
72 Get(Cid, Response<Result<Block, StorageError>>),
73 Set(Block, Response<Result<Cid, StorageError>>),
74}
75
76#[derive(Debug)]
77struct JsBlockStorageActor {
78 get: Function,
82
83 set: Function,
87}
88impl JsBlockStorageActor {
89 async fn get(&self, cid: &Cid) -> Result<Block, StorageError> {
90 let this = JsValue::null();
91 let js_cid =
92 serde_wasm_bindgen::to_value(cid).map_err(|err| anyhow!("Convert `Cid` to `JsValue` failed: {}", err))?;
93 let call: JsValue = self.get.call1(&this, &js_cid).map_err(|err| anyhow!("Call error: {:?}", err))?;
94 let promise: Promise = call
95 .dyn_into::<Promise>()
96 .map_err(|value| anyhow!("Result is not a `Promise`: {:?}", value))?;
97 let future = JsFuture::from(promise);
98 let result = future.await.map_err(|err| anyhow!("Get block failed: {:?}", err))?;
99 if result.is_null_or_undefined() {
100 return Err(StorageError::NotFound(*cid, anyhow!("Getter returned undefined")));
101 }
102 let bytes = result
103 .clone()
104 .dyn_into::<Uint8Array>()
105 .map_err(|err| anyhow!("Failed to convert result to Uint8Array: {:?}", err))?;
106 Ok(Block::new(*cid, bytes.to_vec()).map_err(|err| anyhow!("Data and Cid are not compatible: {:?}", err))?)
107 }
108
109 async fn set(&self, block: Block) -> Result<Cid, StorageError> {
110 let this = JsValue::null();
111 let (cid, data) = block.into_inner();
112 let js_cid =
113 serde_wasm_bindgen::to_value(&cid).map_err(|err| anyhow!("Convert `Cid` to `JsValue` failed: {}", err))?;
114 let js_data = Uint8Array::from(data.as_ref());
115 let call = self
116 .set
117 .call2(&this, &js_cid, &js_data)
118 .map_err(|err| anyhow!("Call error: {:?}", err))?;
119 let promise: Promise = call
120 .dyn_into::<Promise>()
121 .map_err(|value| anyhow!("Result is not a `Promise`: {:?}", value))?;
122 let future = JsFuture::from(promise);
123 let result = future.await.map_err(|err| anyhow!("Set block failed: {:?}", err))?;
124 let cid_bytes = result
125 .dyn_into::<Uint8Array>()
126 .map_err(|err| anyhow!("Convert storage set result JsValue to Cid failed: {:?}", err.as_string()))?
127 .to_vec();
128 let storage_set_cid = Cid::try_from(cid_bytes).map_err(|err| anyhow!("Get Cid from bytes failed: {}", err))?;
129 Ok(storage_set_cid)
130 }
131}
132impl LocalActor for JsBlockStorageActor {
133 type Message = JsBlockStorageMessage;
134 type State = ();
135 type Initialize = ();
136
137 async fn handle(
138 &self,
139 _handle: &ActorHandle<Self::Message>,
140 message: Self::Message,
141 _state: &mut Self::State,
142 ) -> Result<(), ActorError> {
143 match message {
144 JsBlockStorageMessage::Get(cid, response) => {
145 response.respond(self.get(&cid).await);
146 },
147 JsBlockStorageMessage::Set(block, response) => {
148 response.respond(self.set(block).await);
149 },
150 }
151 Ok(())
152 }
153
154 async fn initialize(
155 &self,
156 _handle: &ActorHandle<Self::Message>,
157 _tags: &co_primitives::Tags,
158 _initialize: Self::Initialize,
159 ) -> Result<Self::State, co_actor::ActorError> {
160 Ok(())
161 }
162}