Move commands to api crate
Signed-off-by: trivernis <trivernis@protonmail.com>pull/4/head
parent
8fbc4436e3
commit
c19423a271
File diff suppressed because it is too large
Load Diff
@ -1,92 +0,0 @@
|
|||||||
use crate::commands::{add_once_buffer, get_ipc};
|
|
||||||
use mediarepo::requests::{FindFilesByTagsRequest, GetFileThumbnailsRequest, ReadFileRequest};
|
|
||||||
use mediarepo::responses::{FileResponse, ThumbnailResponse};
|
|
||||||
|
|
||||||
use crate::context::Context;
|
|
||||||
use crate::error::AppResult;
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_all_files(context: tauri::State<'_, Context>) -> AppResult<Vec<FileResponse>> {
|
|
||||||
let ipc = get_ipc(&context).await?;
|
|
||||||
let response = ipc
|
|
||||||
.emitter
|
|
||||||
.emit_to("files", "all_files", ())
|
|
||||||
.await?
|
|
||||||
.await_reply(&ipc)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(response.data::<Vec<FileResponse>>()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn find_files(
|
|
||||||
tags: Vec<String>,
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<Vec<FileResponse>> {
|
|
||||||
let ipc = get_ipc(&context).await?;
|
|
||||||
let response = ipc
|
|
||||||
.emitter
|
|
||||||
.emit_to("files", "find_files", FindFilesByTagsRequest { tags })
|
|
||||||
.await?
|
|
||||||
.await_reply(&ipc)
|
|
||||||
.await?;
|
|
||||||
Ok(response.data::<Vec<FileResponse>>()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn read_file_by_hash(
|
|
||||||
hash: String,
|
|
||||||
mime: String,
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<String> {
|
|
||||||
let ipc = get_ipc(&context).await?;
|
|
||||||
let response = ipc
|
|
||||||
.emitter
|
|
||||||
.emit_to("files", "read_file", ReadFileRequest::Hash(hash.clone()))
|
|
||||||
.await?
|
|
||||||
.await_reply(&ipc)
|
|
||||||
.await?;
|
|
||||||
let raw_data = response.data_raw().to_vec();
|
|
||||||
let uri = add_once_buffer(&context, hash, mime, raw_data);
|
|
||||||
|
|
||||||
Ok(uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_thumbnails(
|
|
||||||
hash: String,
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<Vec<ThumbnailResponse>> {
|
|
||||||
let ipc = get_ipc(&context).await?;
|
|
||||||
let response = ipc
|
|
||||||
.emitter
|
|
||||||
.emit_to(
|
|
||||||
"files",
|
|
||||||
"get_thumbnails",
|
|
||||||
GetFileThumbnailsRequest::Hash(hash),
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
.await_reply(&ipc)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(response.data::<Vec<ThumbnailResponse>>()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn read_thumbnail(
|
|
||||||
hash: String,
|
|
||||||
mime: String,
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<String> {
|
|
||||||
let ipc = get_ipc(&context).await?;
|
|
||||||
let response = ipc
|
|
||||||
.emitter
|
|
||||||
.emit_to("files", "read_thumbnail", hash.clone())
|
|
||||||
.await?
|
|
||||||
.await_reply(&ipc)
|
|
||||||
.await?;
|
|
||||||
let raw_data = response.data_raw().to_vec();
|
|
||||||
let uri = add_once_buffer(&context, hash, mime, raw_data);
|
|
||||||
|
|
||||||
Ok(uri)
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
use rmp_ipc::ipc::context::Context as IPCContext;
|
|
||||||
|
|
||||||
use crate::context::{Context, OnceBuffer};
|
|
||||||
use crate::error::{AppError, AppResult};
|
|
||||||
|
|
||||||
pub mod files;
|
|
||||||
pub mod repo;
|
|
||||||
pub mod tags;
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn emit_info(context: tauri::State<'_, Context>) -> AppResult<()> {
|
|
||||||
let ipc = context.ipc.read().await;
|
|
||||||
if let Some(ipc) = &*ipc {
|
|
||||||
ipc.emitter.emit("info", ()).await?;
|
|
||||||
println!("Emitted info event.");
|
|
||||||
} else {
|
|
||||||
println!("No IPC Context");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipc(context: &tauri::State<'_, Context>) -> AppResult<IPCContext> {
|
|
||||||
let ipc = context.ipc.read().await;
|
|
||||||
(ipc.clone()).ok_or(AppError::new("No ipc connection."))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a once-buffer to the buffer store
|
|
||||||
pub fn add_once_buffer(
|
|
||||||
context: &tauri::State<'_, Context>,
|
|
||||||
key: String,
|
|
||||||
mime: String,
|
|
||||||
buf: Vec<u8>,
|
|
||||||
) -> String {
|
|
||||||
let uri = format!("once://{}", key);
|
|
||||||
let once_buffer = OnceBuffer { mime, buf };
|
|
||||||
let mut once_buffers = context.once_buffers.lock().unwrap();
|
|
||||||
once_buffers.insert(key, once_buffer);
|
|
||||||
|
|
||||||
uri
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
use crate::context::Context;
|
|
||||||
use crate::error::{AppError, AppResult};
|
|
||||||
use crate::ipc::build_ipc_context;
|
|
||||||
use crate::settings::save_settings;
|
|
||||||
use rmp_ipc::ipc::context::Context as IPCContext;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::mem;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tauri::Window;
|
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
static REPO_CONFIG_FILE: &str = "repo.toml";
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
pub struct Repository {
|
|
||||||
name: String,
|
|
||||||
path: Option<String>,
|
|
||||||
address: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
pub struct RepoConfig {
|
|
||||||
pub listen_address: String,
|
|
||||||
pub database_path: String,
|
|
||||||
pub default_file_store: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_repositories(context: tauri::State<'_, Context>) -> AppResult<Vec<Repository>> {
|
|
||||||
let settings = context.settings.read().await;
|
|
||||||
|
|
||||||
Ok(settings.repositories.values().cloned().collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_active_repository(
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<Option<Repository>> {
|
|
||||||
let repo = context.active_repository.read().await;
|
|
||||||
Ok(repo.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn add_repository(
|
|
||||||
name: String,
|
|
||||||
path: String,
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<Vec<Repository>> {
|
|
||||||
let repo_path = path.clone();
|
|
||||||
let path = PathBuf::from(path);
|
|
||||||
let RepoConfig { listen_address, .. } = read_repo_config(path.join(REPO_CONFIG_FILE)).await?;
|
|
||||||
|
|
||||||
let repo = Repository {
|
|
||||||
name,
|
|
||||||
path: Some(repo_path),
|
|
||||||
address: listen_address,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut repositories = Vec::new();
|
|
||||||
{
|
|
||||||
let mut settings = context.settings.write().await;
|
|
||||||
settings.repositories.insert(repo.name.clone(), repo);
|
|
||||||
save_settings(&settings)?;
|
|
||||||
repositories.append(&mut settings.repositories.values().cloned().collect());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(repositories)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn select_repository(
|
|
||||||
window: Window,
|
|
||||||
name: String,
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<()> {
|
|
||||||
let settings = context.settings.read().await;
|
|
||||||
let repo = settings
|
|
||||||
.repositories
|
|
||||||
.get(&name)
|
|
||||||
.ok_or(AppError::new(format!("Repository '{}' not found", name)))?;
|
|
||||||
let ipc = connect(window, &repo.address).await?;
|
|
||||||
let mut ipc_ctx = context.ipc.write().await;
|
|
||||||
let old_ipc = mem::replace(&mut *ipc_ctx, Some(ipc));
|
|
||||||
|
|
||||||
if let Some(old_ctx) = old_ipc {
|
|
||||||
old_ctx.stop().await?;
|
|
||||||
}
|
|
||||||
let mut active_repo = context.active_repository.write().await;
|
|
||||||
*active_repo = Some(repo.clone());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_repo_config(path: PathBuf) -> AppResult<RepoConfig> {
|
|
||||||
let toml_str = fs::read_to_string(path).await?;
|
|
||||||
let config = toml::from_str(&toml_str)?;
|
|
||||||
|
|
||||||
Ok(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connects to the IPC Server
|
|
||||||
async fn connect(window: Window, address: &str) -> AppResult<IPCContext> {
|
|
||||||
build_ipc_context(window, address).await
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
use crate::commands::get_ipc;
|
|
||||||
use crate::context::Context;
|
|
||||||
use crate::error::AppResult;
|
|
||||||
use mediarepo::requests::FileIdentifier;
|
|
||||||
use mediarepo::responses::TagResponse;
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_tags_for_file(
|
|
||||||
hash: String,
|
|
||||||
context: tauri::State<'_, Context>,
|
|
||||||
) -> AppResult<Vec<TagResponse>> {
|
|
||||||
let ipc = get_ipc(&context).await?;
|
|
||||||
let response = ipc
|
|
||||||
.emitter
|
|
||||||
.emit_to("tags", "tags_for_file", FileIdentifier::Hash(hash))
|
|
||||||
.await?
|
|
||||||
.await_reply(&ipc)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(response.data::<Vec<TagResponse>>()?)
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
use crate::commands::repo::Repository;
|
|
||||||
use crate::settings::Settings;
|
|
||||||
use rmp_ipc::ipc::context::Context as IPCContext;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex as StdMutex;
|
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
pub struct OnceBuffer {
|
|
||||||
pub mime: String,
|
|
||||||
pub buf: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Context {
|
|
||||||
pub active_repository: Arc<RwLock<Option<Repository>>>,
|
|
||||||
pub ipc: Arc<RwLock<Option<IPCContext>>>,
|
|
||||||
pub settings: Arc<RwLock<Settings>>,
|
|
||||||
pub once_buffers: Arc<StdMutex<HashMap<String, OnceBuffer>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub fn new(settings: Settings) -> Self {
|
|
||||||
Self {
|
|
||||||
ipc: Arc::new(RwLock::new(None)),
|
|
||||||
active_repository: Arc::new(RwLock::new(None)),
|
|
||||||
settings: Arc::new(RwLock::new(settings)),
|
|
||||||
once_buffers: Arc::new(StdMutex::new(HashMap::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
pub type AppResult<T> = Result<T, AppError>;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct AppError {
|
|
||||||
message: String
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppError {
|
|
||||||
pub fn new<S: ToString>(msg: S) -> Self {
|
|
||||||
Self {
|
|
||||||
message: msg.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AppError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.message.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for AppError {}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for AppError {
|
|
||||||
fn from(e: std::io::Error) -> Self {
|
|
||||||
Self::new(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<toml::de::Error> for AppError {
|
|
||||||
fn from(e: toml::de::Error) -> Self {
|
|
||||||
Self::new(format!("Failed to deserialize toml: {:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<toml::ser::Error> for AppError {
|
|
||||||
fn from(e: toml::ser::Error) -> Self {
|
|
||||||
Self::new(format!("Failed to serialize to toml: {:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<rmp_ipc::error::Error> for AppError {
|
|
||||||
fn from(e: rmp_ipc::error::Error) -> Self {
|
|
||||||
Self::new(format!("Daemon Error: {:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<tauri::Error> for AppError {
|
|
||||||
fn from(e: tauri::Error) -> Self {
|
|
||||||
Self::new(format!("Tauri error: {:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AppError> for rmp_ipc::error::Error {
|
|
||||||
fn from(e: AppError) -> Self {
|
|
||||||
rmp_ipc::error::Error::Message(e.message)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
mod files;
|
|
||||||
|
|
||||||
use crate::error::AppResult;
|
|
||||||
use mediarepo::responses::InfoResponse;
|
|
||||||
use rmp_ipc::error::Result;
|
|
||||||
use rmp_ipc::error_event::{ErrorEventData, ERROR_EVENT_NAME};
|
|
||||||
use rmp_ipc::event::Event;
|
|
||||||
use rmp_ipc::ipc::context::Context as IPCContext;
|
|
||||||
use rmp_ipc::IPCBuilder;
|
|
||||||
use tauri::Window;
|
|
||||||
use typemap_rev::TypeMapKey;
|
|
||||||
|
|
||||||
pub struct WindowKey;
|
|
||||||
|
|
||||||
impl TypeMapKey for WindowKey {
|
|
||||||
type Value = Window;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn build_ipc_context(window: Window, address: &str) -> AppResult<IPCContext> {
|
|
||||||
let ctx = IPCBuilder::new()
|
|
||||||
.address(address)
|
|
||||||
.insert::<WindowKey>(window)
|
|
||||||
.on(ERROR_EVENT_NAME, |c, e| Box::pin(handle_error(c, e)))
|
|
||||||
.on("info", |c, e| Box::pin(handle_info(c, e)))
|
|
||||||
.build_client()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_error(ctx: &IPCContext, event: Event) -> Result<()> {
|
|
||||||
let error_data = event.data::<ErrorEventData>()?;
|
|
||||||
let data = ctx.data.read().await;
|
|
||||||
let window = data.get::<WindowKey>().unwrap();
|
|
||||||
window
|
|
||||||
.emit("error", error_data)
|
|
||||||
.expect("Failed to emit error event");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_info(ctx: &IPCContext, event: Event) -> Result<()> {
|
|
||||||
let info_data = event.data::<InfoResponse>()?;
|
|
||||||
let data = ctx.data.read().await;
|
|
||||||
let window = data.get::<WindowKey>().unwrap();
|
|
||||||
window
|
|
||||||
.emit("info", info_data)
|
|
||||||
.expect("Failed to emit info event");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use crate::commands::repo::Repository;
|
|
||||||
use crate::error::AppResult;
|
|
||||||
use directories::ProjectDirs;
|
|
||||||
use std::fs;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
static SETTINGS_FILE: &str = "settings.toml";
|
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
|
||||||
pub struct Settings {
|
|
||||||
pub repositories: HashMap<String, Repository>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_settings_path() -> PathBuf {
|
|
||||||
let dirs = ProjectDirs::from("com", "trivernis", "mediarepo").unwrap();
|
|
||||||
let config_path = dirs.config_dir().to_path_buf();
|
|
||||||
|
|
||||||
config_path.join(SETTINGS_FILE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the settings to the file
|
|
||||||
pub fn save_settings(settings: &Settings) -> AppResult<()> {
|
|
||||||
let settings_path = get_settings_path();
|
|
||||||
let settings_string = toml::to_string(&settings)?;
|
|
||||||
fs::write(&settings_path, &settings_string.into_bytes())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads the settings from the file
|
|
||||||
pub fn load_settings() -> AppResult<Settings> {
|
|
||||||
let dirs = ProjectDirs::from("com", "trivernis", "mediarepo").unwrap();
|
|
||||||
let config_path = dirs.config_dir().to_path_buf();
|
|
||||||
if !config_path.exists() {
|
|
||||||
fs::create_dir_all(&config_path)?;
|
|
||||||
}
|
|
||||||
let settings_path = config_path.join(SETTINGS_FILE);
|
|
||||||
if !settings_path.exists() {
|
|
||||||
let settings = Settings::default();
|
|
||||||
save_settings(&settings)?;
|
|
||||||
}
|
|
||||||
let config_str = fs::read_to_string(settings_path)?;
|
|
||||||
let settings = toml::from_str(&config_str)?;
|
|
||||||
|
|
||||||
Ok(settings)
|
|
||||||
}
|
|
Loading…
Reference in New Issue