Add daemon management commands

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent aab7f71e12
commit 305ac256d1

@ -32,7 +32,7 @@ features = []
[dependencies.tokio] [dependencies.tokio]
version = "1.12.0" version = "1.12.0"
optional = true optional = true
features = ["sync", "fs", "net", "io-util", "io-std", "time", "rt"] features = ["sync", "fs", "net", "io-util", "io-std", "time", "rt", "process"]
[dependencies.toml] [dependencies.toml]
version = "0.5.8" version = "0.5.8"

@ -0,0 +1,82 @@
use crate::daemon_management::error::{DaemonError, DaemonResult};
use std::ffi::OsStr;
use tokio::process::{Child, Command};
#[derive(Debug)]
pub struct DaemonCli {
daemon_path: String,
repo_path: String,
child: Option<Child>,
}
impl DaemonCli {
pub fn new(daemon_path: String, repo_path: String) -> Self {
Self {
daemon_path,
repo_path,
child: None,
}
}
/// Initializes a repository at the specified path
#[tracing::instrument]
pub async fn init_repo(&self) -> DaemonResult<()> {
let output = self
.run_command(vec!["--repo", self.repo_path.as_str(), "init"])
.await?;
tracing::debug!("Response: {}", String::from_utf8(output).unwrap());
Ok(())
}
/// Starts a daemon for the given repository
#[tracing::instrument]
pub fn start_daemon(&mut self) -> DaemonResult<()> {
let child = self.run_daemon_process(vec!["--repo", self.repo_path.as_str(), "start"])?;
self.child = Some(child);
Ok(())
}
/// Returns if the daemon is currently running
pub fn daemon_running(&mut self) -> bool {
if let Some(child) = &mut self.child {
child.try_wait().map(|e| e.is_some()).unwrap_or(true)
} else {
false
}
}
/// Returns the path the daemon is serving
pub fn repo_path(&self) -> &String {
&self.repo_path
}
/// Runs a daemon subcommand
async fn run_command<S: AsRef<OsStr>, I: IntoIterator<Item = S>>(
&self,
args: I,
) -> DaemonResult<Vec<u8>> {
let child = self.run_daemon_process(args)?;
let output = child.wait_with_output().await?;
if output.status.success() {
Ok(output.stdout)
} else {
let stdout = String::from_utf8(output.stdout).map_err(|e| e.to_string())?;
let stderr = String::from_utf8(output.stderr).map_err(|e| e.to_string())?;
Err(DaemonError::from(format!("{}\n{}", stdout, stderr)))
}
}
/// Runs a daemon process with the given args
fn run_daemon_process<S: AsRef<OsStr>, I: IntoIterator<Item = S>>(
&self,
args: I,
) -> DaemonResult<Child> {
Command::new(&self.daemon_path)
.args(args)
.spawn()
.map_err(DaemonError::from)
}
}

@ -0,0 +1,30 @@
use std::fmt::{Display, Formatter};
pub type DaemonResult<T> = Result<T, DaemonError>;
#[derive(Debug)]
pub struct DaemonError {
pub message: String,
}
impl Display for DaemonError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.message.fmt(f)
}
}
impl std::error::Error for DaemonError {}
impl From<std::io::Error> for DaemonError {
fn from(e: std::io::Error) -> Self {
Self {
message: e.to_string(),
}
}
}
impl From<String> for DaemonError {
fn from(s: String) -> Self {
Self { message: s }
}
}

@ -0,0 +1,2 @@
pub mod cli;
pub mod error;

@ -3,5 +3,8 @@ pub mod types;
#[cfg(feature = "client-api")] #[cfg(feature = "client-api")]
pub mod client_api; pub mod client_api;
#[cfg(feature = "client-api")]
pub mod daemon_management;
#[cfg(feature = "tauri-plugin")] #[cfg(feature = "tauri-plugin")]
pub mod tauri_plugin; pub mod tauri_plugin;

@ -0,0 +1,25 @@
use crate::client_api::ApiClient;
use crate::tauri_plugin::commands::AppAccess;
use crate::tauri_plugin::error::PluginResult;
#[tauri::command]
pub async fn init_repository(app_state: AppAccess<'_>, repo_path: String) -> PluginResult<()> {
let daemon = app_state.get_daemon_cli(repo_path).await;
daemon.init_repo().await?;
Ok(())
}
#[tauri::command]
pub async fn start_daemon(app_state: AppAccess<'_>, repo_path: String) -> PluginResult<()> {
let mut daemon = app_state.get_daemon_cli(repo_path).await;
daemon.start_daemon()?;
app_state.add_started_daemon(daemon).await;
Ok(())
}
#[tauri::command]
pub async fn check_daemon_running(address: String) -> PluginResult<bool> {
Ok(ApiClient::connect(&address).await.is_ok())
}

