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}