1#![doc = include_str!("../README.md")]
2
3mod c_static_str;
4pub(crate) use c_static_str::*;
5mod open_options;
6use arena::Arena;
7use js_sys::{ArrayBuffer, Object, Reflect};
8pub use open_options::OpenOptions;
9use read::ReadResult;
10use util::{get_value, get_value_as_f64, js_value_to_error, set_value, Task};
11use wasm_bindgen_futures::{stream::JsStream, JsFuture};
12mod arena;
13mod file;
14mod read;
15mod seek;
16mod write;
17pub use file::File;
18mod metadata;
19mod util;
20pub use metadata::*;
21
22use std::{
23 cell::RefCell,
24 ffi::OsString,
25 io::{Error, ErrorKind, Result},
26 path::{Component, Path, PathBuf},
27 rc::Rc,
28};
29
30use futures_lite::{AsyncReadExt, AsyncWriteExt, Stream, StreamExt};
31use wasm_bindgen::prelude::*;
32use web_sys::{
33 window, FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemGetDirectoryOptions,
34 FileSystemGetFileOptions, FileSystemRemoveOptions, MessageEvent, Worker, WorkerGlobalScope,
35};
36
37const GETTING_JS_FIELD_ERROR: &str = "Getting js field error, this is an error of the crate.";
38const ARENA_REMOVE_ERROR: &str = "Removing from arena error, this is an error of the crate.";
39const DYN_INTO_ERROR: &str = "Converting js type failed, this is an error of the crate.";
40const POST_ERROR: &str = "Posting message to worker failed, this is an error of the crate";
41
42struct FsInner {
43 opening_tasks: Arena<Rc<RefCell<Task<Result<File>>>>>,
44 reading_tasks: Arena<Rc<RefCell<Task<Result<ReadResult>>>>>,
45 writing_tasks: Arena<Rc<RefCell<Task<Result<usize>>>>>,
46 flushing_tasks: Arena<Rc<RefCell<Task<Result<()>>>>>,
47 closing_tasks: Arena<Rc<RefCell<Task<Result<()>>>>>,
48 truncating_tasks: Arena<Rc<RefCell<Task<Result<()>>>>>,
49}
50struct Fs {
51 inner: Rc<RefCell<FsInner>>,
52 _closure: Closure<dyn FnMut(MessageEvent)>,
53 worker: Worker,
54}
55impl Fs {
56 fn new() -> Self {
57 let worker = Worker::new(&wasm_bindgen::link_to!(module = "/src/worker.js"))
58 .expect("Creating web worker failed. This crate relies on web worker to work.");
59
60 let inner = FsInner {
61 opening_tasks: Arena::new(),
62 reading_tasks: Arena::new(),
63 writing_tasks: Arena::new(),
64 flushing_tasks: Arena::new(),
65 closing_tasks: Arena::new(),
66 truncating_tasks: Arena::new(),
67 };
68 let inner = Rc::new(RefCell::new(inner));
69 let inner_clone = inner.clone();
70 #[repr(u32)]
71 enum InMsgType {
72 Open = 0,
73 Read,
74 Write,
75 Flush,
76 Close,
77 Truncate,
78 }
79 let on_message: Closure<dyn FnMut(MessageEvent)> =
80 Closure::new(move |msg: MessageEvent| {
81 let received = msg.data();
82 let error = get_value(&received, &ERROR);
83 let error = if !error.is_undefined() {
84 Some(error.as_string()).expect(
85 "Converting js error to string failed, this is an error of the crate.",
86 )
87 } else {
88 None
89 };
90
91 let open_msg = Reflect::get_u32(&received, InMsgType::Open as u32)
92 .expect(GETTING_JS_FIELD_ERROR);
93 if !open_msg.is_undefined() {
94 let index = get_value_as_f64(&open_msg, &INDEX) as usize;
95 let task = inner_clone
96 .borrow_mut()
97 .opening_tasks
98 .remove(index)
99 .expect(ARENA_REMOVE_ERROR);
100 let mut state = task.borrow_mut();
101 if let Some(error) = error {
102 state.result = Some(Err(Error::other(error)));
103 } else {
104 let fd = get_value_as_f64(&open_msg, &FD) as usize;
105 let size = get_value_as_f64(&open_msg, &SIZE) as u64;
106 state.result = Some(Ok(File::new(fd, size)));
107 }
108 if let Some(waker) = state.waker.take() {
109 waker.wake();
110 }
111 return;
112 }
113 let read_msg = Reflect::get_u32(&received, InMsgType::Read as u32)
114 .expect(GETTING_JS_FIELD_ERROR);
115 if !read_msg.is_undefined() {
116 let index = get_value_as_f64(&read_msg, &INDEX) as usize;
117 let task = inner_clone
118 .borrow_mut()
119 .reading_tasks
120 .remove(index)
121 .expect(ARENA_REMOVE_ERROR);
122 let mut state = task.borrow_mut();
123 if let Some(error) = error {
124 state.result = Some(Err(Error::other(error)));
125 } else {
126 let size = get_value_as_f64(&read_msg, &SIZE) as usize;
127 let array_buffer = get_value(&read_msg, &BUF)
128 .dyn_into::<ArrayBuffer>()
129 .expect(DYN_INTO_ERROR);
130 state.result = Some(Ok(ReadResult {
131 buf: array_buffer,
132 size,
133 }));
134 }
135 if let Some(waker) = state.waker.take() {
136 waker.wake();
137 }
138 return;
139 }
140 let write_msg = Reflect::get_u32(&received, InMsgType::Write as u32)
141 .expect(GETTING_JS_FIELD_ERROR);
142 if !write_msg.is_undefined() {
143 let index = get_value_as_f64(&write_msg, &INDEX) as usize;
144 let task = inner_clone
145 .borrow_mut()
146 .writing_tasks
147 .remove(index)
148 .expect(ARENA_REMOVE_ERROR);
149 let mut state = task.borrow_mut();
150
151 if let Some(error) = error {
152 state.result = Some(Err(Error::other(error)));
153 } else {
154 let size = get_value_as_f64(&write_msg, &SIZE) as usize;
155 state.result = Some(Ok(size));
156 }
157
158 if let Some(waker) = state.waker.take() {
159 waker.wake();
160 }
161 return;
162 }
163 let flush_msg = Reflect::get_u32(&received, InMsgType::Flush as u32)
164 .expect(GETTING_JS_FIELD_ERROR);
165 if !flush_msg.is_undefined() {
166 let index = get_value_as_f64(&flush_msg, &INDEX) as usize;
167 let task = inner_clone
168 .borrow_mut()
169 .flushing_tasks
170 .remove(index)
171 .expect(ARENA_REMOVE_ERROR);
172 let mut state = task.borrow_mut();
173
174 if let Some(error) = error {
175 state.result = Some(Err(Error::other(error)));
176 } else {
177 state.result = Some(Ok(()))
178 }
179
180 if let Some(waker) = state.waker.take() {
181 waker.wake();
182 }
183 return;
184 }
185 let close_msg = Reflect::get_u32(&received, InMsgType::Close as u32)
186 .expect(GETTING_JS_FIELD_ERROR);
187 if !close_msg.is_undefined() {
188 let index = get_value_as_f64(&close_msg, &INDEX) as usize;
189 let task = inner_clone
190 .borrow_mut()
191 .closing_tasks
192 .remove(index)
193 .expect(ARENA_REMOVE_ERROR);
194 let mut state = task.borrow_mut();
195
196 if let Some(error) = error {
197 state.result = Some(Err(Error::other(error)));
198 } else {
199 state.result = Some(Ok(()))
200 }
201
202 if let Some(waker) = state.waker.take() {
203 waker.wake();
204 }
205 return;
206 }
207 let truncate_msg = Reflect::get_u32(&received, InMsgType::Truncate as u32)
208 .expect(GETTING_JS_FIELD_ERROR);
209 if !truncate_msg.is_undefined() {
210 let index = get_value_as_f64(&truncate_msg, &INDEX) as usize;
211 let task = inner_clone
212 .borrow_mut()
213 .truncating_tasks
214 .remove(index)
215 .expect(ARENA_REMOVE_ERROR);
216 let mut state = task.borrow_mut();
217
218 if let Some(error) = error {
219 state.result = Some(Err(Error::other(error)));
220 } else {
221 state.result = Some(Ok(()))
222 }
223
224 if let Some(waker) = state.waker.take() {
225 waker.wake();
226 }
227 return;
228 }
229 });
230 worker.set_onmessage(Some(on_message.as_ref().unchecked_ref()));
231 Self {
232 inner,
233 _closure: on_message,
234 worker,
235 }
236 }
237 fn drop_file(&self, fd: usize) {
238 let msg = Object::new();
239 let drop = Object::new();
240 set_value(&drop, &FD, &JsValue::from(fd));
241 set_value(&msg, &DROP, &drop);
242
243 self.worker.post_message(&msg).expect(POST_ERROR);
244 }
245}
246thread_local! {
247 static FS: RefCell<Fs> = RefCell::new(Fs::new());
248}
249
250async fn get_root() -> Result<FileSystemDirectoryHandle> {
251 let storage = if let Some(window) = window() {
252 let navigator = window.navigator();
253 navigator.storage()
254 } else if js_sys::global().is_instance_of::<WorkerGlobalScope>() {
255 let global = js_sys::global().unchecked_into::<WorkerGlobalScope>();
256 global.navigator().storage()
257 } else {
258 return Err(std::io::Error::new(
259 std::io::ErrorKind::Unsupported,
260 "unable to access browser storage",
261 ));
262 };
263 JsFuture::from(storage.get_directory())
264 .await
265 .map_err(|_| {
266 std::io::Error::new(
267 std::io::ErrorKind::Unsupported,
268 "unable to get root directory",
269 )
270 })?
271 .dyn_into::<FileSystemDirectoryHandle>()
272 .map_err(|_| std::io::Error::new(std::io::ErrorKind::Unsupported, DYN_INTO_ERROR))
273}
274
275async fn child_dir(
276 parent: &FileSystemDirectoryHandle,
277 name: &str,
278 create: bool,
279) -> Result<FileSystemDirectoryHandle> {
280 let options = FileSystemGetDirectoryOptions::new();
281 options.set_create(create);
282 let result = JsFuture::from(parent.get_directory_handle_with_options(name, &options))
283 .await
284 .map_err(|e| js_value_to_error(e))?
285 .dyn_into::<FileSystemDirectoryHandle>()
286 .expect(DYN_INTO_ERROR);
287 Ok(result)
288}
289
290async fn child_file(
291 parent: &FileSystemDirectoryHandle,
292 name: &str,
293 create: bool,
294) -> Result<FileSystemFileHandle> {
295 let options = FileSystemGetFileOptions::new();
296 options.set_create(create);
297 let result = JsFuture::from(parent.get_file_handle_with_options(name, &options))
298 .await
299 .map_err(|e| js_value_to_error(e))?
300 .dyn_into::<FileSystemFileHandle>()
301 .expect(DYN_INTO_ERROR);
302 Ok(result)
303}
304
305async fn get_parent_dir<P: AsRef<Path>>(
306 path: P,
307 create: bool,
308) -> Result<FileSystemDirectoryHandle> {
309 let path = path.as_ref();
310 let root = get_root().await?;
311 let mut parents_stack = vec![root];
312 if let Some(path) = path.parent() {
313 for component in path.components() {
314 match component {
315 Component::Prefix(_) => return Err(Error::from(ErrorKind::PermissionDenied)),
317 Component::CurDir | Component::RootDir => (),
318 Component::ParentDir => {
319 if parents_stack.len() == 1 {
321 return Err(Error::from(ErrorKind::PermissionDenied));
322 } else {
323 parents_stack.pop();
324 }
325 }
326 Component::Normal(name) => {
327 let name = name.to_string_lossy();
328 parents_stack.push(
329 child_dir(parents_stack.last().as_ref().unwrap(), &name, create).await?,
330 );
331 }
332 }
333 }
334 }
335 Ok(parents_stack.pop().unwrap())
336}
337
338async fn get_dir<P: AsRef<Path>>(
339 path: P,
340 create: bool,
341 create_parents: bool,
342) -> Result<FileSystemDirectoryHandle> {
343 let parent_dir = get_parent_dir(&path, create_parents).await?;
344 if let Some(name) = path.as_ref().file_name() {
345 let name = name.to_string_lossy();
346 child_dir(&parent_dir, &name, create).await
347 } else {
348 Ok(parent_dir)
349 }
350}
351
352async fn get_file<P: AsRef<Path>>(path: P, create: bool) -> Result<FileSystemFileHandle> {
353 let parent_dir = get_parent_dir(&path, false).await?;
354 if let Some(name) = path.as_ref().file_name() {
355 let name = name.to_string_lossy();
356 child_file(&parent_dir, &name, create).await
357 } else {
358 Err(Error::from(ErrorKind::AlreadyExists))
359 }
360}
361
362pub async fn create_dir<P: AsRef<Path>>(path: P) -> Result<()> {
363 get_dir(path, true, false).await?;
364 Ok(())
365}
366pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
367 get_dir(path, true, true).await?;
368 Ok(())
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub enum FileType {
374 File,
375 Dir,
376}
377impl FileType {
378 pub fn is_dir(&self) -> bool {
379 *self == Self::Dir
380 }
381 pub fn is_file(&self) -> bool {
382 *self == Self::File
383 }
384 pub fn is_symlink(&self) -> bool {
385 false
386 }
387}
388
389#[derive(Debug)]
390pub struct DirEntry {
391 name: OsString,
392 file_type: FileType,
393 path: PathBuf,
394}
395impl DirEntry {
396 pub fn file_name(&self) -> OsString {
397 self.name.clone()
398 }
399 pub async fn file_type(&self) -> Result<FileType> {
401 Ok(self.file_type)
402 }
403 pub async fn metadata(&self) -> Result<std::fs::Metadata> {
405 Err(Error::other("Metadata is not supported currently"))
406 }
407 pub fn path(&self) -> PathBuf {
408 self.path.clone()
409 }
410}
411
412pub async fn read_dir<P: AsRef<Path>>(path: P) -> Result<impl Stream<Item = Result<DirEntry>>> {
413 let dir = get_dir(&path, false, false).await?;
414 let stream = JsStream::from(dir.entries());
415 let read_dir = stream.map(move |v| {
416 let entry = v.map_err(|e| js_value_to_error(e))?;
417 const RESOLVE_ENTRY_ERROR: &str =
418 "Getting the key and value of the dir entry failed, this is an error of the crate.";
419 let key = Reflect::get_u32(&entry, 0)
420 .expect(RESOLVE_ENTRY_ERROR)
421 .as_string()
422 .expect("This is supposed to be a string, else this is an error of the crate.");
423 let value = Reflect::get_u32(&entry, 1).expect(RESOLVE_ENTRY_ERROR);
424
425 let mut path = path.as_ref().to_path_buf();
426 path.push(&key);
427 let name = OsString::from(key);
428 if let Some(_) = value.dyn_ref::<FileSystemFileHandle>() {
429 Ok(DirEntry {
430 name,
431 file_type: FileType::File,
432 path,
433 })
434 } else {
435 Ok(DirEntry {
436 name,
437 file_type: FileType::Dir,
438 path,
439 })
440 }
441 });
442 Ok(read_dir)
443}
444
445pub async fn remove_dir<P: AsRef<Path>>(path: P) -> Result<()> {
447 let parent_dir = get_parent_dir(&path, false).await?;
448 let name = path
449 .as_ref()
450 .file_name()
451 .ok_or(Error::from(ErrorKind::NotFound))?
452 .to_string_lossy();
453
454 JsFuture::from(parent_dir.remove_entry(&name))
455 .await
456 .map_err(|e| js_value_to_error(e))?;
457
458 Ok(())
459}
460
461pub async fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
463 remove_dir(path).await
464}
465
466pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
467 let parent_dir = get_parent_dir(&path, false).await?;
468 let name = path
469 .as_ref()
470 .file_name()
471 .ok_or(Error::from(ErrorKind::NotFound))?
472 .to_string_lossy();
473
474 let options = FileSystemRemoveOptions::new();
475 options.set_recursive(true);
476
477 JsFuture::from(parent_dir.remove_entry_with_options(&name, &options))
478 .await
479 .map_err(|e| js_value_to_error(e))?;
480
481 Ok(())
482}
483
484pub async fn read<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
485 let mut file = File::open(path).await?;
486 let mut buf = Vec::new();
487 file.read_to_end(&mut buf).await?;
488 Ok(buf)
489}
490
491pub async fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
492 let mut file = File::open(path).await?;
493 let mut buf = String::new();
494 file.read_to_string(&mut buf).await?;
495 Ok(buf)
496}
497
498pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
499 let mut file = File::create_new(path).await?;
500 file.write_all(contents.as_ref()).await.unwrap();
501 Ok(())
502}
503
504pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<u64> {
505 let mut src = File::open(from).await?;
506 let mut dst = File::create_new(to).await?;
507 let buf_size = src.size.min(1 << 6) as usize;
508 let mut buf = vec![0; buf_size];
509 loop {
510 let read_size = src.read(&mut buf).await?;
511 if read_size == 0 {
512 break;
513 }
514 dst.write_all(&buf[0..read_size]).await?;
515 buf[0..read_size].fill(0);
516 }
517 Ok(src.size)
518}