Skip to content

Commit

Permalink
Introduced a Directory and allowed users to mount it in run()
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael-F-Bryan committed Nov 9, 2023
1 parent 2b17c18 commit 5a000cc
Show file tree
Hide file tree
Showing 10 changed files with 691 additions and 41 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ features = [
"DedicatedWorkerGlobalScope",
"ErrorEvent",
"FileReader",
"FileSystemDirectoryHandle",
"FileSystemFileHandle",
"FileSystemHandle",
"Headers",
"MessageEvent",
"Navigator",
Expand All @@ -64,6 +67,7 @@ features = [
"RequestInit",
"RequestMode",
"Response",
"StorageManager",
"Url",
"WebSocket",
"Window",
Expand Down
5 changes: 5 additions & 0 deletions src/facade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ impl SpawnConfig {
let env = self.parse_env()?;
runner.set_envs(env);

for (dest, dir) in self.mounted_directories()? {
// runner.mount_dir(dest.as_ref(), dir)?;
todo!("Mount {dir:?} to {dest}");
}

if let Some(uses) = self.uses() {
let uses = crate::utils::js_string_array(uses)?;
let packages = load_injected_packages(uses, runtime).await?;
Expand Down
143 changes: 143 additions & 0 deletions src/fs/directory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use std::sync::Arc;

use virtual_fs::{AsyncReadExt, AsyncWriteExt, FileSystem};
use wasm_bindgen::prelude::wasm_bindgen;
use web_sys::FileSystemDirectoryHandle;

use crate::utils::Error;

/// A directory that can be mounted inside a WASIX instance.
#[derive(Debug, Clone, wasm_bindgen_derive::TryFromJsValue)]
#[wasm_bindgen]
pub struct Directory(Arc<dyn FileSystem>);

#[wasm_bindgen]
impl Directory {
/// Create a temporary [`Directory`] that holds its contents in memory.
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Directory::default()
}

/// Create a new [`Directory`] using the [File System API][mdn].
///
/// > **Important:** this API will only work inside a secure context.
///
/// Some ways a [`FileSystemDirectoryHandle`] can be created are...
///
/// - Using the [Origin private file system API][opfs]
/// - Calling [`window.showDirectoryPicker()`][sdp]
/// to access a file on the host machine (i.e. outside of the browser)
/// - From the [HTML Drag & Drop API][dnd] by calling [`DataTransferItem.getAsFileSystemHandle()`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFileSystemHandle)
///
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API
/// [dnd]: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
/// [sdp]: https://developer.mozilla.org/en-US/docs/Web/API/window/showDirectoryPicker
/// [opfs]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system
#[wasm_bindgen(js_name = "fromBrowser")]
pub fn from_browser(handle: FileSystemDirectoryHandle) -> Self {
Directory(Arc::new(crate::fs::web::spawn(handle)))
}

#[wasm_bindgen(js_name = "readDir")]
pub fn read_dir(&self, mut path: String) -> Result<Vec<String>, Error> {
if !path.starts_with('/') {
path.insert(0, '/');
}

let mut contents = Vec::new();

for entry in FileSystem::read_dir(self, path.as_ref())? {
let entry = entry?;
contents.push(entry.path.display().to_string());
}

Ok(contents)
}

/// Write to a file.
#[wasm_bindgen(js_name = "writeFile")]
pub async fn write_file(&self, mut path: String, contents: Vec<u8>) -> Result<(), Error> {
if !path.starts_with('/') {
path.insert(0, '/');
}

let mut f = self
.new_open_options()
.write(true)
.create(true)
.open(&path)?;
f.write_all(&contents).await?;

Ok(())
}

/// Read the contents of a file from this directory.
///
/// Note that the path is relative to the directory's root.
#[wasm_bindgen(js_name = "readFile")]
pub async fn read_file(&self, mut path: String) -> Result<js_sys::Uint8Array, Error> {
if !path.starts_with('/') {
path.insert(0, '/');
}

let mut f = self.new_open_options().read(true).open(&path)?;
let mut buffer = Vec::with_capacity(f.size() as usize);
f.read_to_end(&mut buffer).await?;

Ok(js_sys::Uint8Array::from(&buffer[..]))
}

/// Create a directory.
#[wasm_bindgen(js_name = "createDir")]
pub fn create_dir(&self, mut path: String) -> Result<(), Error> {
if !path.starts_with('/') {
path.insert(0, '/');
}

FileSystem::create_dir(self, path.as_ref())?;

Ok(())
}
}

impl Default for Directory {
fn default() -> Self {
Directory(Arc::new(virtual_fs::mem_fs::FileSystem::default()))
}
}

impl FileSystem for Directory {
fn read_dir(&self, path: &std::path::Path) -> virtual_fs::Result<virtual_fs::ReadDir> {
self.0.read_dir(path)
}

fn create_dir(&self, path: &std::path::Path) -> virtual_fs::Result<()> {
self.0.create_dir(path)
}

fn remove_dir(&self, path: &std::path::Path) -> virtual_fs::Result<()> {
self.0.remove_dir(path)
}

fn rename<'a>(
&'a self,
from: &'a std::path::Path,
to: &'a std::path::Path,
) -> futures::future::BoxFuture<'a, virtual_fs::Result<()>> {
self.0.rename(from, to)
}

fn metadata(&self, path: &std::path::Path) -> virtual_fs::Result<virtual_fs::Metadata> {
self.0.metadata(path)
}

fn remove_file(&self, path: &std::path::Path) -> virtual_fs::Result<()> {
self.0.remove_file(path)
}

fn new_open_options(&self) -> virtual_fs::OpenOptions {
self.0.new_open_options()
}
}
4 changes: 4 additions & 0 deletions src/fs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod directory;
mod web;

pub use self::directory::Directory;
Loading

0 comments on commit 5a000cc

Please sign in to comment.