@ -1,12 +1,14 @@
use parking_lot::lock_api::Mutex; use parking_lot::lock_api::Mutex;
use tauri::State; use tauri::State;
pub use daemon::*;
pub use file::*; pub use file::*;
pub use repo::*; pub use repo::*;
pub use tag::*; pub use tag::*;
use crate::tauri_plugin::state::{ApiState, AppState, BufferState, VolatileBuffer}; use crate::tauri_plugin::state::{ApiState, AppState, BufferState, VolatileBuffer};
pub mod daemon;
pub mod file; pub mod file;
pub mod repo; pub mod repo;
pub mod tag; pub mod tag;

@ -1,4 +1,5 @@
use crate::client_api::error::ApiError; use crate::client_api::error::ApiError;
use crate::daemon_management::error::DaemonError;
use rmp_ipc::error::Error; use rmp_ipc::error::Error;
use serde::Serialize; use serde::Serialize;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -67,3 +68,9 @@ impl From<toml::ser::Error> for PluginError {
} }
} }
} }
impl From<DaemonError> for PluginError {
fn from(e: DaemonError) -> Self {
Self { message: e.message }
}
}

@ -39,7 +39,10 @@ impl<R: Runtime> MediarepoPlugin<R> {
get_tags_for_file, get_tags_for_file,
get_active_repository, get_active_repository,
add_repository, add_repository,
select_repository select_repository,
init_repository,
start_daemon,
check_daemon_running,
]), ]),
} }
} }
@ -61,14 +64,15 @@ impl<R: Runtime> Plugin<R> for MediarepoPlugin<R> {
let buffer_state = BufferState::default(); let buffer_state = BufferState::default();
app.manage(buffer_state.clone()); app.manage(buffer_state.clone());
let repo_state = AppState::load()?;
app.manage(repo_state);
thread::spawn(move || loop { thread::spawn(move || loop {
thread::sleep(Duration::from_secs(10)); thread::sleep(Duration::from_secs(10));
buffer_state.clear_expired(); buffer_state.clear_expired();
}); });
let repo_state = AppState::load()?;
app.manage(repo_state);
Ok(()) Ok(())
} }

@ -14,11 +14,21 @@ pub struct Repository {
pub(crate) address: String, pub(crate) address: String,
} }
#[derive(Default, Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Settings { pub struct Settings {
pub daemon_path: String,
pub repositories: HashMap<String, Repository>, pub repositories: HashMap<String, Repository>,
} }
impl Default for Settings {
fn default() -> Self {
Self {
daemon_path: String::from("mediarepo-daemon"),
repositories: HashMap::new(),
}
}
}
fn get_settings_path() -> PathBuf { fn get_settings_path() -> PathBuf {
let dirs = ProjectDirs::from("com", "trivernis", "mediarepo").unwrap(); let dirs = ProjectDirs::from("com", "trivernis", "mediarepo").unwrap();
let config_path = dirs.config_dir().to_path_buf(); let config_path = dirs.config_dir().to_path_buf();

@ -9,6 +9,7 @@ use tauri::async_runtime::RwLock;
use tokio::time::Instant; use tokio::time::Instant;
use crate::client_api::ApiClient; use crate::client_api::ApiClient;
use crate::daemon_management::cli::DaemonCli;
use crate::tauri_plugin::error::{PluginError, PluginResult}; use crate::tauri_plugin::error::{PluginError, PluginResult};
use crate::tauri_plugin::settings::{load_settings, Repository, Settings}; use crate::tauri_plugin::settings::{load_settings, Repository, Settings};
@ -117,6 +118,7 @@ impl BufferState {
pub struct AppState { pub struct AppState {
pub active_repo: Arc<RwLock<Option<Repository>>>, pub active_repo: Arc<RwLock<Option<Repository>>>,
pub settings: Arc<RwLock<Settings>>, pub settings: Arc<RwLock<Settings>>,
pub running_daemons: Arc<RwLock<HashMap<String, DaemonCli>>>,
} }
impl AppState { impl AppState {
@ -125,10 +127,25 @@ impl AppState {
let settings = load_settings()?; let settings = load_settings()?;
let state = Self { let state = Self {
active_repo: Arc::new(RwLock::new(None)), active_repo: Default::default(),
settings: Arc::new(RwLock::new(settings)), settings: Arc::new(RwLock::new(settings)),
running_daemons: Default::default(),
}; };
Ok(state) Ok(state)
} }
/// Returns the daemon cli client
pub async fn get_daemon_cli(&self, repo_path: String) -> DaemonCli {
let settings = self.settings.read().await;
let path = settings.daemon_path.clone();
DaemonCli::new(path, repo_path)
}
/// Adds a started daemon to the running daemons
pub async fn add_started_daemon(&self, daemon: DaemonCli) {
let mut daemons = self.running_daemons.write().await;
daemons.insert(daemon.repo_path().to_owned(), daemon);
}
} }

Loading…
Cancel
Save