From 6912ad6f0532f8c658648edeb072c88b06271a6f Mon Sep 17 00:00:00 2001 From: trivernis Date: Sun, 24 Oct 2021 12:29:43 +0200 Subject: [PATCH] Add commands to repo plugin Signed-off-by: trivernis --- mediarepo-api/Cargo.toml | 9 ++- mediarepo-api/src/client_api/mod.rs | 7 +++ .../src/tauri_plugin/commands/file.rs | 61 +++++++++++++++++++ .../src/tauri_plugin/commands/mod.rs | 20 ++++++ .../src/tauri_plugin/custom_schemes.rs | 25 ++++++++ mediarepo-api/src/tauri_plugin/error.rs | 25 ++++++++ mediarepo-api/src/tauri_plugin/mod.rs | 61 +++++++++++++++++++ mediarepo-api/src/tauri_plugin/state.rs | 53 ++++++++++++++++ 8 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 mediarepo-api/src/tauri_plugin/commands/file.rs create mode 100644 mediarepo-api/src/tauri_plugin/commands/mod.rs create mode 100644 mediarepo-api/src/tauri_plugin/custom_schemes.rs create mode 100644 mediarepo-api/src/tauri_plugin/error.rs create mode 100644 mediarepo-api/src/tauri_plugin/state.rs diff --git a/mediarepo-api/Cargo.toml b/mediarepo-api/Cargo.toml index fe61269..668c4ad 100644 --- a/mediarepo-api/Cargo.toml +++ b/mediarepo-api/Cargo.toml @@ -11,6 +11,8 @@ tracing = "0.1.29" thiserror = "1.0.30" async-trait = {version = "0.1.51", optional=true} rmp-ipc = {version = "0.7.2", optional=true} +parking_lot = {version="0.11.2", optional=true} +serde_json = {version="1.0.68", optional=true} [dependencies.serde] version = "1.0.130" @@ -26,6 +28,11 @@ optional=true default-features = false features = [] +[dependencies.tokio] +version = "1.12.0" +optional = true +features = ["sync"] + [features] -tauri-plugin = ["client-api","tauri", "rmp-ipc"] +tauri-plugin = ["client-api","tauri", "rmp-ipc", "parking_lot", "serde_json"] client-api = ["rmp-ipc", "async-trait"] \ No newline at end of file diff --git a/mediarepo-api/src/client_api/mod.rs b/mediarepo-api/src/client_api/mod.rs index fc55b41..62ade72 100644 --- a/mediarepo-api/src/client_api/mod.rs +++ b/mediarepo-api/src/client_api/mod.rs @@ -65,6 +65,7 @@ impl ApiClient { } /// Connects to the ipc Socket + #[tracing::instrument(level = "debug")] pub async fn connect(address: &str) -> ApiResult { let ctx = IPCBuilder::new().address(address).build_client().await?; @@ -83,4 +84,10 @@ impl ApiClient { .await?; Ok(res.data::()?) } + + #[tracing::instrument(level = "debug", skip(self))] + pub async fn exit(self) -> ApiResult<()> { + self.ctx.stop().await?; + Ok(()) + } } diff --git a/mediarepo-api/src/tauri_plugin/commands/file.rs b/mediarepo-api/src/tauri_plugin/commands/file.rs new file mode 100644 index 0000000..535fc98 --- /dev/null +++ b/mediarepo-api/src/tauri_plugin/commands/file.rs @@ -0,0 +1,61 @@ +use crate::tauri_plugin::commands::{add_once_buffer, ApiAccess, BufferAccess}; +use crate::tauri_plugin::error::PluginResult; +use crate::types::files::{FileMetadataResponse, ThumbnailMetadataResponse}; + +#[tauri::command] +pub async fn get_all_files(api_state: ApiAccess<'_>) -> PluginResult> { + let api = api_state.api().await?; + let all_files = api.file.all_files().await?; + + Ok(all_files) +} + +#[tauri::command] +pub async fn find_files( + tags: Vec, + api_state: ApiAccess<'_>, +) -> PluginResult> { + let api = api_state.api().await?; + let files = api.file.find_files(tags).await?; + + Ok(files) +} + +#[tauri::command] +pub async fn read_file_by_hash( + hash: String, + mime_type: String, + api_state: ApiAccess<'_>, + buffer_state: BufferAccess<'_>, +) -> PluginResult { + let api = api_state.api().await?; + let content = api.file.read_file_by_hash(hash.clone()).await?; + let uri = add_once_buffer(buffer_state, hash, mime_type, content); + + Ok(uri) +} + +#[tauri::command] +pub async fn get_file_thumbnails( + hash: String, + api_state: ApiAccess<'_>, +) -> PluginResult> { + let api = api_state.api().await?; + let thumbs = api.file.get_file_thumbnails(hash).await?; + + Ok(thumbs) +} + +#[tauri::command] +pub async fn read_thumbnail( + hash: String, + mime_type: String, + api_state: ApiAccess<'_>, + buffer_state: BufferAccess<'_>, +) -> PluginResult { + let api = api_state.api().await?; + let content = api.file.read_thumbnail(hash.clone()).await?; + let uri = add_once_buffer(buffer_state, hash, mime_type, content); + + Ok(uri) +} diff --git a/mediarepo-api/src/tauri_plugin/commands/mod.rs b/mediarepo-api/src/tauri_plugin/commands/mod.rs new file mode 100644 index 0000000..f6b415f --- /dev/null +++ b/mediarepo-api/src/tauri_plugin/commands/mod.rs @@ -0,0 +1,20 @@ +use tauri::State; + +pub use file::*; + +use crate::tauri_plugin::state::{ApiState, BufferState, OnceBuffer}; + +pub mod file; + +pub type ApiAccess<'a> = State<'a, ApiState>; +pub type BufferAccess<'a> = State<'a, BufferState>; + +/// Adds a once-buffer to the buffer store +fn add_once_buffer(buffer_state: BufferAccess, key: String, mime: String, buf: Vec) -> String { + let uri = format!("once://{}", key); + let once_buffer = OnceBuffer::new(mime, buf); + let mut once_buffers = buffer_state.buffer.lock(); + once_buffers.insert(key, once_buffer); + + uri +} diff --git a/mediarepo-api/src/tauri_plugin/custom_schemes.rs b/mediarepo-api/src/tauri_plugin/custom_schemes.rs new file mode 100644 index 0000000..219d3b0 --- /dev/null +++ b/mediarepo-api/src/tauri_plugin/custom_schemes.rs @@ -0,0 +1,25 @@ +use crate::tauri_plugin::state::BufferState; +use tauri::http::ResponseBuilder; +use tauri::{Builder, Manager, Runtime}; + +pub fn register_custom_uri_schemes(builder: Builder) -> Builder { + builder.register_uri_scheme_protocol("once", |app, request| { + let buf_state = app.state::(); + let resource_key = request.uri().trim_start_matches("once://"); + let buffer = { + let mut buffers = buf_state.buffer.lock(); + buffers.remove(resource_key) + }; + if let Some(buffer) = buffer { + ResponseBuilder::new() + .mimetype(&buffer.mime) + .status(200) + .body(buffer.buf) + } else { + ResponseBuilder::new() + .mimetype("text/plain") + .status(404) + .body("Resource not found".as_bytes().to_vec()) + } + }) +} diff --git a/mediarepo-api/src/tauri_plugin/error.rs b/mediarepo-api/src/tauri_plugin/error.rs new file mode 100644 index 0000000..7fc37b0 --- /dev/null +++ b/mediarepo-api/src/tauri_plugin/error.rs @@ -0,0 +1,25 @@ +use crate::client_api::error::ApiError; +use serde::Serialize; + +pub type PluginResult = Result; + +#[derive(Clone, Serialize)] +pub struct PluginError { + message: String, +} + +impl From<&str> for PluginError { + fn from(s: &str) -> Self { + Self { + message: s.to_string(), + } + } +} + +impl From for PluginError { + fn from(e: ApiError) -> Self { + Self { + message: format!("ApiError: {:?}", e), + } + } +} diff --git a/mediarepo-api/src/tauri_plugin/mod.rs b/mediarepo-api/src/tauri_plugin/mod.rs index e69de29..3fa250f 100644 --- a/mediarepo-api/src/tauri_plugin/mod.rs +++ b/mediarepo-api/src/tauri_plugin/mod.rs @@ -0,0 +1,61 @@ +use tauri::plugin::Plugin; +use tauri::{AppHandle, Builder, Invoke, Manager, Runtime}; + +use state::ApiState; + +use crate::tauri_plugin::state::BufferState; + +mod commands; +pub mod custom_schemes; +pub mod error; +mod state; + +use commands::*; + +pub fn register_plugin(builder: Builder) -> Builder { + let repo_plugin = MediarepoPlugin::new(); + + custom_schemes::register_custom_uri_schemes(builder.plugin(repo_plugin)) +} + +pub struct MediarepoPlugin { + invoke_handler: Box) + Send + Sync>, +} + +impl MediarepoPlugin { + pub fn new() -> Self { + Self { + invoke_handler: Box::new(tauri::generate_handler![ + get_all_files, + find_files, + read_file_by_hash, + get_file_thumbnails, + read_thumbnail + ]), + } + } +} + +impl Plugin for MediarepoPlugin { + fn name(&self) -> &'static str { + "mediarepo" + } + + fn initialize( + &mut self, + app: &AppHandle, + _config: serde_json::value::Value, + ) -> tauri::plugin::Result<()> { + let api_state = ApiState::new(); + app.manage(api_state); + + let buffer_state = BufferState::default(); + app.manage(buffer_state); + + Ok(()) + } + + fn extend_api(&mut self, message: Invoke) { + (self.invoke_handler)(message) + } +} diff --git a/mediarepo-api/src/tauri_plugin/state.rs b/mediarepo-api/src/tauri_plugin/state.rs new file mode 100644 index 0000000..307c814 --- /dev/null +++ b/mediarepo-api/src/tauri_plugin/state.rs @@ -0,0 +1,53 @@ +use std::collections::HashMap; +use std::mem; +use std::sync::Arc; + +use parking_lot::Mutex; +use tauri::async_runtime::RwLock; + +use crate::client_api::ApiClient; +use crate::tauri_plugin::error::{PluginError, PluginResult}; + +pub struct ApiState { + inner: Arc>>, +} + +impl ApiState { + pub fn new() -> Self { + Self { + inner: Arc::new(RwLock::new(None)), + } + } + + pub async fn set_api(&self, client: ApiClient) { + let mut inner = self.inner.write().await; + let old_client = mem::replace(&mut *inner, Some(client)); + + if let Some(client) = old_client { + let _ = client.exit().await; + } + } + + pub async fn api(&self) -> PluginResult { + let inner = self.inner.read().await; + inner + .clone() + .ok_or_else(|| PluginError::from("Not connected")) + } +} + +pub struct OnceBuffer { + pub mime: String, + pub buf: Vec, +} + +impl OnceBuffer { + pub fn new(mime: String, buf: Vec) -> Self { + Self { mime, buf } + } +} + +#[derive(Default)] +pub struct BufferState { + pub buffer: Arc>>, +}