diff options
Diffstat (limited to '')
-rw-r--r-- | filamento/Cargo.toml | 1 | ||||
-rw-r--r-- | filamento/src/db.rs | 13 | ||||
-rw-r--r-- | filamento/src/error.rs | 114 | ||||
-rw-r--r-- | filamento/src/files/opfs.rs | 2 | ||||
-rw-r--r-- | filamento/src/logic/connect.rs | 2 | ||||
-rw-r--r-- | filamento/src/logic/mod.rs | 2 | ||||
-rw-r--r-- | filamento/src/logic/online.rs | 49 |
7 files changed, 155 insertions, 28 deletions
diff --git a/filamento/Cargo.toml b/filamento/Cargo.toml index 7d7af0c..b89c577 100644 --- a/filamento/Cargo.toml +++ b/filamento/Cargo.toml @@ -52,6 +52,7 @@ tokio = { workspace = true, features = ["sync", "time", "rt", "fs", "io-std"] } [target.'cfg(target_arch = "wasm32")'.dependencies] tokio = { workspace = true, features = ["sync", "time", "rt"] } +js-sys.workspace = true web-sys = { workspace = true, features = [ "FileSystemDirectoryHandle", "FileSystemWritableFileStream", diff --git a/filamento/src/db.rs b/filamento/src/db.rs index 385382a..298d54a 100644 --- a/filamento/src/db.rs +++ b/filamento/src/db.rs @@ -88,12 +88,21 @@ impl Db { spawn_blocking(move || { spawn_local(async move { debug!("installing opfs in spawn"); - rusqlite::ffi::install_opfs_sahpool( + match rusqlite::ffi::install_opfs_sahpool( Some(&rusqlite::ffi::OpfsSAHPoolCfg::default()), false, ) .await - .unwrap(); + { + Ok(_) => {} + Err(e) => { + use crate::error::OpfsSAHError; + + let error: OpfsSAHError = e.into(); + result_send.send(Err(error.into())); + return; + } + } debug!("opfs installed"); let file_name = format!("file:{}?vfs=opfs-sahpool", file_name.as_ref()); let result = DbActor::new(file_name, receiver); diff --git a/filamento/src/error.rs b/filamento/src/error.rs index 721d532..fb7d778 100644 --- a/filamento/src/error.rs +++ b/filamento/src/error.rs @@ -293,6 +293,9 @@ pub enum IqProcessError { #[derive(Debug, Error, Clone)] pub enum DatabaseOpenError { + #[cfg(target_arch = "wasm32")] + #[error("opfs: {0}")] + OpfsSAH(#[from] OpfsSAHError), #[error("error: {0}")] Error(Arc<rusqlite::Error>), // #[error("migration: {0}")] @@ -311,6 +314,117 @@ pub enum DatabaseOpenError { // } // } +#[cfg(target_arch = "wasm32")] +impl From<rusqlite::ffi::OpfsSAHError> for OpfsSAHError { + fn from(e: rusqlite::ffi::OpfsSAHError) -> Self { + use wasm_bindgen::UnwrapThrowExt; + match e { + rusqlite::ffi::OpfsSAHError::Vfs(_register_vfs_error) => Self::VfsRegistration, + rusqlite::ffi::OpfsSAHError::ImportDb(_import_db_error) => Self::ImportDb, + rusqlite::ffi::OpfsSAHError::NotSuported => Self::NotSupported, + rusqlite::ffi::OpfsSAHError::GetDirHandle(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::GetDirHandle(message) + } + rusqlite::ffi::OpfsSAHError::GetFileHandle(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::GetFileHandle(message) + } + rusqlite::ffi::OpfsSAHError::CreateSyncAccessHandle(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::CreateSyncAccessHandle(message) + } + rusqlite::ffi::OpfsSAHError::IterHandle(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::IterHandle(message) + } + rusqlite::ffi::OpfsSAHError::GetPath(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::GetPath(message) + } + rusqlite::ffi::OpfsSAHError::RemoveEntity(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::RemoveEntity(message) + } + rusqlite::ffi::OpfsSAHError::GetSize(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::GetSize(message) + } + rusqlite::ffi::OpfsSAHError::Read(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::Read(message) + } + rusqlite::ffi::OpfsSAHError::Write(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::Write(message) + } + rusqlite::ffi::OpfsSAHError::Flush(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::Flush(message) + } + rusqlite::ffi::OpfsSAHError::Truncate(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::Truncate(message) + } + rusqlite::ffi::OpfsSAHError::Reflect(js_value) => { + let error: js_sys::Error = js_value.into(); + let message = error.message().as_string().unwrap_throw(); + Self::Reflect(message) + } + rusqlite::ffi::OpfsSAHError::Generic(s) => Self::Generic(s), + rusqlite::ffi::OpfsSAHError::Custom(s) => Self::Generic(s), + } + } +} + +#[cfg(target_arch = "wasm32")] +#[derive(Debug, Error, Clone)] +pub enum OpfsSAHError { + #[error("VFS registration")] + VfsRegistration, + #[error("import db error")] + ImportDb, + #[error("not supported")] + NotSupported, + #[error("get dir handle: {0}")] + GetDirHandle(String), + #[error("get file handle: {0}")] + GetFileHandle(String), + #[error("create sync access handle: {0}")] + CreateSyncAccessHandle(String), + #[error("iter handle: {0}")] + IterHandle(String), + #[error("get path: {0}")] + GetPath(String), + #[error("remove entity: {0}")] + RemoveEntity(String), + #[error("get size: {0}")] + GetSize(String), + #[error("read: {0}")] + Read(String), + #[error("write: {0}")] + Write(String), + #[error("flush: {0}")] + Flush(String), + #[error("truncate: {0}")] + Truncate(String), + #[error("reflect: {0}")] + Reflect(String), + #[error("generic: {0}")] + Generic(String), +} + impl From<std::io::Error> for DatabaseOpenError { fn from(e: std::io::Error) -> Self { Self::Io(Arc::new(e)) diff --git a/filamento/src/files/opfs.rs b/filamento/src/files/opfs.rs index fb32c6e..0bcce35 100644 --- a/filamento/src/files/opfs.rs +++ b/filamento/src/files/opfs.rs @@ -10,7 +10,7 @@ use web_sys::{ use crate::FileStore; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FilesOPFS { directory: String, } diff --git a/filamento/src/logic/connect.rs b/filamento/src/logic/connect.rs index 9d61ca4..6e392f1 100644 --- a/filamento/src/logic/connect.rs +++ b/filamento/src/logic/connect.rs @@ -11,7 +11,7 @@ use crate::{ use super::ClientLogic; -pub async fn handle_connect<Fs: FileStore + Clone + Send + Sync>( +pub async fn handle_connect<Fs: FileStore + Clone + Send + Sync + 'static>( logic: ClientLogic<Fs>, connection: Connected, ) { diff --git a/filamento/src/logic/mod.rs b/filamento/src/logic/mod.rs index 146f3b0..ddf0343 100644 --- a/filamento/src/logic/mod.rs +++ b/filamento/src/logic/mod.rs @@ -127,7 +127,7 @@ impl<Fs: FileStore> ClientLogic<Fs> { } } -impl<Fs: FileStore + Clone + Send + Sync> Logic for ClientLogic<Fs> { +impl<Fs: FileStore + Clone + Send + Sync + 'static> Logic for ClientLogic<Fs> { type Cmd = Command<Fs>; // pub async fn handle_stream_error(self, error) {} diff --git a/filamento/src/logic/online.rs b/filamento/src/logic/online.rs index febd3e1..b36f9a9 100644 --- a/filamento/src/logic/online.rs +++ b/filamento/src/logic/online.rs @@ -11,7 +11,9 @@ use stanza::{ iq::{self, Iq, IqType, Query}, Stanza }, xep_0030::{info, items}, xep_0060::{self, owner, pubsub::{self, Pubsub}}, xep_0084, xep_0172::{self, Nick}, xep_0203::Delay }; -use tokio::sync::oneshot; +use tokio::{sync::oneshot, task::spawn_blocking}; +#[cfg(target_arch = "wasm32")] +use tokio_with_wasm::alias as tokio; use tracing::{debug, error, info}; use uuid::Uuid; @@ -27,7 +29,7 @@ use super::{ }, ClientLogic }; -pub async fn handle_online<Fs: FileStore + Clone>(logic: ClientLogic<Fs>, command: Command<Fs>, connection: Connected) { +pub async fn handle_online<Fs: FileStore + Clone + 'static>(logic: ClientLogic<Fs>, command: Command<Fs>, connection: Connected) { let result = handle_online_result(&logic, command, connection).await; match result { Ok(_) => {} @@ -288,12 +290,9 @@ pub async fn handle_accept_subscription_request<Fs: FileStore + Clone>( connection: Connected, jid: BareJID, ) -> Result<(), SubscribeError> { - let client_user = logic.db.read_user(logic.jid.clone()).await?; - let nick = client_user.nick.map(|nick| Nick(nick)); let presence = Stanza::Presence(stanza::client::presence::Presence { to: Some(jid.into()), - r#type: Some(stanza::client::presence::PresenceType::Subscribe), - nick, + r#type: Some(stanza::client::presence::PresenceType::Subscribed), ..Default::default() }); connection.write_handle().write(presence).await?; @@ -973,29 +972,33 @@ pub async fn handle_change_nick<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>, Ok(()) } -pub async fn handle_change_avatar<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>, img_data: Option<Vec<u8>>) -> Result<(), AvatarPublishError<Fs>> { +pub async fn handle_change_avatar<Fs: FileStore + Clone + 'static>(logic: &ClientLogic<Fs>, img_data: Option<Vec<u8>>) -> Result<(), AvatarPublishError<Fs>> { match img_data { // set avatar Some(data) => { - // load the image data and guess the format - let image = ImageReader::new(Cursor::new(data)).with_guessed_format()?.decode()?; + let (bytes, hash, data_png, data_b64) = spawn_blocking(move || -> Result<_, _> { + // load the image data and guess the format + let image = ImageReader::new(Cursor::new(data)).with_guessed_format()?.decode()?; + + // convert the image to png; + let mut data_png = Vec::new(); + let image = image.resize(192, 192, image::imageops::FilterType::Nearest); + image.write_to(&mut Cursor::new(&mut data_png), image::ImageFormat::Jpeg)?; - // convert the image to png; - let mut data_png = Vec::new(); - let image = image.resize(192, 192, image::imageops::FilterType::Nearest); - image.write_to(&mut Cursor::new(&mut data_png), image::ImageFormat::Jpeg)?; + // calculate the length of the data in bytes. + let bytes = data_png.len().try_into()?; - // calculate the length of the data in bytes. - let bytes = data_png.len().try_into()?; + // calculate sha1 hash of the data + let mut sha1 = Sha1::new(); + sha1.update(&data_png); + let sha1_result = sha1.finalize(); + let hash = hex::encode(sha1_result); - // calculate sha1 hash of the data - let mut sha1 = Sha1::new(); - sha1.update(&data_png); - let sha1_result = sha1.finalize(); - let hash = hex::encode(sha1_result); + // encode the image data as base64 + let data_b64 = BASE64_STANDARD.encode(data_png.clone()); - // encode the image data as base64 - let data_b64 = BASE64_STANDARD.encode(data_png.clone()); + Ok::<(u32, String, Vec<u8>, String), AvatarPublishError<Fs>>((bytes, hash, data_png, data_b64)) + }).await.unwrap()?; // publish the data to the data node logic.client().publish(pep::Item::AvatarData(Some(avatar::Data { hash: hash.clone(), data_b64 })), "urn:xmpp:avatar:data".to_string()).await?; @@ -1081,7 +1084,7 @@ pub async fn handle_delete_pep_node<Fs: FileStore + Clone>( } // TODO: could probably macro-ise? -pub async fn handle_online_result<Fs: FileStore + Clone>( +pub async fn handle_online_result<Fs: FileStore + Clone + 'static>( logic: &ClientLogic<Fs>, command: Command<Fs>, connection: Connected, |