use std::sync::Arc;
use crate::{platform::PlatformStorage, wasm::SphereFile};
use js_sys::{Array, Function};
use noosphere_core::context::{
HasMutableSphereContext, SphereContentRead, SphereContentWrite, SphereContext, SphereCursor,
SphereWalker,
};
use tokio::sync::Mutex;
use tokio_stream::StreamExt;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct SphereFs {
#[wasm_bindgen(skip)]
pub inner: SphereCursor<Arc<Mutex<SphereContext<PlatformStorage>>>, PlatformStorage>,
}
#[wasm_bindgen]
impl SphereFs {
#[wasm_bindgen]
pub async fn read(&self, slug: String) -> Result<Option<SphereFile>, String> {
let file = self
.inner
.read(&slug)
.await
.map_err(|error| format!("{:?}", error))?;
Ok(file.map(|file| SphereFile {
inner: file.boxed(),
cursor: self.inner.clone(),
}))
}
#[wasm_bindgen(js_name = "writeString")]
pub async fn write_string(
&mut self,
slug: String,
content_type: String,
value: String,
additional_headers: Option<Array>,
) -> Result<String, String> {
self.write(
slug,
content_type,
value.as_bytes().to_vec(),
additional_headers,
)
.await
}
#[wasm_bindgen]
pub async fn write(
&mut self,
slug: String,
content_type: String,
value: Vec<u8>,
additional_headers: Option<Array>,
) -> Result<String, String> {
let additional_headers = self.convert_headers_representation(additional_headers);
let version = self
.inner
.write(&slug, &content_type, value.as_slice(), additional_headers)
.await
.map_err(|error| format!("{:?}", error))?;
Ok(version.to_string())
}
#[wasm_bindgen]
pub async fn save(&mut self, additional_headers: Option<Array>) -> Result<String, String> {
let additional_headers = self.convert_headers_representation(additional_headers);
self.inner
.save(additional_headers)
.await
.map(|cid| cid.to_string())
.map_err(|error| format!("{:?}", error))
}
#[wasm_bindgen]
pub async fn stream(&self, callback: Function) -> Result<(), String> {
let stream = SphereWalker::from(&self.inner).into_content_stream();
let this = JsValue::null();
tokio::pin!(stream);
while let Some(Ok((slug, file))) = stream.next().await {
let file = file.boxed();
let file = SphereFile {
inner: file,
cursor: self.inner.clone(),
};
let slug = JsValue::from(slug);
let file = JsValue::from(file);
callback
.call2(&this, &slug, &file)
.map_err(|error| format!("{:?}", error))?;
}
Ok(())
}
fn convert_headers_representation(
&self,
additional_headers: Option<Array>,
) -> Option<Vec<(String, String)>> {
if let Some(headers) = additional_headers {
let mut additional_headers = Vec::new();
for value in headers.iter() {
if Array::is_array(&value) {
let entry = Array::from(&value);
match (entry.get(0).as_string(), entry.get(1).as_string()) {
(Some(key), Some(value)) => additional_headers.push((key, value)),
_ => continue,
}
}
}
Some(additional_headers)
} else {
None
}
}
}