diff options
Diffstat (limited to 'filamento/src/files')
-rw-r--r-- | filamento/src/files/opfs.rs | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/filamento/src/files/opfs.rs b/filamento/src/files/opfs.rs new file mode 100644 index 0000000..e040762 --- /dev/null +++ b/filamento/src/files/opfs.rs @@ -0,0 +1,110 @@ +use std::path::Path; + +use thiserror::Error; +use wasm_bindgen::JsValue; +use wasm_bindgen_futures::JsFuture; +use web_sys::{ + window, File, FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemGetDirectoryOptions, FileSystemGetFileOptions, FileSystemWritableFileStream, Url +}; + +use crate::FileStore; + +#[derive(Clone)] +pub struct FilesOPFS { + directory: String, +} + +impl FilesOPFS { + pub async fn new(directory: impl AsRef<str>) -> Result<Self, OPFSError> { + let directory = directory.as_ref(); + let directory_string = directory.to_string(); + let promise = window().unwrap().navigator().storage().get_directory(); + let opfs_root: FileSystemDirectoryHandle = JsFuture::from(promise).await?.into(); + let options = FileSystemGetDirectoryOptions::new(); + options.set_create(true); + let directory: FileSystemDirectoryHandle = + JsFuture::from(opfs_root.get_directory_handle_with_options(directory, &options)) + .await? + .into(); + Ok(Self { directory: directory_string }) + } + + pub async fn get_src(&self, file_name: impl AsRef<str>) -> Result<String, OPFSError> { + let promise = window().unwrap().navigator().storage().get_directory(); + let opfs_root: FileSystemDirectoryHandle = JsFuture::from(promise).await?.into(); + let directory: FileSystemDirectoryHandle = + JsFuture::from(opfs_root.get_directory_handle(&self.directory)) + .await? + .into(); + let handle: FileSystemFileHandle = + JsFuture::from(directory.get_file_handle(file_name.as_ref())) + .await? + .into(); + let file: File = JsFuture::from(handle.get_file()).await?.into(); + let src = Url::create_object_url_with_blob(&file)?; + Ok(src) + } +} + +impl FileStore for FilesOPFS { + type Err = OPFSError; + + async fn is_stored(&self, name: &str) -> Result<bool, Self::Err> { + let promise = window().unwrap().navigator().storage().get_directory(); + let opfs_root: FileSystemDirectoryHandle = JsFuture::from(promise).await?.into(); + let directory: FileSystemDirectoryHandle = + JsFuture::from(opfs_root.get_directory_handle(&self.directory)) + .await? + .into(); + let stored = JsFuture::from(directory.get_file_handle(name)) + .await + .map(|_| true) + // TODO: distinguish between other errors and notfound + .unwrap_or(false); + Ok(stored) + } + + async fn store(&self, name: &str, data: &[u8]) -> Result<(), Self::Err> { + let promise = window().unwrap().navigator().storage().get_directory(); + let opfs_root: FileSystemDirectoryHandle = JsFuture::from(promise).await?.into(); + let directory: FileSystemDirectoryHandle = + JsFuture::from(opfs_root.get_directory_handle(&self.directory)) + .await? + .into(); + let options = FileSystemGetFileOptions::new(); + options.set_create(true); + let handle: FileSystemFileHandle = JsFuture::from(directory.get_file_handle_with_options(name, &options)) + .await? + .into(); + let write_handle: FileSystemWritableFileStream = + JsFuture::from(handle.create_writable()).await?.into(); + let write_promise = write_handle.write_with_u8_array(data)?; + let _ = JsFuture::from(write_promise).await?; + let _ = JsFuture::from(write_handle.close()).await?; + Ok(()) + } + + async fn delete(&self, name: &str) -> Result<(), Self::Err> { + let promise = window().unwrap().navigator().storage().get_directory(); + let opfs_root: FileSystemDirectoryHandle = JsFuture::from(promise).await?.into(); + let directory: FileSystemDirectoryHandle = + JsFuture::from(opfs_root.get_directory_handle(&self.directory)) + .await? + .into(); + let _ = JsFuture::from(directory.remove_entry(name)).await?; + Ok(()) + } +} + +#[derive(Error, Clone, Debug)] +pub enum OPFSError { + #[error("not found")] + NotFound, +} + +// TODO: better errors +impl From<JsValue> for OPFSError { + fn from(value: JsValue) -> Self { + Self::NotFound + } +} |