Skip to content

Commit

Permalink
feat: Write wasm bindings for public file content r&w
Browse files Browse the repository at this point in the history
  • Loading branch information
matheus23 committed Dec 1, 2023
1 parent 26b1c33 commit 235633f
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 19 deletions.
11 changes: 5 additions & 6 deletions wnfs-wasm/src/fs/public/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl PublicDirectory {
.await
.map_err(error("Cannot add to store"))?;

let cid_u8array = Uint8Array::from(&cid.to_bytes()[..]);
let cid_u8array = Uint8Array::from(cid.to_bytes().as_ref());

Ok(value!(cid_u8array))
}))
Expand Down Expand Up @@ -118,9 +118,9 @@ impl PublicDirectory {
.await
.map_err(error("Cannot read from directory"))?;

let result = Uint8Array::from(&result.to_bytes()[..]);
let u8array = Uint8Array::from(result.as_ref());

Ok(value!(result))
Ok(value!(u8array))
}))
}

Expand Down Expand Up @@ -165,20 +165,19 @@ impl PublicDirectory {
pub fn write(
&self,
path_segments: &Array,
content_cid: Vec<u8>,
content: Vec<u8>,
time: &Date,
store: BlockStore,
) -> JsResult<Promise> {
let mut directory = Rc::clone(&self.0);
let store = ForeignBlockStore(store);

let cid = Cid::try_from(content_cid).map_err(error("Invalid CID"))?;
let time = DateTime::<Utc>::from(time);
let path_segments = utils::convert_path_segments(path_segments)?;

Ok(future_to_promise(async move {
(&mut directory)
.write(&path_segments, cid, time, &store)
.write(&path_segments, content, time, &store)
.await
.map_err(error("Cannot write to directory"))?;

Expand Down
61 changes: 50 additions & 11 deletions wnfs-wasm/src/fs/public/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
value,
};
use chrono::{DateTime, Utc};
use js_sys::{Error, Promise, Uint8Array};
use js_sys::{Error, Number, Promise, Uint8Array};
use libipld_core::cid::Cid;
use std::rc::Rc;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
Expand All @@ -32,11 +32,9 @@ pub struct PublicFile(pub(crate) Rc<WnfsPublicFile>);
impl PublicFile {
/// Creates a new file in a WNFS public file system.
#[wasm_bindgen(constructor)]
pub fn new(time: &js_sys::Date, cid: Vec<u8>) -> JsResult<PublicFile> {
pub fn new(time: &js_sys::Date) -> PublicFile {
let time = DateTime::<Utc>::from(time);
let cid = Cid::try_from(&cid[..]).map_err(error("Invalid CID"))?;

Ok(PublicFile(Rc::new(WnfsPublicFile::new(time, cid))))
Self(WnfsPublicFile::new_rc(time))
}

/// Gets a unique id for node.
Expand All @@ -48,11 +46,11 @@ impl PublicFile {
/// Stores a file in provided block store.
pub fn store(&self, store: BlockStore) -> JsResult<Promise> {
let file = Rc::clone(&self.0);
let mut store = ForeignBlockStore(store);
let store = ForeignBlockStore(store);

Ok(future_to_promise(async move {
let cid = file
.store(&mut store)
.store(&store)
.await
.map_err(|e| Error::new(&format!("Cannot add to store: {e}")))?;

Expand All @@ -66,6 +64,7 @@ impl PublicFile {
pub fn load(cid: Vec<u8>, store: BlockStore) -> JsResult<Promise> {
let store = ForeignBlockStore(store);
let cid = Cid::try_from(cid).map_err(|e| Error::new(&format!("Cannot parse cid: {e}")))?;

Ok(future_to_promise(async move {
let file: WnfsPublicFile = store
.get_deserializable(&cid)
Expand Down Expand Up @@ -96,10 +95,50 @@ impl PublicFile {
JsMetadata(self.0.get_metadata()).try_into()
}

/// Gets the content cid of the file.
#[wasm_bindgen(js_name = "contentCid")]
pub fn content_cid(&self) -> Vec<u8> {
self.0.get_content_cid().to_bytes()
/// Gets the content of the file at given offset & with an optional byte limit.
#[wasm_bindgen(js_name = "readAt")]
pub fn read_at(
&self,
byte_offset: Number,
limit: Option<Number>,
store: BlockStore,
) -> JsResult<Promise> {
let file = Rc::clone(&self.0);
let store = ForeignBlockStore(store);
let byte_offset = f64::from(byte_offset) as u64;
let limit = limit.map(|lim| f64::from(lim) as usize);

Ok(future_to_promise(async move {
let result = file
.read_at(byte_offset, limit, &store)
.await
.map_err(error("Cannot read file"))?;

let uint8array = Uint8Array::from(result.as_ref());

Ok(value!(uint8array))
}))
}

/// Sets the content of a file to a byte array.
#[wasm_bindgen(js_name = "setContent")]
pub fn set_content(
&self,
time: &js_sys::Date,
content: Vec<u8>,
store: BlockStore,
) -> JsResult<Promise> {
let mut file = Rc::clone(&self.0);
let store = ForeignBlockStore(store);
let time = DateTime::<Utc>::from(time);

Ok(future_to_promise(async move {
file.set_content(time, content, &store)
.await
.map_err(error("Cannot set file content"))?;

Ok(value!(PublicFile(file)))
}))
}

/// Converts this directory to a node.
Expand Down
50 changes: 48 additions & 2 deletions wnfs-wasm/tests/public.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,13 +288,59 @@ test.describe("PublicDirectory", () => {
const result = await page.evaluate(async () => {
const {
wnfs: { PublicFile },
mock: { sampleCID },
} = await window.setup();

const time = new Date();
return new PublicFile(time, sampleCID).metadata();
return new PublicFile(time).metadata();
});

expect(result.created).not.toBeUndefined();
});

test("A PublicFile can be written and read", async ({ page }) => {
const result = await page.evaluate(async () => {
const {
wnfs: { PublicFile },
mock: { MemoryBlockStore },
} = await window.setup();

const store = new MemoryBlockStore();
const time = new Date();
const file = new PublicFile(time);

const content = new TextEncoder().encode("Hello, World!");
const file2 = await file.setContent(time, content, store);

const readBack = await file2.readAt(0, undefined, store);

return new TextDecoder().decode(readBack);
});

expect(result).toEqual("Hello, World!");
});

test("A PublicFile supports chunking files", async ({ page }) => {
const longString = "x".repeat(5 * 1024 * 1024); // 5 MiB

const result = await page.evaluate(async () => {
const {
wnfs: { PublicFile },
mock: { MemoryBlockStore },
} = await window.setup();

const store = new MemoryBlockStore();
const time = new Date();
const file = new PublicFile(time);

const longString = "x".repeat(5 * 1024 * 1024);
const content = new TextEncoder().encode(longString);
const file2 = await file.setContent(time, content, store);

const readBack = await file2.readAt(0, undefined, store);

return new TextDecoder().decode(readBack);
});

expect(result).toEqual(longString);
});
});

0 comments on commit 235633f

Please sign in to comment.