web_fs/
open_options.rs

1use std::{
2    cell::RefCell,
3    future::Future,
4    io::Result,
5    path::Path,
6    pin::Pin,
7    rc::Rc,
8    task::{Context, Poll},
9};
10
11use js_sys::Object;
12use wasm_bindgen::JsValue;
13use wasm_bindgen_futures::spawn_local;
14use web_sys::FileSystemFileHandle;
15
16use crate::{get_file, set_value, File, Fs, Task, FS, HANDLE, INDEX, OPEN, OPTIONS, POST_ERROR};
17
18const APPEND: u8 = 0b0000_0001;
19const CREATE: u8 = 0b0000_0010;
20const CREATE_NEW: u8 = 0b0000_0100;
21const READ: u8 = 0b0000_1000;
22const TRUNCATE: u8 = 0b0001_0000;
23const WRITE: u8 = 0b0010_0000;
24pub struct OpenOptions(u8);
25
26impl Fs {
27    fn open(
28        &self,
29        handle: FileSystemFileHandle,
30        options: u8,
31        inner: Rc<RefCell<Task<Result<File>>>>,
32    ) {
33        let index = self.inner.borrow_mut().opening_tasks.insert(inner);
34
35        let open = Object::new();
36        set_value(&open, &INDEX, &JsValue::from(index));
37        set_value(&open, &HANDLE, &handle);
38        set_value(&open, &OPTIONS, &JsValue::from(options));
39        let msg = Object::new();
40        set_value(&msg, &OPEN, &open);
41
42        self.worker.post_message(&msg).expect(POST_ERROR);
43    }
44}
45
46pub struct OpenFileFuture {
47    inner: Rc<RefCell<Task<Result<File>>>>,
48    append: bool,
49}
50impl Future for OpenFileFuture {
51    type Output = Result<File>;
52    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
53        let mut inner = self.inner.borrow_mut();
54
55        if let Some(val) = inner.result.take() {
56            let result = val.map(|mut file| {
57                if self.append {
58                    file.cursor = file.size
59                }
60                file
61            });
62            return Poll::Ready(result);
63        }
64        inner.waker = Some(cx.waker().clone());
65        Poll::Pending
66    }
67}
68impl OpenOptions {
69    pub fn new() -> Self {
70        Self(0)
71    }
72    fn set_bit(&mut self, bit: u8, value: bool) {
73        if value {
74            self.0 |= bit;
75        } else {
76            self.0 &= !bit;
77        }
78    }
79    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
80        self.set_bit(APPEND, append);
81        self
82    }
83    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
84        self.set_bit(CREATE, create);
85        self
86    }
87    pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
88        self.set_bit(CREATE_NEW, create_new);
89        self
90    }
91    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
92        self.set_bit(READ, read);
93        self
94    }
95    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
96        self.set_bit(TRUNCATE, truncate);
97        self
98    }
99    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
100        self.set_bit(WRITE, write);
101        self
102    }
103    pub fn open<P: AsRef<Path>>(&self, path: P) -> OpenFileFuture {
104        let path = path.as_ref().to_string_lossy().to_string();
105
106        let state = Task {
107            waker: None,
108            result: None,
109        };
110        let inner = Rc::new(RefCell::new(state));
111        let inner_clone = inner.clone();
112
113        let options = self.0;
114        spawn_local(async move {
115            let handle = get_file(path, options & CREATE | options & CREATE_NEW > 0).await;
116            match handle {
117                Ok(handle) => FS.with_borrow(|fs| fs.open(handle, options, inner_clone)),
118                Err(e) => {
119                    let mut state = inner_clone.borrow_mut();
120                    state.result = Some(Err(e));
121                    if let Some(waker) = state.waker.take() {
122                        waker.wake();
123                    }
124                }
125            }
126        });
127        OpenFileFuture {
128            inner,
129            append: self.0 & APPEND > 0,
130        }
131    }
132}