Skip to content

Commit

Permalink
docs: add docs for OPFS
Browse files Browse the repository at this point in the history
  • Loading branch information
crwen authored and ethe committed Dec 5, 2024
1 parent af66ff6 commit 7721cfa
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
9 changes: 9 additions & 0 deletions fusio/src/impls/disk/opfs/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::{
Error,
};

/// [OPFS](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system) backend
pub struct OPFS;

impl Fs for OPFS {
Expand All @@ -31,6 +32,11 @@ impl Fs for OPFS {
FileSystemTag::Local
}

/// Open a [`OPFSFile`] with options.
///
/// It is not permitted to use paths that temporarily step outside the sandbox with something
/// like `../foo` or `./bar`. It is recommended to call [`Path::from_opfs_path`] or
/// [`Path::parse`]
async fn open_options(&self, path: &Path, options: OpenOptions) -> Result<Self::File, Error> {
let segments: Vec<&str> = path.as_ref().trim_matches('/').split("/").collect();

Expand All @@ -56,6 +62,7 @@ impl Fs for OPFS {
Ok(OPFSFile::new(file_handle))
}

/// Recursively creates a directory and all of its parent components if they are missing.
async fn create_dir_all(path: &Path) -> Result<(), Error> {
let options = FileSystemGetDirectoryOptions::new();
options.set_create(true);
Expand All @@ -65,6 +72,7 @@ impl Fs for OPFS {
Ok(())
}

/// Returns an iterator over the entries within a directory.
async fn list(
&self,
path: &Path,
Expand All @@ -90,6 +98,7 @@ impl Fs for OPFS {
})
}

/// Recursively removes an entry from OPFS. See more detail in [removeEntry](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/removeEntry)
async fn remove(&self, path: &Path) -> Result<(), Error> {
let dir_options = FileSystemGetDirectoryOptions::new();
dir_options.set_create(false);
Expand Down
44 changes: 41 additions & 3 deletions fusio/src/impls/disk/opfs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ where
js_val.dyn_into::<T>().map_err(|_obj| Error::CastError)
}

/// File handle of OPFS file
pub struct FileHandle {
file_handle: FileSystemFileHandle,
}
Expand All @@ -33,6 +34,9 @@ impl FileHandle {
}

impl FileHandle {
/// Attempts to write an entire buffer into the file.
///
/// Unlike [`OPFSFile::write_all`], changes will be written to the actual file.
pub async fn write_at<B: IoBuf>(&self, buf: B, pos: u64) -> (Result<(), Error>, B) {
let options = FileSystemCreateWritableOptions::new();
options.set_keep_existing_data(true);
Expand All @@ -58,8 +62,10 @@ impl FileHandle {

(result, buf)
}

pub async fn write_with_stream<B: IoBuf>(
/// Attempts to write an entire buffer into the stream.
///
/// No changes are written to the actual file on disk until the stream is closed.
async fn write_with_stream<B: IoBuf>(
&self,
buf: B,
stream: &FileSystemWritableFileStream,
Expand All @@ -70,6 +76,7 @@ impl FileHandle {
}
}

/// Create a `FileSystemWritableFileStream` and return a JavaScript Promise
fn create_writable_with_options(
&self,
options: &FileSystemCreateWritableOptions,
Expand All @@ -79,6 +86,12 @@ impl FileHandle {
}

impl FileHandle {
/// Reads all bytes until EOF in this source, placing them into `buf`.
///
/// # Errors
///
/// If an error is encountered then the `read_to_end_at` operation
/// immediately completes.
async fn read_to_end_at(&self, mut buf: Vec<u8>, pos: u64) -> (Result<(), Error>, Vec<u8>) {
let file_promise = self.file_handle.get_file();
let file = match promise::<File>(file_promise).await {
Expand Down Expand Up @@ -119,6 +132,12 @@ impl FileHandle {
(Ok(()), buf)
}

/// Reads the exact number of bytes required to fill `buf` at `pos`.
///
/// # Errors
///
/// If the operation encounters an "end of file" before completely
/// filling the buffer, it returns an error of [`Error::Io`].
pub async fn read_exact_at<B: IoBufMut>(&self, mut buf: B, pos: u64) -> (Result<(), Error>, B) {
let buf_len = buf.bytes_init() as i32;
let buf_slice = buf.as_slice_mut();
Expand Down Expand Up @@ -172,14 +191,15 @@ impl FileHandle {
}
}

/// OPFS based on [FileSystemWritableFileStream](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream)
pub struct OPFSFile {
file_handle: Option<Arc<FileHandle>>,
write_stream: Option<FileSystemWritableFileStream>,
pos: u32,
}

impl OPFSFile {
pub fn new(file_handle: FileSystemFileHandle) -> Self {
pub(crate) fn new(file_handle: FileSystemFileHandle) -> Self {
Self {
file_handle: Some(Arc::new(FileHandle::new(file_handle))),
write_stream: None,
Expand All @@ -196,6 +216,10 @@ impl OPFSFile {
}

impl Write for OPFSFile {
/// Attempts to write an entire buffer into the file.
///
/// No changes are written to the actual file on disk until [`OPFSFile::close`] has been called.
/// See more detail in [write](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/write)
async fn write_all<B: IoBuf>(&mut self, buf: B) -> (Result<(), Error>, B) {
let file_handle = self.file_handle.as_ref().expect("write file after closed");
if self.write_stream.is_none() {
Expand Down Expand Up @@ -228,6 +252,7 @@ impl Write for OPFSFile {
Ok(())
}

/// Close the associated OPFS file.
async fn close(&mut self) -> Result<(), Error> {
let writer = self.write_stream.take();
if let Some(writer) = writer {
Expand All @@ -239,18 +264,31 @@ impl Write for OPFSFile {
}

impl Read for OPFSFile {
/// Reads the exact number of bytes required to fill `buf` at `pos`.
///
/// # Errors
///
/// If the operation encounters an "end of file" before completely
/// filling the buffer, it returns an error of [`Error::Io`].
async fn read_exact_at<B: IoBufMut>(&mut self, buf: B, pos: u64) -> (Result<(), Error>, B) {
let file_handle = self.file_handle.as_ref().expect("read file after closed");

file_handle.read_exact_at(buf, pos).await
}

/// Reads all bytes until EOF in this source, placing them into `buf`.
///
/// # Errors
///
/// If an error is encountered then the `read_to_end_at` operation
/// immediately completes.
async fn read_to_end_at(&mut self, buf: Vec<u8>, pos: u64) -> (Result<(), Error>, Vec<u8>) {
let file_handle = self.file_handle.as_ref().expect("read file after closed");

file_handle.read_to_end_at(buf, pos).await
}

/// Return the size of file in bytes.
async fn size(&self) -> Result<u64, Error> {
let file_handle = self.file_handle.as_ref().expect("read file after closed");
file_handle.size().await
Expand Down

0 comments on commit 7721cfa

Please sign in to comment.