web_fs/
file.rs

1use std::{
2    cell::RefCell,
3    future::Future,
4    io::{Error, ErrorKind, Result},
5    path::Path,
6    pin::Pin,
7    rc::Rc,
8    task::{Context, Poll},
9};
10
11use futures_lite::AsyncWriteExt;
12use js_sys::Object;
13use wasm_bindgen::JsValue;
14
15use crate::{
16    open_options::OpenFileFuture, read::ReadResult, util::set_value, FileType, Fs, Metadata,
17    OpenOptions, Permissions, Task, FD, FS, INDEX, SIZE, TRUNCATE,
18};
19
20pub struct File {
21    pub(crate) fd: usize,
22    pub(crate) cursor: u64,
23    pub(crate) size: u64,
24    pub(crate) read_task: Option<Rc<RefCell<Task<Result<ReadResult>>>>>,
25    pub(crate) write_task: Option<Rc<RefCell<Task<Result<usize>>>>>,
26    pub(crate) flush_task: Option<Rc<RefCell<Task<Result<()>>>>>,
27    pub(crate) close_task: Option<Rc<RefCell<Task<Result<()>>>>>,
28}
29impl File {
30    pub(crate) fn new(fd: usize, size: u64) -> Self {
31        Self {
32            fd,
33            size,
34            cursor: 0,
35            read_task: None,
36            write_task: None,
37            flush_task: None,
38            close_task: None,
39        }
40    }
41    pub fn open<P: AsRef<Path>>(path: P) -> OpenFileFuture {
42        OpenOptions::new().read(true).open(path)
43    }
44    pub fn create<P: AsRef<Path>>(path: P) -> OpenFileFuture {
45        OpenOptions::new().create(true).write(true).open(path)
46    }
47    /// Currenly this is only available in [`std::fs::File`] not [`async-fs::File`]
48    pub fn create_new<P: AsRef<Path>>(path: P) -> OpenFileFuture {
49        OpenOptions::new().create_new(true).write(true).open(path)
50    }
51    /// This is a different implementation that require `&mut self` while [`async-fs::File`] and [`std::fs::File`] doesn't.
52    pub async fn sync_data(&mut self) -> Result<()> {
53        self.flush().await?;
54        Ok(())
55    }
56    /// This is a different implementation that require `&mut self` while [`async-fs::File`] and [`std::fs::File`] doesn't.
57    pub async fn sync_all(&mut self) -> Result<()> {
58        self.flush().await?;
59        Ok(())
60    }
61    /// This is a different implementation that require `&mut self` while [`async-fs::File`] and [`std::fs::File`] doesn't.
62    pub fn set_len<'a>(&'a mut self, size: u64) -> TruncateFuture<'a> {
63        let task = Rc::new(RefCell::new(Task {
64            waker: None,
65            result: None,
66        }));
67        let task_clone = task.clone();
68        FS.with_borrow(|fs| fs.truncate(self.fd, size, task_clone));
69        TruncateFuture {
70            task,
71            size,
72            file: self,
73        }
74    }
75    pub async fn metadata(&self) -> Result<Metadata> {
76        Ok(Metadata {
77            ty: FileType::File,
78            len: self.size,
79        })
80    }
81    /// Currently always returns Err,
82    /// because the permission of a file in *File System API* is determined when opening the file
83    /// and can't be changed afterwards.
84    pub async fn set_permissions(&self, perm: Permissions) -> Result<()> {
85        drop(perm);
86        Err(Error::from(ErrorKind::Other))
87    }
88}
89
90impl Drop for File {
91    fn drop(&mut self) {
92        FS.with_borrow(|fs| fs.drop_file(self.fd));
93    }
94}
95
96pub struct TruncateFuture<'a> {
97    task: Rc<RefCell<Task<Result<()>>>>,
98    size: u64,
99    file: &'a mut File,
100}
101impl Future for TruncateFuture<'_> {
102    type Output = Result<()>;
103    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
104        let inner_self = self.get_mut();
105        let mut inner = inner_self.task.borrow_mut();
106
107        if let Some(val) = inner.result.take() {
108            if let Ok(()) = val {
109                inner_self.file.size = inner_self.size;
110            }
111            return Poll::Ready(val);
112        }
113        inner.waker = Some(cx.waker().clone());
114        Poll::Pending
115    }
116}
117
118impl Fs {
119    fn truncate(&self, fd: usize, size: u64, task: Rc<RefCell<Task<Result<()>>>>) {
120        let index = self.inner.borrow_mut().truncating_tasks.insert(task);
121
122        let msg = Object::new();
123        let truncate = Object::new();
124        set_value(&truncate, &INDEX, &JsValue::from_f64(index as f64));
125        set_value(&truncate, &FD, &JsValue::from_f64(fd as f64));
126        set_value(&truncate, &SIZE, &JsValue::from_f64(size as f64));
127        set_value(&msg, &TRUNCATE, &truncate);
128
129        self.worker.post_message(&msg).unwrap();
130    }
131}