web-fs 0.2.9

An async file system for browser wasm.
Documentation
use std::{
    cell::RefCell,
    future::Future,
    io::Result,
    path::Path,
    pin::Pin,
    rc::Rc,
    task::{Context, Poll},
};

use js_sys::Object;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::spawn_local;
use web_sys::FileSystemFileHandle;

use crate::{get_file, set_value, File, Fs, Task, FS, HANDLE, INDEX, OPEN, OPTIONS, POST_ERROR};

const APPEND: u8 = 0b0000_0001;
const CREATE: u8 = 0b0000_0010;
const CREATE_NEW: u8 = 0b0000_0100;
const READ: u8 = 0b0000_1000;
const TRUNCATE: u8 = 0b0001_0000;
const WRITE: u8 = 0b0010_0000;
pub struct OpenOptions(u8);

impl Fs {
    fn open(
        &self,
        handle: FileSystemFileHandle,
        options: u8,
        inner: Rc<RefCell<Task<Result<File>>>>,
    ) {
        let index = self.inner.borrow_mut().opening_tasks.insert(inner);

        let open = Object::new();
        set_value(&open, &INDEX, &JsValue::from(index));
        set_value(&open, &HANDLE, &handle);
        set_value(&open, &OPTIONS, &JsValue::from(options));
        let msg = Object::new();
        set_value(&msg, &OPEN, &open);

        self.worker.post_message(&msg).expect(POST_ERROR);
    }
}

pub struct OpenFileFuture {
    inner: Rc<RefCell<Task<Result<File>>>>,
    append: bool,
}
impl Future for OpenFileFuture {
    type Output = Result<File>;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut inner = self.inner.borrow_mut();

        if let Some(val) = inner.result.take() {
            let result = val.map(|mut file| {
                if self.append {
                    file.cursor = file.size
                }
                file
            });
            return Poll::Ready(result);
        }
        inner.waker = Some(cx.waker().clone());
        Poll::Pending
    }
}
impl OpenOptions {
    pub fn new() -> Self {
        Self(0)
    }
    fn set_bit(&mut self, bit: u8, value: bool) {
        if value {
            self.0 |= bit;
        } else {
            self.0 &= !bit;
        }
    }
    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
        self.set_bit(APPEND, append);
        self
    }
    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
        self.set_bit(CREATE, create);
        self
    }
    pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
        self.set_bit(CREATE_NEW, create_new);
        self
    }
    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
        self.set_bit(READ, read);
        self
    }
    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
        self.set_bit(TRUNCATE, truncate);
        self
    }
    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
        self.set_bit(WRITE, write);
        self
    }
    pub fn open<P: AsRef<Path>>(&self, path: P) -> OpenFileFuture {
        let path = path.as_ref().to_string_lossy().to_string();

        let state = Task {
            waker: None,
            result: None,
        };
        let inner = Rc::new(RefCell::new(state));
        let inner_clone = inner.clone();

        let options = self.0;
        spawn_local(async move {
            let handle = get_file(path, options & CREATE | options & CREATE_NEW > 0).await;
            match handle {
                Ok(handle) => FS.with_borrow(|fs| fs.open(handle, options, inner_clone)),
                Err(e) => {
                    let mut state = inner_clone.borrow_mut();
                    state.result = Some(Err(e));
                    if let Some(waker) = state.waker.take() {
                        waker.wake();
                    }
                }
            }
        });
        OpenFileFuture {
            inner,
            append: self.0 & APPEND > 0,
        }
    }
}