diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..ed02dba --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,12 @@ +{ + "lsp": { + "rust-analyzer": { + "initialization_options": { + // Use `-p` instead of `--workspace` for cargo check + "check": { + "workspace": false + } + } + } + } +} diff --git a/clover-hub/src/main.rs b/clover-hub/src/main.rs index 10ff72e..c00dd13 100644 --- a/clover-hub/src/main.rs +++ b/clover-hub/src/main.rs @@ -8,10 +8,15 @@ mod server; mod tui; mod utils; -use log::{info, warn}; +use clap::{ + Arg, + Command, +}; use env_logger; -use tokio::time; -use tokio_util::sync::CancellationToken; +use log::{ + info, + warn, +}; use std::env; use std::error::Error; use std::ffi::OsString; @@ -19,7 +24,8 @@ use std::num::ParseIntError; use std::process::exit; use std::sync::Arc; use std::time::Duration; -use clap::{Arg, Command}; +use tokio::time; +use tokio_util::sync::CancellationToken; use crate::server::server_main; use crate::tui::tui_main; @@ -43,19 +49,41 @@ fn cli() -> Command { .subcommand(Command::new("aio").args(aio_args())) .subcommand( Command::new("server") - .arg(Arg::new("data_dir").short('d').long("data-dir").required(false).default_value("/opt/clover").help("The data directory to use other than `/opt/clover`")) - .arg(port_arg()) + .arg( + Arg::new("data_dir") + .short('d') + .long("data-dir") + .required(false) + .default_value("/opt/clover") + .help("The data directory to use other than `/opt/clover`"), + ) + .arg(port_arg()), ) .subcommand( Command::new("tui") - .arg(Arg::new("host").short('H').long("host").required(false).default_value("localhost").help("The host to connect to other than `localhost`")) - .arg(port_arg()) + .arg( + Arg::new("host") + .short('H') + .long("host") + .required(false) + .default_value("localhost") + .help("The host to connect to other than `localhost`"), + ) + .arg(port_arg()), ), ) } fn port_arg() -> Arg { - Arg::new("port").short('p').long("port").required(false).default_value(DEFAULT_PORT_STR).help(format!("The port on the host to connect to if not on {}.", DEFAULT_PORT_STR)) + Arg::new("port") + .short('p') + .long("port") + .required(false) + .default_value(DEFAULT_PORT_STR) + .help(format!( + "The port on the host to connect to if not on {}.", + DEFAULT_PORT_STR + )) } fn aio_args() -> Vec { @@ -64,15 +92,22 @@ fn aio_args() -> Vec { fn unwrap_port_arg(arg: Result) -> u16 { match arg { - Ok(val) => { val }, + Ok(val) => val, Err(e) => { - warn!("User-provided port did not parse correctly, using default, due to:\n{}", e); + warn!( + "User-provided port did not parse correctly, using default, due to:\n{}", + e + ); DEFAULT_PORT } } } -fn get_signal_handle(big_boy_token: CancellationToken, cancellation_token: CancellationToken, server_token: Option) -> tokio::task::JoinHandle<()> { +fn get_signal_handle( + big_boy_token: CancellationToken, + cancellation_token: CancellationToken, + server_token: Option, +) -> tokio::task::JoinHandle<()> { tokio::task::spawn(async move { tokio::select! { _ = wait_for_signal_impl(server_token.clone()) => { @@ -80,7 +115,7 @@ fn get_signal_handle(big_boy_token: CancellationToken, cancellation_token: Cance cancellation_token.cancel(); let mut token_long = Default::default(); - + tokio::select! { _ = wait_for_signal_impl(None) => { warn!("Forcibly exiting!"); @@ -101,7 +136,7 @@ fn get_signal_handle(big_boy_token: CancellationToken, cancellation_token: Cance } } } => {} - } + } } } }) @@ -112,7 +147,10 @@ fn get_signal_handle(big_boy_token: CancellationToken, cancellation_token: Cance #[cfg(unix)] async fn wait_for_signal_impl(server_token: Option) { use log::debug; - use tokio::signal::unix::{signal, SignalKind}; + use tokio::signal::unix::{ + SignalKind, + signal, + }; // Infos here: // https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html @@ -173,45 +211,78 @@ async fn run(big_boy_token: CancellationToken) { let cancellation_token = CancellationToken::new(); let from_server_token = CancellationToken::new(); let server_cancellation_token_clone = from_server_token.clone(); - let data_dir = sub_matches.get_one::("data_dir").expect("Default set in Clap."); - let port = unwrap_port_arg(sub_matches.get_one::("port").expect("Default set in Clap.").parse::()); + let data_dir = sub_matches + .get_one::("data_dir") + .expect("Default set in Clap."); + let port = unwrap_port_arg( + sub_matches + .get_one::("port") + .expect("Default set in Clap.") + .parse::(), + ); info!("Running Backend Server and Terminal UI (All-In-One)..."); let server_port = Arc::new(port); let server_token = cancellation_token.clone(); - let server_handle = tokio::task::spawn(async move { - server_main(data_dir, *server_port.to_owned(), server_token, from_server_token).await; + let server_handle = tokio::task::spawn(async move { + server_main( + data_dir, + *server_port.to_owned(), + server_token, + from_server_token, + ) + .await; }); let tui_port = Arc::new(port); let tui_token = cancellation_token.clone(); - let tui_handle = tokio::task::spawn(async move { - let _ = tui_main(*tui_port.to_owned(), Ok::("localhost".to_string()).ok(), tui_token).await; + let tui_handle = tokio::task::spawn(async move { + let _ = tui_main( + *tui_port.to_owned(), + Ok::("localhost".to_string()).ok(), + tui_token, + ) + .await; }); - let signal_handle = get_signal_handle(big_boy_token.clone(), cancellation_token, Some(server_cancellation_token_clone)); + let signal_handle = get_signal_handle( + big_boy_token.clone(), + cancellation_token, + Some(server_cancellation_token_clone), + ); tokio::select! {_ = futures::future::join_all(vec![signal_handle, tui_handle, server_handle]) => { info!("Exiting..."); exit(0); }} - }, + } ("server", sub_matches) => { info!("Starting CloverHub!"); let from_server_token = CancellationToken::new(); let server_cancellation_token_clone = from_server_token.clone(); let cancellation_token = CancellationToken::new(); - let data_dir = sub_matches.get_one::("data_dir").expect("Default set in Clap."); - let port = unwrap_port_arg(sub_matches.get_one::("port").expect("Default provided in Clap.").parse::()); + let data_dir = sub_matches + .get_one::("data_dir") + .expect("Default set in Clap."); + let port = unwrap_port_arg( + sub_matches + .get_one::("port") + .expect("Default provided in Clap.") + .parse::(), + ); info!("Running Backend Server..."); let server_token = cancellation_token.clone(); - let server_handle = tokio::task::spawn(async move { - server_main(data_dir, port, server_token, from_server_token).await; + let server_handle = tokio::task::spawn(async move { + server_main(data_dir, port, server_token, from_server_token).await; }); - - let signal_handle = get_signal_handle(big_boy_token.clone(), cancellation_token, Some(server_cancellation_token_clone)); + + let signal_handle = get_signal_handle( + big_boy_token.clone(), + cancellation_token, + Some(server_cancellation_token_clone), + ); tokio::select! {_ = futures::future::join_all(vec![signal_handle, server_handle]) => { info!("Exiting..."); @@ -221,14 +292,27 @@ async fn run(big_boy_token: CancellationToken) { ("tui", sub_matches) => { info!("Starting CloverHub!"); let cancellation_token = CancellationToken::new(); - let host = sub_matches.get_one::("host").expect("Default set in Clap."); - let port = unwrap_port_arg(sub_matches.get_one::("port").expect("Default set in Clap.").parse::()); + let host = sub_matches + .get_one::("host") + .expect("Default set in Clap."); + let port = unwrap_port_arg( + sub_matches + .get_one::("port") + .expect("Default set in Clap.") + .parse::(), + ); info!("Running Terminal UI..."); let tui_host = Arc::new(host); let tui_token = cancellation_token.clone(); - let tui_handle = tokio::task::spawn(async move { - tui_main(port, Ok::((*tui_host.to_owned()).to_string()).ok(), tui_token).await.err(); + let tui_handle = tokio::task::spawn(async move { + tui_main( + port, + Ok::((*tui_host.to_owned()).to_string()).ok(), + tui_token, + ) + .await + .err(); }); let signal_handle = get_signal_handle(big_boy_token.clone(), cancellation_token, None); diff --git a/clover-hub/src/server/appd/docker.rs b/clover-hub/src/server/appd/docker.rs index e3e96eb..793ac8a 100644 --- a/clover-hub/src/server/appd/docker.rs +++ b/clover-hub/src/server/appd/docker.rs @@ -1,25 +1,28 @@ -use std::sync::Arc; -use bollard::{image::BuildImageOptions, Docker}; -use futures::TryStreamExt; use crate::server::appd::models::Application; -use log::{info, debug, error}; +use bollard::{ + Docker, + image::BuildImageOptions, +}; +use futures::TryStreamExt; +use log::{ + debug, + error, + info, +}; +use std::sync::Arc; pub enum Error { - ContainerBuildFailed { - container_id: String - }, - ContainerCreationFailed { - container_id: String - }, - ContainerStartFailed { - container_id: String - }, - ContainerStopFailed { - container_id: String - } + ContainerBuildFailed { container_id: String }, + ContainerCreationFailed { container_id: String }, + ContainerStartFailed { container_id: String }, + ContainerStopFailed { container_id: String }, } -pub async fn init_app(docker: Arc, app_id: &String, app_spec: &mut Application) -> Result<(), Error> { +pub async fn init_app( + docker: Arc, + app_id: &String, + app_spec: &mut Application, +) -> Result<(), Error> { let app_str = format!("{} ({})", app_spec.name.clone(), app_id.clone()); info!("Initializing {}...", app_str.clone()); @@ -31,38 +34,68 @@ pub async fn init_app(docker: Arc, app_id: &String, app_spec: &mut Appli let mut build_errored = None; match container_config.build { - None => {}, + None => {} Some(build_config) => { - let image_tag = format!("{}/{}:{}", app_id.clone(), container_id.clone(), app_spec.version.clone()); - info!("{}, Building image: {}...", container_str.clone(), image_tag.clone()); - - let mut build_progress = docker.build_image(BuildImageOptions { - dockerfile: build_config.url.0.clone(), - t: image_tag.clone(), - ..Default::default() - }, None, None); + let image_tag = format!( + "{}/{}:{}", + app_id.clone(), + container_id.clone(), + app_spec.version.clone() + ); + info!( + "{}, Building image: {}...", + container_str.clone(), + image_tag.clone() + ); + + let mut build_progress = docker.build_image( + BuildImageOptions { + dockerfile: build_config.url.0.clone(), + t: image_tag.clone(), + ..Default::default() + }, + None, + None, + ); while let Ok(Some(response)) = build_progress.try_next().await { debug!("{}: {:?}", image_tag.clone(), response.clone()); match response.error { - None => { - match response.progress_detail { - Some(progress) => { - info!("{}, Building: {}, {:?}/{:?}", container_str.clone(), image_tag.clone(), progress.current, progress.total); - } - None => {} + None => match response.progress_detail { + Some(progress) => { + info!( + "{}, Building: {}, {:?}/{:?}", + container_str.clone(), + image_tag.clone(), + progress.current, + progress.total + ); } + None => {} }, Some(e) => { - error!("{}, Building: {}, Build failed! Stopping because of:\n{}", container_str.clone(), image_tag.clone(), e); - build_errored = Some(Error::ContainerBuildFailed { container_id: container_config.options.name.clone() }); + error!( + "{}, Building: {}, Build failed! Stopping because of:\n{}", + container_str.clone(), + image_tag.clone(), + e + ); + build_errored = Some(Error::ContainerBuildFailed { + container_id: container_config.options.name.clone(), + }); } } } match build_errored { - None => { info!("{}: Finished building: {}!", container_str.clone(), image_tag.clone()); } + None => { + info!( + "{}: Finished building: {}!", + container_str.clone(), + image_tag.clone() + ); + } Some(_) => {} } } @@ -72,31 +105,49 @@ pub async fn init_app(docker: Arc, app_id: &String, app_spec: &mut Appli Some(e) => { app_init_errored = Some(e); break; - }, + } None => { debug!("{}, Creating...", container_str.clone()); - match docker.create_container(Some(container_config.options.clone()), container_config.config.clone()).await { + match docker + .create_container( + Some(container_config.options.clone()), + container_config.config.clone(), + ) + .await + { Ok(_) => { info!("{}, Starting...", container_str.clone()); - match docker.start_container::( - &container_config.options.name.clone(), - None - ).await { + match docker + .start_container::(&container_config.options.name.clone(), None) + .await + { Ok(_) => { info!("{}, Started!", container_str.clone()); - }, + } Err(e) => { // TODO: Better error formatting. - error!("{}, Failed to start container!\n{:?}", container_str.clone(), e); - app_init_errored = Some(Error::ContainerStartFailed { container_id: container_id.clone() }); + error!( + "{}, Failed to start container!\n{:?}", + container_str.clone(), + e + ); + app_init_errored = Some(Error::ContainerStartFailed { + container_id: container_id.clone(), + }); break; } } - }, + } Err(e) => { // TODO: Better error formatting. - error!("{}, Failed to create container!\n{:?}", container_str.clone(), e); - app_init_errored = Some(Error::ContainerCreationFailed { container_id: container_id.clone() }); + error!( + "{}, Failed to create container!\n{:?}", + container_str.clone(), + e + ); + app_init_errored = Some(Error::ContainerCreationFailed { + container_id: container_id.clone(), + }); break; } } @@ -105,11 +156,11 @@ pub async fn init_app(docker: Arc, app_id: &String, app_spec: &mut Appli } match app_init_errored { - None => { + None => { app_spec.initialized = true; - Ok(()) - }, - Some(e) => { + Ok(()) + } + Some(e) => { // match e.clone() { // Error::ContainerBuildFailed { container_id } => {}, // Error::ContainerCreationFailed { container_id } => { @@ -120,12 +171,16 @@ pub async fn init_app(docker: Arc, app_id: &String, app_spec: &mut Appli // } // } - Err(e) + Err(e) } } } -pub async fn remove_app(docker: Arc, app_id: &String, app_spec: &mut Application) -> Result<(), Error> { +pub async fn remove_app( + docker: Arc, + app_id: &String, + app_spec: &mut Application, +) -> Result<(), Error> { let app_str = format!("{} ({})", app_spec.name.clone(), app_id.clone()); info!("Removing {}...", app_str.clone()); @@ -136,30 +191,32 @@ pub async fn remove_app(docker: Arc, app_id: &String, app_spec: &mut App let container_str = format!("{}, Container: {}", app_str.clone(), container_id.clone()); info!("{}, Stopping...", container_str.clone()); - match docker.stop_container( - &container_config.options.name.clone(), - None - ).await { + match docker + .stop_container(&container_config.options.name.clone(), None) + .await + { Ok(_) => { info!("{}, Stopped!", container_str.clone()); // TODO: Add config to remove containers when the app is removed. - }, + } Err(e) => { // TODO: Better error formatting. - error!("{}, Failed to stop container!\n{:?}", container_str.clone(), e); - app_removal_errored = Some(Error::ContainerStopFailed { container_id: container_id.clone() }); + error!( + "{}, Failed to stop container!\n{:?}", + container_str.clone(), + e + ); + app_removal_errored = Some(Error::ContainerStopFailed { + container_id: container_id.clone(), + }); break; } } } match app_removal_errored { - None => { - Ok(()) - }, - Some(e) => { - Err(e) - } + None => Ok(()), + Some(e) => Err(e), } } diff --git a/clover-hub/src/server/appd/mod.rs b/clover-hub/src/server/appd/mod.rs index 1c885ee..5123684 100644 --- a/clover-hub/src/server/appd/mod.rs +++ b/clover-hub/src/server/appd/mod.rs @@ -1,26 +1,42 @@ -pub mod models; pub mod docker; +pub mod models; -use std::sync::Arc; +use self::docker::init_app; +use crate::utils::send_ipc_message; +use bollard::{ + API_DEFAULT_VERSION, + Docker, +}; use docker::remove_app; -use log::{debug, error, info, warn}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use log::{ + debug, + error, + info, + warn, +}; +use std::sync::Arc; +use tokio::sync::mpsc::{ + UnboundedReceiver, + UnboundedSender, + unbounded_channel, +}; use tokio_util::sync::CancellationToken; use url::Url; -use crate::utils::send_ipc_message; -use bollard::{Docker, API_DEFAULT_VERSION}; -use self::docker::init_app; -use super::evtbuzz::models::{IPCMessageWithId, CoreUserConfig, Store}; +use super::evtbuzz::models::{ + CoreUserConfig, + IPCMessageWithId, + Store, +}; // TODO: Create application manifest schema/models pub async fn appd_main( - ipc_tx: UnboundedSender, - mut ipc_rx: UnboundedReceiver, - store: Arc, + ipc_tx: UnboundedSender, + mut ipc_rx: UnboundedReceiver, + store: Arc, user_config: Arc, - cancellation_tokens: (CancellationToken, CancellationToken) + cancellation_tokens: (CancellationToken, CancellationToken), ) { info!("Starting AppDaemon..."); @@ -34,46 +50,65 @@ pub async fn appd_main( let init_user = Arc::new(user_config.clone()); let (init_from_tx, mut init_from_rx) = unbounded_channel::(); let init_docker = docker.clone(); - cancellation_tokens.0.run_until_cancelled(async move { - let mut init_apps = init_store.applications.lock().await; - let mut apps_initialized = 0; - - if init_apps.len() == 0 { - info!("No pre-configured applications to initialize."); - } else { - for (id, spec) in init_apps.iter_mut() { - match init_app(init_docker.clone(), id, spec).await { - Ok(_) => { apps_initialized += 1; }, - Err(_e) => { - error!("Failed to initialize application {} ({})!", spec.name.clone(), id.clone()); + cancellation_tokens + .0 + .run_until_cancelled(async move { + let mut init_apps = init_store.applications.lock().await; + let mut apps_initialized = 0; + + if init_apps.len() == 0 { + info!("No pre-configured applications to initialize."); + } else { + for (id, spec) in init_apps.iter_mut() { + match init_app(init_docker.clone(), id, spec).await { + Ok(_) => { + apps_initialized += 1; + } + Err(_e) => { + error!( + "Failed to initialize application {} ({})!", + spec.name.clone(), + id.clone() + ); + } } - } - // Update the application state. - init_store.applications.lock().await.insert(id.clone(), spec.clone()); + // Update the application state. + init_store + .applications + .lock() + .await + .insert(id.clone(), spec.clone()); + } } - } - if apps_initialized != init_apps.len() { - warn!("Only initialized {} apps out of {}!", apps_initialized, init_apps.len()); - - let _ = send_ipc_message( - &init_store, - &init_user, - Arc::new(init_from_tx.clone()), - "clover://appd.clover.reboot-codes.com/status".to_string(), - "incomplete-init".to_string() - ).await; - } else { - let _ = send_ipc_message( - &init_store, - &init_user, - Arc::new(init_from_tx.clone()), - "clover://appd.clover.reboot-codes.com/status".to_string(), - "finished-init".to_string() - ).await; - } - }).await; + if apps_initialized != init_apps.len() { + warn!( + "Only initialized {} apps out of {}!", + apps_initialized, + init_apps.len() + ); + + let _ = send_ipc_message( + &init_store, + &init_user, + Arc::new(init_from_tx.clone()), + "clover://appd.clover.reboot-codes.com/status".to_string(), + "incomplete-init".to_string(), + ) + .await; + } else { + let _ = send_ipc_message( + &init_store, + &init_user, + Arc::new(init_from_tx.clone()), + "clover://appd.clover.reboot-codes.com/status".to_string(), + "finished-init".to_string(), + ) + .await; + } + }) + .await; let ipc_recv_token = cancellation_tokens.0.clone(); let ipc_recv_handle = tokio::task::spawn(async move { @@ -121,9 +156,9 @@ pub async fn appd_main( _ = cleanup_token.cancelled() => { ipc_recv_handle.abort(); ipc_trans_handle.abort(); - + info!("Cleaning up applications..."); - + let mut cleanup_apps = cleanup_store.applications.lock().await; let mut apps_removed = 0; @@ -152,7 +187,7 @@ pub async fn appd_main( cancellation_tokens.1.cancel(); } } - }, + } Err(e) => { error!("Failed to setup docker connection!\n{}", e); } diff --git a/clover-hub/src/server/appd/models.rs b/clover-hub/src/server/appd/models.rs index fcc4963..d9b8185 100644 --- a/clover-hub/src/server/appd/models.rs +++ b/clover-hub/src/server/appd/models.rs @@ -1,7 +1,14 @@ -use std::collections::HashMap; +use crate::server::warehouse::repos::models::{ + Optional, + OptionalString, + RequiredString, +}; use bollard::container; -use serde::{Deserialize, Serialize}; -use crate::server::warehouse::repos::models::{Optional, OptionalString, RequiredString}; +use serde::{ + Deserialize, + Serialize, +}; +use std::collections::HashMap; // TODO: Define defaults via `Default` trait impl. @@ -10,15 +17,15 @@ use crate::server::warehouse::repos::models::{Optional, OptionalString, Required #[serde(remote = "container::CreateContainerOptions")] pub struct CreateContainerOptionsDef where - T: Into + Serialize, + T: Into + Serialize, { - /// Assign the specified name to the container. - pub name: T, + /// Assign the specified name to the container. + pub name: T, - /// The platform to use for the container. - /// Added in API v1.41. - #[serde(skip_serializing_if = "Option::is_none")] - pub platform: Option, + /// The platform to use for the container. + /// Added in API v1.41. + #[serde(skip_serializing_if = "Option::is_none")] + pub platform: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -38,7 +45,7 @@ pub struct Application { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ContainerConfig { #[serde(with = "CreateContainerOptionsDef")] - pub options: container::CreateContainerOptions, + pub options: container::CreateContainerOptions, pub config: container::Config, pub build: Option, } @@ -48,7 +55,7 @@ pub struct BuildConfig { /// Url to either container repo, or source git repo pub url: RequiredString, /// Optional repository creds - pub creds: Optional + pub creds: Optional, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/clover-hub/src/server/arbiter/mod.rs b/clover-hub/src/server/arbiter/mod.rs index 13ec0cc..e85a2ee 100644 --- a/clover-hub/src/server/arbiter/mod.rs +++ b/clover-hub/src/server/arbiter/mod.rs @@ -1,35 +1,50 @@ pub mod models; +use crate::utils::send_ipc_message; +use log::{ + debug, + info, +}; use std::sync::Arc; -use log::{debug, info}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use tokio::sync::mpsc::{ + UnboundedReceiver, + UnboundedSender, + unbounded_channel, +}; use tokio_util::sync::CancellationToken; use url::Url; -use crate::utils::send_ipc_message; -use super::evtbuzz::models::{IPCMessageWithId, CoreUserConfig, Store}; +use super::evtbuzz::models::{ + CoreUserConfig, + IPCMessageWithId, + Store, +}; pub async fn arbiter_main( - ipc_tx: UnboundedSender, - mut ipc_rx: UnboundedReceiver, - store: Arc, + ipc_tx: UnboundedSender, + mut ipc_rx: UnboundedReceiver, + store: Arc, user_config: Arc, - cancellation_tokens: (CancellationToken, CancellationToken) + cancellation_tokens: (CancellationToken, CancellationToken), ) { info!("Starting Arbiter..."); let init_store = Arc::new(store.clone()); let init_user = Arc::new(user_config.clone()); let (init_from_tx, mut init_from_rx) = unbounded_channel::(); - cancellation_tokens.0.run_until_cancelled(async move { - let _ = send_ipc_message( - &init_store, - &init_user, - Arc::new(init_from_tx), - "clover://arbiter.clover.reboot-codes.com/status".to_string(), - "finished-init".to_string() - ).await; - }).await; + cancellation_tokens + .0 + .run_until_cancelled(async move { + let _ = send_ipc_message( + &init_store, + &init_user, + Arc::new(init_from_tx), + "clover://arbiter.clover.reboot-codes.com/status".to_string(), + "finished-init".to_string(), + ) + .await; + }) + .await; let ipc_recv_token = cancellation_tokens.0.clone(); let ipc_recv_handle = tokio::task::spawn(async move { @@ -75,7 +90,7 @@ pub async fn arbiter_main( _ = cleanup_token.cancelled() => { ipc_recv_handle.abort(); ipc_trans_handle.abort(); - + info!("Cleaning up users..."); // TODO: Clean up registered users when server is shutting down. diff --git a/clover-hub/src/server/arbiter/models.rs b/clover-hub/src/server/arbiter/models.rs index 137907a..ea8f560 100644 --- a/clover-hub/src/server/arbiter/models.rs +++ b/clover-hub/src/server/arbiter/models.rs @@ -1,7 +1,13 @@ -use std::{collections::HashMap, sync::Arc}; -use serde::{Deserialize, Serialize}; -use tokio::sync::Mutex; use crate::server::evtbuzz::models::Session; +use serde::{ + Deserialize, + Serialize, +}; +use std::{ + collections::HashMap, + sync::Arc, +}; +use tokio::sync::Mutex; // TODO: Define defaults via `Default` trait impl. @@ -11,17 +17,17 @@ pub struct User { pub api_keys: Vec, pub sessions: Arc>>, pub user_type: String, - pub pretty_name: String + pub pretty_name: String, } impl User { pub fn to_user_with_id(self, id: String) -> UserWithId { - UserWithId { - id, - api_keys: self.api_keys, + UserWithId { + id, + api_keys: self.api_keys, sessions: self.sessions, user_type: self.user_type, - pretty_name: self.pretty_name + pretty_name: self.pretty_name, } } } @@ -32,7 +38,7 @@ pub struct UserWithId { pub api_keys: Vec, pub sessions: Arc>>, pub user_type: String, - pub pretty_name: String + pub pretty_name: String, } impl Into for UserWithId { @@ -41,7 +47,7 @@ impl Into for UserWithId { api_keys: self.api_keys, sessions: self.sessions, user_type: self.user_type, - pretty_name: self.pretty_name + pretty_name: self.pretty_name, } } } diff --git a/clover-hub/src/server/evtbuzz/listener.rs b/clover-hub/src/server/evtbuzz/listener.rs index 022768c..8597222 100644 --- a/clover-hub/src/server/evtbuzz/listener.rs +++ b/clover-hub/src/server/evtbuzz/listener.rs @@ -1,21 +1,54 @@ -use log::{debug, error, info, warn}; +use crate::server::arbiter::models::{ + ApiKeyWithKey, + UserWithId, +}; +use crate::server::evtbuzz::models::{ + Client, + ClientWithId, + CoreUserConfig, + IPCMessageWithId, + Session, + Store, +}; +use crate::server::evtbuzz::websockets::handle_ws_client; +use crate::utils::{ + gen_cid_with_check, + gen_ipc_message, + iso8601, +}; +use log::{ + debug, + error, + info, + warn, +}; use regex::Regex; -use tokio::sync::Mutex; -use tokio_util::sync::CancellationToken; -use url::Url; +use serde::{ + Deserialize, + Serialize, +}; use std::collections::HashMap; use std::convert::Infallible; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use std::net::{ + IpAddr, + Ipv4Addr, + SocketAddr, +}; use std::sync::Arc; use std::time::SystemTime; -use serde::{Deserialize, Serialize}; use thiserror::Error; -use warp::{Filter, http::StatusCode}; -use crate::server::arbiter::models::{ApiKeyWithKey, UserWithId}; -use crate::utils::{gen_cid_with_check, gen_ipc_message, iso8601}; -use crate::server::evtbuzz::models::{Client, ClientWithId, CoreUserConfig, IPCMessageWithId, Session, Store}; -use crate::server::evtbuzz::websockets::handle_ws_client; +use tokio::sync::Mutex; +use tokio::sync::mpsc::{ + self, + UnboundedReceiver, + UnboundedSender, +}; +use tokio_util::sync::CancellationToken; +use url::Url; +use warp::{ + Filter, + http::StatusCode, +}; // example error response #[derive(Serialize, Debug)] @@ -36,45 +69,66 @@ impl warp::reject::Reject for ApiErrors {} // generic errors handler for all api errors // ensures unified error structure -async fn handle_rejection(err: warp::reject::Rejection) -> std::result::Result { - let code; - let message; - - if err.is_not_found() { - code = StatusCode::NOT_FOUND; - message = "Not found"; - } else if let Some(_) = err.find::() { - code = StatusCode::BAD_REQUEST; - message = "Invalid Body"; - } else if let Some(e) = err.find::() { - match e { - ApiErrors::NotAuthorized(_error_message) => { - code = StatusCode::UNAUTHORIZED; - message = "Action not authorized"; - } +async fn handle_rejection( + err: warp::reject::Rejection, +) -> std::result::Result { + let code; + let message; + + if err.is_not_found() { + code = StatusCode::NOT_FOUND; + message = "Not found"; + } else if let Some(_) = err.find::() { + code = StatusCode::BAD_REQUEST; + message = "Invalid Body"; + } else if let Some(e) = err.find::() { + match e { + ApiErrors::NotAuthorized(_error_message) => { + code = StatusCode::UNAUTHORIZED; + message = "Action not authorized"; } - } else if let Some(_) = err.find::() { - code = StatusCode::METHOD_NOT_ALLOWED; - message = "Method not allowed"; - } else { - // We should have expected this... Just log and say its a 500 - error!("unhandled rejection: {:?}", err); - code = StatusCode::INTERNAL_SERVER_ERROR; - message = "Internal server error"; } + } else if let Some(_) = err.find::() { + code = StatusCode::METHOD_NOT_ALLOWED; + message = "Method not allowed"; + } else { + // We should have expected this... Just log and say its a 500 + error!("unhandled rejection: {:?}", err); + code = StatusCode::INTERNAL_SERVER_ERROR; + message = "Internal server error"; + } - let json = warp::reply::json(&ApiErrorResult { detail: message.into() }); + let json = warp::reply::json(&ApiErrorResult { + detail: message.into(), + }); - Ok(warp::reply::with_status(json, code)) + Ok(warp::reply::with_status(json, code)) } // middleware that looks for authorization header and validates it -async fn ensure_authentication(path: String, store: Arc>, auth_header: Option) -> Result<(UserWithId, ApiKeyWithKey, ClientWithId, Session), warp::reject::Rejection> { +async fn ensure_authentication( + path: String, + store: Arc>, + auth_header: Option, +) -> Result<(UserWithId, ApiKeyWithKey, ClientWithId, Session), warp::reject::Rejection> { let client_id = gen_cid_with_check(&store).await; - let mut client = ClientWithId { api_key: "".to_string(), user_id: "".to_string(), id: client_id.clone(), active: true }; - store.clients.lock().await.insert(client_id.clone(), client.clone().into()); - - info!("Client: {}, hit secure path: {}, attempting authentication...", client.id.clone(), path.clone()); + let mut client = ClientWithId { + api_key: "".to_string(), + user_id: "".to_string(), + id: client_id.clone(), + active: true, + }; + store + .clients + .lock() + .await + .insert(client_id.clone(), client.clone().into()); + + info!( + "Client: {}, hit secure path: {}, attempting authentication...", + client.id.clone(), + path.clone() + ); match auth_header { Some(header) => { @@ -96,37 +150,102 @@ async fn ensure_authentication(path: String, store: Arc>, auth_header } if authenticated { - debug!("Running through client registration for api_key: {}", api_key_str.clone()); + debug!( + "Running through client registration for api_key: {}", + api_key_str.clone() + ); let api_keys = store.clone().api_keys.clone(); let api_keys_locked = api_keys.lock().await; - let api_key = api_keys_locked.get(&api_key_str.clone()).unwrap().clone().to_api_key_with_key(api_key_str.clone()); + let api_key = api_keys_locked + .get(&api_key_str.clone()) + .unwrap() + .clone() + .to_api_key_with_key(api_key_str.clone()); let user_id = api_key.clone().user_id; debug!("Registering as client: {}", client_id.clone()); - client = ClientWithId { api_key: api_key_str.clone(), user_id: user_id.clone(), id: client_id.clone(), active: true }; - store.clients.lock().await.insert(client_id.clone(), client.clone().into()); - - let user = store.users.clone().lock().await.get(&user_id.clone()).unwrap().clone().to_user_with_id(user_id.clone()); - let session = Session { start_time: iso8601(&SystemTime::now()), end_time: "".to_string(), api_key: api_key.key.clone() }; - user.sessions.lock().await.insert(client_id.clone(), session.clone()); + client = ClientWithId { + api_key: api_key_str.clone(), + user_id: user_id.clone(), + id: client_id.clone(), + active: true, + }; + store + .clients + .lock() + .await + .insert(client_id.clone(), client.clone().into()); + + let user = store + .users + .clone() + .lock() + .await + .get(&user_id.clone()) + .unwrap() + .clone() + .to_user_with_id(user_id.clone()); + let session = Session { + start_time: iso8601(&SystemTime::now()), + end_time: "".to_string(), + api_key: api_key.key.clone(), + }; + user + .sessions + .lock() + .await + .insert(client_id.clone(), session.clone()); debug!("Registered: {}!", client_id.clone()); - info!("Client: {}, authenticated as user: {}!", client_id.clone(), api_key.clone().user_id); - return Ok((user.clone(), api_key.clone(), client.clone(), session.clone())); + info!( + "Client: {}, authenticated as user: {}!", + client_id.clone(), + api_key.clone().user_id + ); + return Ok(( + user.clone(), + api_key.clone(), + client.clone(), + session.clone(), + )); } else { - warn!("Client: {}, attempted to connect with an invalid api key, disconnecting...", client.id.clone()); - client = ClientWithId { api_key: client.api_key, user_id: client.user_id, id: client.id, active: false }; - store.clients.lock().await.insert(client_id.clone(), client.clone().into()); + warn!( + "Client: {}, attempted to connect with an invalid api key, disconnecting...", + client.id.clone() + ); + client = ClientWithId { + api_key: client.api_key, + user_id: client.user_id, + id: client.id, + active: false, + }; + store + .clients + .lock() + .await + .insert(client_id.clone(), client.clone().into()); return Err(warp::reject::custom(ApiErrors::NotAuthorized( "api key not registered".to_string(), ))); } - }, + } None => { - warn!("Client: {}, attempted to connect without an api key, disconnecting...", client.id.clone()); - client = ClientWithId { api_key: client.api_key, user_id: client.user_id, id: client.id, active: false }; - store.clients.lock().await.insert(client_id.clone(), client.clone().into()); + warn!( + "Client: {}, attempted to connect without an api key, disconnecting...", + client.id.clone() + ); + client = ClientWithId { + api_key: client.api_key, + user_id: client.user_id, + id: client.id, + active: false, + }; + store + .clients + .lock() + .await + .insert(client_id.clone(), client.clone().into()); Err(warp::reject::custom(ApiErrors::NotAuthorized( "no authorization header".to_string(), ))) @@ -136,17 +255,28 @@ async fn ensure_authentication(path: String, store: Arc>, auth_header #[derive(Serialize, Deserialize, Clone)] pub struct ServerHealth { - up_since: String + up_since: String, } -async fn handle_ipc_send(sender: Arc>, msg: IPCMessageWithId, user_config: &Arc, store: &Store) { +async fn handle_ipc_send( + sender: Arc>, + msg: IPCMessageWithId, + user_config: &Arc, + store: &Store, +) { let users_mutex = &store.users.to_owned(); let users = users_mutex.lock().await; - let user_conf = users.get(&user_config.id.clone()).expect(&format!("ERROR: Core user not found: {}", user_config.id.clone())); + let user_conf = users.get(&user_config.id.clone()).expect(&format!( + "ERROR: Core user not found: {}", + user_config.id.clone() + )); let keys_mutex = &store.api_keys.to_owned(); let keys = keys_mutex.lock().await; - let api_key_conf = keys.get(&user_config.api_key.clone()).expect(&format!("ERROR: Core user api_key not found: {}", user_config.api_key.clone())); + let api_key_conf = keys.get(&user_config.api_key.clone()).expect(&format!( + "ERROR: Core user api_key not found: {}", + user_config.api_key.clone() + )); let mut event_sent = false; for allowed_event_regex in api_key_conf.allowed_events_from.clone() { @@ -156,38 +286,80 @@ async fn handle_ipc_send(sender: Arc>, m match sender.send(msg.clone()) { Ok(_) => { event_sent = true; - }, + } Err(e) => { - error!("Core user: {}, IPC channel: {}, Failed to send message: {{ \"author\": \"{}\", \"kind\": \"{}\", \"message\": \"{}\" }}, due to:\n{}", user_config.id.clone(), user_conf.user_type.clone(), msg.author.clone(), msg.kind.clone(), msg.message.clone(), e); + error!( + "Core user: {}, IPC channel: {}, Failed to send message: {{ \"author\": \"{}\", \"kind\": \"{}\", \"message\": \"{}\" }}, due to:\n{}", + user_config.id.clone(), + user_conf.user_type.clone(), + msg.author.clone(), + msg.kind.clone(), + msg.message.clone(), + e + ); } }; } - }, + } Err(e) => { - error!("Core user: {}, api key's \"allowed events from\", regex: {}, is invalid! Regex Error: {}", user_config.id.clone(), allowed_event_regex.clone(), e); + error!( + "Core user: {}, api key's \"allowed events from\", regex: {}, is invalid! Regex Error: {}", + user_config.id.clone(), + allowed_event_regex.clone(), + e + ); } } } if !event_sent { - debug!("Core user: {}, event \"{}\" not sent.", user_config.id, msg.kind.clone()); + debug!( + "Core user: {}, event \"{}\" not sent.", + user_config.id, + msg.kind.clone() + ); } } pub async fn evtbuzz_listener( - port: u16, + port: u16, store: Arc, - mut arbiter_ipc: (&CoreUserConfig, UnboundedReceiver, UnboundedSender), - mut renderer_ipc: (&CoreUserConfig, UnboundedReceiver, UnboundedSender), - mut modman_ipc: (&CoreUserConfig, UnboundedReceiver, UnboundedSender), - mut inference_engine_ipc: (&CoreUserConfig, UnboundedReceiver, UnboundedSender), - mut appd_ipc: (&CoreUserConfig, UnboundedReceiver, UnboundedSender), - mut warehouse_ipc: (&CoreUserConfig, UnboundedReceiver, UnboundedSender), + mut arbiter_ipc: ( + &CoreUserConfig, + UnboundedReceiver, + UnboundedSender, + ), + mut renderer_ipc: ( + &CoreUserConfig, + UnboundedReceiver, + UnboundedSender, + ), + mut modman_ipc: ( + &CoreUserConfig, + UnboundedReceiver, + UnboundedSender, + ), + mut inference_engine_ipc: ( + &CoreUserConfig, + UnboundedReceiver, + UnboundedSender, + ), + mut appd_ipc: ( + &CoreUserConfig, + UnboundedReceiver, + UnboundedSender, + ), + mut warehouse_ipc: ( + &CoreUserConfig, + UnboundedReceiver, + UnboundedSender, + ), cancellation_tokens: (CancellationToken, CancellationToken), - evtbuzz_user_config: Arc + evtbuzz_user_config: Arc, ) { info!("Starting EvtBuzz on port: {}...", port); - let clients_tx: Arc>>> = Arc::new(Mutex::new(HashMap::new())); + let clients_tx: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); let arbiter_cfg = arbiter_ipc.0; let arbiter_tx = arbiter_ipc.2; @@ -207,18 +379,26 @@ pub async fn evtbuzz_listener( (modman_cfg, modman_tx), (inference_engine_cfg, inference_engine_tx), (appd_cfg, appd_tx), - (warehouse_cfg, warehouse_tx) + (warehouse_cfg, warehouse_tx), ] { - let client_obj = Client { - api_key: client.0.api_key.clone(), + let client_obj = Client { + api_key: client.0.api_key.clone(), user_id: client.0.id.clone(), active: true, }; let cid = gen_cid_with_check(&store).await; - debug!("Registering internal client \"{}\":\n{:#?}", cid.clone(), client_obj.clone()); - - store.clients.lock().await.insert(cid.clone(), client_obj.clone()); + debug!( + "Registering internal client \"{}\":\n{:#?}", + cid.clone(), + client_obj.clone() + ); + + store + .clients + .lock() + .await + .insert(cid.clone(), client_obj.clone()); clients_tx.lock().await.insert(cid.clone(), client.1); } @@ -234,11 +414,12 @@ pub async fn evtbuzz_listener( let store_filter = warp::any().map(move || filter_store.clone()); let start_up_time = iso8601(&SystemTime::now()); - let health_check_path = warp::path("health-check") - .map(move || { - let current_health = ServerHealth{up_since: start_up_time.clone()}; - warp::reply::json(¤t_health) - }); + let health_check_path = warp::path("health-check").map(move || { + let current_health = ServerHealth { + up_since: start_up_time.clone(), + }; + warp::reply::json(¤t_health) + }); let ws_path = warp::path("ws") .and(warp::any().map(|| "/ws".to_string())) @@ -259,17 +440,21 @@ pub async fn evtbuzz_listener( // TODO: Add control REST API for start up and shut down. // TODO: Start creating GQL API endpoint. - + let server_port = Arc::new(port.clone()); let http_token = cancellation_tokens.0.clone(); let http_handle = tokio::task::spawn(async move { let (_, server) = warp::serve(routes) // TODO: Add option for listening address. - .try_bind_with_graceful_shutdown(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), *server_port), async move { - tokio::select! { - _ = http_token.cancelled() => {} - } - }).expect(""); + .try_bind_with_graceful_shutdown( + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), *server_port), + async move { + tokio::select! { + _ = http_token.cancelled() => {} + } + }, + ) + .expect(""); server.await; info!("Server stopped."); }); diff --git a/clover-hub/src/server/evtbuzz/models.rs b/clover-hub/src/server/evtbuzz/models.rs index 896be39..f1a6ca5 100644 --- a/clover-hub/src/server/evtbuzz/models.rs +++ b/clover-hub/src/server/evtbuzz/models.rs @@ -1,7 +1,31 @@ -use std::{collections::HashMap, sync::Arc}; +use crate::{ + server::{ + appd::models::Application, + arbiter::models::{ + ApiKey, + ApiKeyWithKeyWithoutUID, + User, + }, + modman::models::Module, + warehouse::{ + config::models::Config, + repos::models::Manifest, + }, + }, + utils::{ + gen_api_key_with_check, + gen_uid_with_check, + }, +}; +use serde::{ + Deserialize, + Serialize, +}; +use std::{ + collections::HashMap, + sync::Arc, +}; use tokio::sync::Mutex; -use serde::{Deserialize, Serialize}; -use crate::{server::{arbiter::models::{ApiKey, ApiKeyWithKeyWithoutUID, User}, warehouse::{config::models::Config, repos::models::Manifest}, modman::models::Module, appd::models::Application}, utils::{gen_api_key_with_check, gen_uid_with_check}}; // TODO: Define defaults via `Default` trait impl. @@ -66,7 +90,7 @@ pub struct Session { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CoreUserConfig { pub id: String, - pub api_key: String + pub api_key: String, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -74,7 +98,7 @@ pub struct UserConfig { pub user_type: String, pub pretty_name: String, pub id: String, - pub api_keys: Vec + pub api_keys: Vec, } // TODO: Add serialization/deserialization functions... @@ -93,13 +117,13 @@ pub struct Store { #[derive(Debug, Clone)] pub struct CoreUserConfigs { - pub evtbuzz: CoreUserConfig, - pub arbiter: CoreUserConfig, - pub renderer: CoreUserConfig, - pub appd: CoreUserConfig, - pub modman: CoreUserConfig, + pub evtbuzz: CoreUserConfig, + pub arbiter: CoreUserConfig, + pub renderer: CoreUserConfig, + pub appd: CoreUserConfig, + pub modman: CoreUserConfig, pub inference_engine: CoreUserConfig, - pub warehouse: CoreUserConfig + pub warehouse: CoreUserConfig, } impl Store { @@ -128,44 +152,54 @@ impl Store { pub async fn add_user(self, user_config: UserConfig) { let mut key_ids: Vec = vec![]; - for key_config in user_config.api_keys.iter() { key_ids.push(key_config.key.clone()); }; + for key_config in user_config.api_keys.iter() { + key_ids.push(key_config.key.clone()); + } - self.users.lock().await.insert(user_config.id.clone(), User { - pretty_name: user_config.pretty_name, - user_type: user_config.user_type, - api_keys: key_ids, - sessions: Arc::new(Mutex::new(HashMap::new())) - }); + self.users.lock().await.insert( + user_config.id.clone(), + User { + pretty_name: user_config.pretty_name, + user_type: user_config.user_type, + api_keys: key_ids, + sessions: Arc::new(Mutex::new(HashMap::new())), + }, + ); for key_config in user_config.api_keys.iter() { - self.api_keys.lock().await.insert(key_config.key.clone(), ApiKey { - allowed_events_to: key_config.allowed_events_to.clone(), - allowed_events_from: key_config.allowed_events_from.clone(), - user_id: user_config.id.clone(), - echo: key_config.echo.clone() - }); - }; + self.api_keys.lock().await.insert( + key_config.key.clone(), + ApiKey { + allowed_events_to: key_config.allowed_events_to.clone(), + allowed_events_from: key_config.allowed_events_from.clone(), + user_id: user_config.id.clone(), + echo: key_config.echo.clone(), + }, + ); + } } pub async fn create_master_user(self) -> CoreUserConfig { let master_user_id = gen_uid_with_check(&self).await; let master_api_key = gen_api_key_with_check(&self).await; - - self.add_user(UserConfig { - id: master_user_id.clone(), - pretty_name: "Master User".to_string(), - user_type: "com.reboot-codes.clover.master".to_string(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: master_api_key.clone(), - echo: true - }] - }).await; + + self + .add_user(UserConfig { + id: master_user_id.clone(), + pretty_name: "Master User".to_string(), + user_type: "com.reboot-codes.clover.master".to_string(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: master_api_key.clone(), + echo: true, + }], + }) + .await; CoreUserConfig { - id: master_user_id.clone(), - api_key: master_api_key.clone() + id: master_user_id.clone(), + api_key: master_api_key.clone(), } } @@ -174,136 +208,157 @@ impl Store { // EvtBuzz let evtbuzz_uid = gen_uid_with_check(&self).await; let evtbuzz_key = gen_api_key_with_check(&self).await; - self.clone().add_user(UserConfig { - user_type: "com.reboot-codes.clover.evtbuzz".to_string(), - pretty_name: "EvtBuzz".to_string(), - id: evtbuzz_uid.clone(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: evtbuzz_key.clone(), - echo: true - }] - }).await; + self + .clone() + .add_user(UserConfig { + user_type: "com.reboot-codes.clover.evtbuzz".to_string(), + pretty_name: "EvtBuzz".to_string(), + id: evtbuzz_uid.clone(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: evtbuzz_key.clone(), + echo: true, + }], + }) + .await; // Arbiter let arbiter_uid = gen_uid_with_check(&self).await; let arbiter_key = gen_api_key_with_check(&self).await; - self.clone().add_user(UserConfig { - user_type: "com.reboot-codes.clover.arbiter".to_string(), - pretty_name: "Arbiter".to_string(), - id: arbiter_uid.clone(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: arbiter_key.clone(), - echo: true - }] - }).await; + self + .clone() + .add_user(UserConfig { + user_type: "com.reboot-codes.clover.arbiter".to_string(), + pretty_name: "Arbiter".to_string(), + id: arbiter_uid.clone(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: arbiter_key.clone(), + echo: true, + }], + }) + .await; // Renderer let renderer_uid = gen_uid_with_check(&self).await; let renderer_key = gen_api_key_with_check(&self).await; - self.clone().add_user(UserConfig { - user_type: "com.reboot-codes.clover.renderer".to_string(), - pretty_name: "Renderer".to_string(), - id: renderer_uid.clone(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: renderer_key.clone(), - echo: true - }] - }).await; + self + .clone() + .add_user(UserConfig { + user_type: "com.reboot-codes.clover.renderer".to_string(), + pretty_name: "Renderer".to_string(), + id: renderer_uid.clone(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: renderer_key.clone(), + echo: true, + }], + }) + .await; // AppD let appd_uid = gen_uid_with_check(&self).await; let appd_key = gen_api_key_with_check(&self).await; - self.clone().add_user(UserConfig { - user_type: "com.reboot-codes.clover.appd".to_string(), - pretty_name: "appd".to_string(), - id: appd_uid.clone(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: appd_key.clone(), - echo: true - }] - }).await; + self + .clone() + .add_user(UserConfig { + user_type: "com.reboot-codes.clover.appd".to_string(), + pretty_name: "appd".to_string(), + id: appd_uid.clone(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: appd_key.clone(), + echo: true, + }], + }) + .await; // ModMan let modman_uid = gen_uid_with_check(&self).await; let modman_key = gen_api_key_with_check(&self).await; - self.clone().add_user(UserConfig { - user_type: "com.reboot-codes.clover.modman".to_string(), - pretty_name: "ModMan".to_string(), - id: modman_uid.clone(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: modman_key.clone(), - echo: true - }] - }).await; + self + .clone() + .add_user(UserConfig { + user_type: "com.reboot-codes.clover.modman".to_string(), + pretty_name: "ModMan".to_string(), + id: modman_uid.clone(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: modman_key.clone(), + echo: true, + }], + }) + .await; // Inference Engine let inference_engine_uid = gen_uid_with_check(&self).await; let inference_engine_key = gen_api_key_with_check(&self).await; - self.clone().add_user(UserConfig { - user_type: "com.reboot-codes.clover.inference-engine".to_string(), - pretty_name: "Inference Engine".to_string(), - id: inference_engine_uid.clone(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: inference_engine_key.clone(), - echo: true - }] - }).await; + self + .clone() + .add_user(UserConfig { + user_type: "com.reboot-codes.clover.inference-engine".to_string(), + pretty_name: "Inference Engine".to_string(), + id: inference_engine_uid.clone(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: inference_engine_key.clone(), + echo: true, + }], + }) + .await; // Warehouse let warehouse_uid = gen_uid_with_check(&self).await; let warehouse_key = gen_api_key_with_check(&self).await; - self.clone().add_user(UserConfig { - user_type: "com.reboot-codes.clover.warehouse".to_string(), - pretty_name: "Warehouse".to_string(), - id: warehouse_uid.clone(), - api_keys: vec![ApiKeyWithKeyWithoutUID { - allowed_events_to: vec![".*".to_string()], - allowed_events_from: vec![".*".to_string()], - key: warehouse_key.clone(), - echo: true - }] - }).await; + self + .clone() + .add_user(UserConfig { + user_type: "com.reboot-codes.clover.warehouse".to_string(), + pretty_name: "Warehouse".to_string(), + id: warehouse_uid.clone(), + api_keys: vec![ApiKeyWithKeyWithoutUID { + allowed_events_to: vec![".*".to_string()], + allowed_events_from: vec![".*".to_string()], + key: warehouse_key.clone(), + echo: true, + }], + }) + .await; CoreUserConfigs { evtbuzz: CoreUserConfig { id: evtbuzz_uid.clone(), - api_key: evtbuzz_key.clone() + api_key: evtbuzz_key.clone(), }, arbiter: CoreUserConfig { id: arbiter_uid.clone(), - api_key: arbiter_key.clone() + api_key: arbiter_key.clone(), }, renderer: CoreUserConfig { id: renderer_uid.clone(), - api_key: renderer_key.clone() + api_key: renderer_key.clone(), }, appd: CoreUserConfig { id: appd_uid.clone(), - api_key: appd_key.clone() + api_key: appd_key.clone(), }, modman: CoreUserConfig { id: modman_uid.clone(), - api_key: modman_key.clone() + api_key: modman_key.clone(), }, inference_engine: CoreUserConfig { id: inference_engine_uid.clone(), - api_key: inference_engine_key.clone() + api_key: inference_engine_key.clone(), }, warehouse: CoreUserConfig { id: warehouse_uid.clone(), - api_key: warehouse_key.clone() + api_key: warehouse_key.clone(), }, } } diff --git a/clover-hub/src/server/evtbuzz/websockets.rs b/clover-hub/src/server/evtbuzz/websockets.rs index a61b74e..6fe0b76 100644 --- a/clover-hub/src/server/evtbuzz/websockets.rs +++ b/clover-hub/src/server/evtbuzz/websockets.rs @@ -1,18 +1,44 @@ -use log::{debug, error, info, warn}; +use crate::server::arbiter::models::{ + ApiKeyWithKey, + UserWithId, +}; +use crate::server::evtbuzz::models::{ + Client, + ClientWithId, + IPCMessageWithId, + Session, + Store, +}; +use crate::utils::iso8601; +use futures::{ + SinkExt, + StreamExt, +}; +use log::{ + debug, + error, + info, + warn, +}; use regex::Regex; -use serde::{Deserialize, Serialize}; -use tokio::sync::Mutex; -use url::Url; -use uuid::Uuid; +use serde::{ + Deserialize, + Serialize, +}; use std::collections::HashMap; -use tokio::sync::mpsc::{self, UnboundedSender}; use std::sync::Arc; use std::time::SystemTime; -use futures::{SinkExt, StreamExt}; -use warp::filters::ws::{Message, WebSocket}; -use crate::server::arbiter::models::{ApiKeyWithKey, UserWithId}; -use crate::utils::iso8601; -use crate::server::evtbuzz::models::{Client, ClientWithId, IPCMessageWithId, Session, Store}; +use tokio::sync::Mutex; +use tokio::sync::mpsc::{ + self, + UnboundedSender, +}; +use url::Url; +use uuid::Uuid; +use warp::filters::ws::{ + Message, + WebSocket, +}; #[derive(Deserialize, Serialize, Debug, Clone)] pub struct WsIn { @@ -20,13 +46,22 @@ pub struct WsIn { pub message: String, } -pub async fn handle_ws_client(auth: (UserWithId, ApiKeyWithKey, ClientWithId, Session), ws: warp::ws::Ws, store: Arc>, to_clients_tx: Arc>>>>, from_clients_tx: Arc>) -> Result { +pub async fn handle_ws_client( + auth: (UserWithId, ApiKeyWithKey, ClientWithId, Session), + ws: warp::ws::Ws, + store: Arc>, + to_clients_tx: Arc>>>>, + from_clients_tx: Arc>, +) -> Result { let user = auth.0.clone(); let api_key = auth.1.clone(); let client = auth.2.clone(); let session = auth.3.clone(); - info!("Upgrading client: {}, to websocket connection...", client.id.clone()); + info!( + "Upgrading client: {}, to websocket connection...", + client.id.clone() + ); let ws_client = Arc::new(client.clone()); Ok(ws.on_upgrade(move |websocket: WebSocket| async move { @@ -74,7 +109,7 @@ pub async fn handle_ws_client(auth: (UserWithId, ApiKeyWithKey, ClientWithId, Se } }; debug!("Client: {}, send message: {{ \"id\": \"{}\", \"kind\": \"{}\", \"message\": \"{}\" }}...", ws_client.id.clone(), message_id.clone(), msg.kind.clone(), msg.message.clone()); - + let mut allowed_to_send = false; for allowed_send_pattern in recv_api_key.allowed_events_from.clone() { match Regex::new(&allowed_send_pattern) { diff --git a/clover-hub/src/server/inference_engine/mod.rs b/clover-hub/src/server/inference_engine/mod.rs index 3590ec7..8edae5b 100644 --- a/clover-hub/src/server/inference_engine/mod.rs +++ b/clover-hub/src/server/inference_engine/mod.rs @@ -1,33 +1,48 @@ +use crate::utils::send_ipc_message; +use log::{ + debug, + info, +}; use std::sync::Arc; -use log::{debug, info}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use tokio::sync::mpsc::{ + UnboundedReceiver, + UnboundedSender, + unbounded_channel, +}; use tokio_util::sync::CancellationToken; use url::Url; -use crate::utils::send_ipc_message; -use super::evtbuzz::models::{IPCMessageWithId, CoreUserConfig, Store}; +use super::evtbuzz::models::{ + CoreUserConfig, + IPCMessageWithId, + Store, +}; pub async fn inference_engine_main( - ipc_tx: UnboundedSender, - mut ipc_rx: UnboundedReceiver, - store: Arc, + ipc_tx: UnboundedSender, + mut ipc_rx: UnboundedReceiver, + store: Arc, user_config: Arc, - cancellation_tokens: (CancellationToken, CancellationToken) + cancellation_tokens: (CancellationToken, CancellationToken), ) { info!("Starting Inference Engine..."); let init_store = Arc::new(store.clone()); let init_user = Arc::new(user_config.clone()); let (init_from_tx, mut init_from_rx) = unbounded_channel::(); - cancellation_tokens.0.run_until_cancelled(async move { - let _ = send_ipc_message( - &init_store, - &init_user, - Arc::new(init_from_tx), - "clover://inference-engine.clover.reboot-codes.com/status".to_string(), - "finished-init".to_string() - ).await; - }).await; + cancellation_tokens + .0 + .run_until_cancelled(async move { + let _ = send_ipc_message( + &init_store, + &init_user, + Arc::new(init_from_tx), + "clover://inference-engine.clover.reboot-codes.com/status".to_string(), + "finished-init".to_string(), + ) + .await; + }) + .await; let ipc_recv_token = cancellation_tokens.0.clone(); let ipc_recv_handle = tokio::task::spawn(async move { diff --git a/clover-hub/src/server/mod.rs b/clover-hub/src/server/mod.rs index b2a2485..1192418 100644 --- a/clover-hub/src/server/mod.rs +++ b/clover-hub/src/server/mod.rs @@ -1,45 +1,54 @@ -pub mod evtbuzz; +pub mod appd; pub mod arbiter; -pub mod renderer; -pub mod modman; +pub mod evtbuzz; pub mod inference_engine; -pub mod appd; +pub mod modman; +pub mod renderer; pub mod warehouse; -use std::env; -use std::sync::Arc; use appd::appd_main; +use arbiter::arbiter_main; +use evtbuzz::listener::evtbuzz_listener; use evtbuzz::models::CoreUserConfigs; -use tokio::sync::mpsc; -use log::{debug, error, info}; -use evtbuzz::models::Store; use evtbuzz::models::IPCMessageWithId; -use evtbuzz::listener::evtbuzz_listener; -use arbiter::arbiter_main; -use renderer::renderer_main; -use modman::modman_main; -use warehouse::setup_warehouse; +use evtbuzz::models::Store; use inference_engine::inference_engine_main; +use log::{ + debug, + error, + info, +}; +use modman::modman_main; +use renderer::renderer_main; +use std::env; +use std::sync::Arc; +use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; +use warehouse::setup_warehouse; use warehouse::warehouse_main; -pub async fn server_main(data_dir: &String, port: u16, cancellation_token: CancellationToken, server_token: CancellationToken) { +pub async fn server_main( + data_dir: &String, + port: u16, + cancellation_token: CancellationToken, + server_token: CancellationToken, +) { info!("Starting CloverHub..."); let ( - store, - master_user_config, + store, + master_user_config, CoreUserConfigs { - evtbuzz: evtbuzz_user_config, - arbiter: arbiter_user_config, + evtbuzz: evtbuzz_user_config, + arbiter: arbiter_user_config, renderer: renderer_user_config, appd: appd_user_config, modman: modman_user_config, inference_engine: inference_engine_user_config, - warehouse: warehouse_user_config - } + warehouse: warehouse_user_config, + }, ) = Store::new_configured_store().await; - if env::var("CLOVER_MASTER_PRINT").unwrap_or("false".to_string()) == "true".to_string() { + if env::var("CLOVER_MASTER_PRINT").unwrap_or("false".to_string()) == "true".to_string() { for core_user in vec![ ("Master", master_user_config.clone()), ("clover-hub.arbiter", arbiter_user_config.clone()), @@ -47,9 +56,17 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance ("clover-hub.appd", appd_user_config.clone()), ("clover-hub.renderer", renderer_user_config.clone()), ("clover-hub.modman", modman_user_config.clone()), - ("clover-hub.inference-engine", inference_engine_user_config.clone()) + ( + "clover-hub.inference-engine", + inference_engine_user_config.clone(), + ), ] { - debug!("\"{}\" user id: \"{}\", primary api key: \"{}\"", core_user.0, core_user.1.id.clone(), core_user.1.api_key.clone()); + debug!( + "\"{}\" user id: \"{}\", primary api key: \"{}\"", + core_user.0, + core_user.1.id.clone(), + core_user.1.api_key.clone() + ); } } @@ -66,7 +83,14 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance let warehouse_tokens = (CancellationToken::new(), CancellationToken::new()); let warehouse_tokens_clone = warehouse_tokens.clone(); let warehouse_handle = tokio::task::spawn(async move { - warehouse_main(warehouse_from_tx, warehouse_to_rx, warehouse_store.clone(), warehouse_uca.clone(), warehouse_tokens_clone).await; + warehouse_main( + warehouse_from_tx, + warehouse_to_rx, + warehouse_store.clone(), + warehouse_uca.clone(), + warehouse_tokens_clone, + ) + .await; }); // Start Arbiter @@ -77,7 +101,14 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance let arbiter_tokens = (CancellationToken::new(), CancellationToken::new()); let arbiter_tokens_clone = arbiter_tokens.clone(); let arbiter_handle = tokio::task::spawn(async move { - arbiter_main(arbiter_from_tx, arbiter_to_rx, arbiter_store.clone(), arbiter_uca.clone(), arbiter_tokens_clone).await; + arbiter_main( + arbiter_from_tx, + arbiter_to_rx, + arbiter_store.clone(), + arbiter_uca.clone(), + arbiter_tokens_clone, + ) + .await; }); // Start Renderer @@ -86,9 +117,16 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance let renderer_store = Arc::new(store.clone()); let renderer_uca = Arc::new(renderer_user_config.clone()); let renderer_tokens = (CancellationToken::new(), CancellationToken::new()); - let renderer_tokens_clone = renderer_tokens.clone(); + let renderer_tokens_clone = renderer_tokens.clone(); let renderer_handle = tokio::task::spawn(async move { - renderer_main(renderer_from_tx, renderer_to_rx, renderer_store.clone(), renderer_uca.clone(), renderer_tokens_clone).await; + renderer_main( + renderer_from_tx, + renderer_to_rx, + renderer_store.clone(), + renderer_uca.clone(), + renderer_tokens_clone, + ) + .await; }); // Start ModMan @@ -99,24 +137,34 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance let modman_tokens = (CancellationToken::new(), CancellationToken::new()); let modman_tokens_clone = modman_tokens.clone(); let modman_handle = tokio::task::spawn(async move { - modman_main(modman_from_tx, modman_to_rx, modman_store.clone(), modman_uca.clone(), modman_tokens_clone).await; + modman_main( + modman_from_tx, + modman_to_rx, + modman_store.clone(), + modman_uca.clone(), + modman_tokens_clone, + ) + .await; }); // Start InferenceEngine - let (inference_engine_from_tx, inference_engine_from_rx) = mpsc::unbounded_channel::(); - let (inference_engine_to_tx, inference_engine_to_rx) = mpsc::unbounded_channel::(); + let (inference_engine_from_tx, inference_engine_from_rx) = + mpsc::unbounded_channel::(); + let (inference_engine_to_tx, inference_engine_to_rx) = + mpsc::unbounded_channel::(); let inference_engine_store = Arc::new(store.clone()); let inference_engine_uca = Arc::new(inference_engine_user_config.clone()); let inference_engine_tokens = (CancellationToken::new(), CancellationToken::new()); let inference_engine_tokens_clone = inference_engine_tokens.clone(); let inference_engine_handle = tokio::task::spawn(async move { inference_engine_main( - inference_engine_from_tx, - inference_engine_to_rx, - inference_engine_store.clone(), - inference_engine_uca.clone(), - inference_engine_tokens_clone - ).await; + inference_engine_from_tx, + inference_engine_to_rx, + inference_engine_store.clone(), + inference_engine_uca.clone(), + inference_engine_tokens_clone, + ) + .await; }); // Start AppDaemon @@ -127,7 +175,14 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance let appd_tokens = (CancellationToken::new(), CancellationToken::new()); let appd_tokens_clone = appd_tokens.clone(); let appd_handle = tokio::task::spawn(async move { - appd_main(appd_from_tx, appd_to_rx, appd_store.clone(), appd_uca.clone(), appd_tokens_clone).await; + appd_main( + appd_from_tx, + appd_to_rx, + appd_store.clone(), + appd_uca.clone(), + appd_tokens_clone, + ) + .await; }); let evtbuzz_port = Arc::new(port); @@ -143,17 +198,42 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance let evtbuzz_tokens_clone = evtbuzz_tokens.clone(); let evtbuzz_handle = tokio::task::spawn(async move { evtbuzz_listener( - *evtbuzz_port.to_owned(), - evtbuzz_store.clone(), - (&evtbuzz_arbiter_user_config_arc.clone(), arbiter_from_rx, arbiter_to_tx), - (&evtbuzz_renderer_user_config_arc.clone(), renderer_from_rx, renderer_to_tx), - (&evtbuzz_modman_user_config_arc.clone(), modman_from_rx, modman_to_tx), - (&evtbuzz_inference_engine_user_config_arc.clone(), inference_engine_from_rx, inference_engine_to_tx), - (&evtbuzz_appd_user_config_arc.clone(), appd_from_rx, appd_to_tx), - (&evtbuzz_warehouse_user_config_arc.clone(), warehouse_from_rx, warehouse_to_tx), + *evtbuzz_port.to_owned(), + evtbuzz_store.clone(), + ( + &evtbuzz_arbiter_user_config_arc.clone(), + arbiter_from_rx, + arbiter_to_tx, + ), + ( + &evtbuzz_renderer_user_config_arc.clone(), + renderer_from_rx, + renderer_to_tx, + ), + ( + &evtbuzz_modman_user_config_arc.clone(), + modman_from_rx, + modman_to_tx, + ), + ( + &evtbuzz_inference_engine_user_config_arc.clone(), + inference_engine_from_rx, + inference_engine_to_tx, + ), + ( + &evtbuzz_appd_user_config_arc.clone(), + appd_from_rx, + appd_to_tx, + ), + ( + &evtbuzz_warehouse_user_config_arc.clone(), + warehouse_from_rx, + warehouse_to_tx, + ), evtbuzz_tokens_clone, - evtbuzz_uca.clone() - ).await; + evtbuzz_uca.clone(), + ) + .await; }); let cleanup_handle = tokio::task::spawn(async move { @@ -210,44 +290,63 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance cleanup_handle, warehouse_handle, evtbuzz_handle, - arbiter_handle, + arbiter_handle, renderer_handle, modman_handle, inference_engine_handle, - appd_handle + appd_handle ]) => { info!("CloverHub Server has exited."); }} - }, + } Err(e) => { match e { warehouse::Error::FailedToCreateDataDir { error } => { - error!("Failed to create data directory! Please create `{}` and set the proper permissions manually, then re-run the server. Failed due to:\n{}", data_dir.clone(), error); - }, + error!( + "Failed to create data directory! Please create `{}` and set the proper permissions manually, then re-run the server. Failed due to:\n{}", + data_dir.clone(), + error + ); + } warehouse::Error::FailedToCreateConfigFile { error } => { - error!("Failed to create the configuration file, due to:\n{}", error); - }, + error!( + "Failed to create the configuration file, due to:\n{}", + error + ); + } warehouse::Error::FailedToOpenConfigFile { error } => { error!("Failed to open config file, due to:\n{}", error); - }, + } warehouse::Error::FailedToParseConfigFile { error } => { error!("Failed to parse configuration file, due to:\n{}", error); - }, + } warehouse::Error::FailedToReadConfigFile { error } => { error!("Failed to read configuration file, due to:\n{}", error); - }, + } warehouse::Error::FailedToWriteToConfigFile { error } => { - error!("Failed to write to the configuration file, due to:\n{}", error); + error!( + "Failed to write to the configuration file, due to:\n{}", + error + ); } warehouse::Error::FailedToCreateReposDir { error } => { - error!("Failed to create the repository storage dir, due to:\n{}", error); - }, + error!( + "Failed to create the repository storage dir, due to:\n{}", + error + ); + } warehouse::Error::FailedToDownloadAndRegisterRepos { error } => { - error!("Failed to download and/or register all repositories, due to:\n{}", error); - }, + error!( + "Failed to download and/or register all repositories, due to:\n{}", + error + ); + } warehouse::Error::FailedToUpdateRepoDirectoryStructure { error } => { - error!("Failed to update the repo directory structure, due to:\n{}", error); - }, + error!( + "Failed to update the repo directory structure, due to:\n{}", + error + ); + } } server_token.cancel(); diff --git a/clover-hub/src/server/modman/busses/models.rs b/clover-hub/src/server/modman/busses/models.rs index b02fecb..4e7ad40 100644 --- a/clover-hub/src/server/modman/busses/models.rs +++ b/clover-hub/src/server/modman/busses/models.rs @@ -14,9 +14,7 @@ pub enum BusTypes { #[cfg(feature = "i2c")] I2C, #[cfg(feature = "uart")] - UART + UART, } -pub trait Bus { - -} +pub trait Bus {} diff --git a/clover-hub/src/server/modman/busses/proxies/can_2.rs b/clover-hub/src/server/modman/busses/proxies/can_2.rs index 9b92193..96dba02 100644 --- a/clover-hub/src/server/modman/busses/proxies/can_2.rs +++ b/clover-hub/src/server/modman/busses/proxies/can_2.rs @@ -1,2 +1,2 @@ -use socketcan; use can; +use socketcan; diff --git a/clover-hub/src/server/modman/busses/proxies/can_fd.rs b/clover-hub/src/server/modman/busses/proxies/can_fd.rs index 9b92193..96dba02 100644 --- a/clover-hub/src/server/modman/busses/proxies/can_fd.rs +++ b/clover-hub/src/server/modman/busses/proxies/can_fd.rs @@ -1,2 +1,2 @@ -use socketcan; use can; +use socketcan; diff --git a/clover-hub/src/server/modman/busses/proxies/i2c.rs b/clover-hub/src/server/modman/busses/proxies/i2c.rs index 3b8a4b7..86d29f9 100644 --- a/clover-hub/src/server/modman/busses/proxies/i2c.rs +++ b/clover-hub/src/server/modman/busses/proxies/i2c.rs @@ -1,2 +1,2 @@ -use i2cdev; use i2c; +use i2cdev; diff --git a/clover-hub/src/server/modman/busses/proxies/mod.rs b/clover-hub/src/server/modman/busses/proxies/mod.rs index b90f5a7..98ae2a2 100644 --- a/clover-hub/src/server/modman/busses/proxies/mod.rs +++ b/clover-hub/src/server/modman/busses/proxies/mod.rs @@ -1,14 +1,14 @@ -#[cfg(feature = "can_fd")] -pub mod can_fd; -#[cfg(feature = "can_2")] -pub mod can_2; #[cfg(feature = "bt_classic")] pub mod bt_classic; #[cfg(feature = "bt_le")] pub mod bt_le; -#[cfg(feature = "spi")] -pub mod spi; +#[cfg(feature = "can_2")] +pub mod can_2; +#[cfg(feature = "can_fd")] +pub mod can_fd; #[cfg(feature = "i2c")] pub mod i2c; +#[cfg(feature = "spi")] +pub mod spi; #[cfg(feature = "uart")] pub mod uart; diff --git a/clover-hub/src/server/modman/components/audio/mod.rs b/clover-hub/src/server/modman/components/audio/mod.rs index e69de29..8b13789 100644 --- a/clover-hub/src/server/modman/components/audio/mod.rs +++ b/clover-hub/src/server/modman/components/audio/mod.rs @@ -0,0 +1 @@ + diff --git a/clover-hub/src/server/modman/components/mod.rs b/clover-hub/src/server/modman/components/mod.rs index 52f3943..13419b9 100644 --- a/clover-hub/src/server/modman/components/mod.rs +++ b/clover-hub/src/server/modman/components/mod.rs @@ -1,5 +1,5 @@ -pub mod movement; -pub mod video; pub mod audio; -pub mod sensors; pub mod models; +pub mod movement; +pub mod sensors; +pub mod video; diff --git a/clover-hub/src/server/modman/components/models.rs b/clover-hub/src/server/modman/components/models.rs index 5f12bbb..251702c 100644 --- a/clover-hub/src/server/modman/components/models.rs +++ b/clover-hub/src/server/modman/components/models.rs @@ -1,3 +1 @@ -pub trait Component { - -} +pub trait Component {} diff --git a/clover-hub/src/server/modman/components/movement/models.rs b/clover-hub/src/server/modman/components/movement/models.rs index dc50149..969a2ef 100644 --- a/clover-hub/src/server/modman/components/movement/models.rs +++ b/clover-hub/src/server/modman/components/movement/models.rs @@ -1,4 +1,2 @@ #[derive(Debug, Clone)] -pub struct MovementComponent { - -} +pub struct MovementComponent {} diff --git a/clover-hub/src/server/modman/components/sensors/mod.rs b/clover-hub/src/server/modman/components/sensors/mod.rs index e69de29..8b13789 100644 --- a/clover-hub/src/server/modman/components/sensors/mod.rs +++ b/clover-hub/src/server/modman/components/sensors/mod.rs @@ -0,0 +1 @@ + diff --git a/clover-hub/src/server/modman/components/video/cameras/mod.rs b/clover-hub/src/server/modman/components/video/cameras/mod.rs index e69de29..8b13789 100644 --- a/clover-hub/src/server/modman/components/video/cameras/mod.rs +++ b/clover-hub/src/server/modman/components/video/cameras/mod.rs @@ -0,0 +1 @@ + diff --git a/clover-hub/src/server/modman/components/video/displays/mod.rs b/clover-hub/src/server/modman/components/video/displays/mod.rs index ff92946..c446ac8 100644 --- a/clover-hub/src/server/modman/components/video/displays/mod.rs +++ b/clover-hub/src/server/modman/components/video/displays/mod.rs @@ -1 +1 @@ -pub mod models; \ No newline at end of file +pub mod models; diff --git a/clover-hub/src/server/modman/components/video/displays/models.rs b/clover-hub/src/server/modman/components/video/displays/models.rs index 03df586..e8aff98 100644 --- a/clover-hub/src/server/modman/components/video/displays/models.rs +++ b/clover-hub/src/server/modman/components/video/displays/models.rs @@ -1,7 +1,10 @@ -use std::{collections::HashMap, num::NonZero}; -use decorum::Real; use crate::server::modman::busses::models::BusTypes; use bevy::prelude::Component; +use decorum::Real; +use std::{ + collections::HashMap, + num::NonZero, +}; #[derive(Debug, Clone)] pub struct DisplayComponent { @@ -11,14 +14,14 @@ pub struct DisplayComponent { pub size: DisplaySize, pub connection: ConnectionType, // If configured, the Entity ID of the VDisplay this physical display is a part of. - pub virtual_display: Option + pub virtual_display: Option, } #[derive(Debug, Clone, Component)] pub struct VirtualDisplayComponent { /// Position and Component ID of the displays that make up this Virtual Display pub displays: HashMap, - pub resolution: DisplayResolution + pub resolution: DisplayResolution, } #[derive(Debug, Clone)] @@ -37,14 +40,14 @@ pub struct DisplayResolution { /// All measurements in CM. Only one is required for square pixels. Otherwise, use both values for rectangular pixels (e.g. 1:2 pixel aspect ratio vs 1:1). pub struct DisplaySize { pub height: Option>, - pub width: Option> + pub width: Option>, } #[derive(Debug, Clone)] pub enum ConnectionType { #[cfg(feature = "compositor")] GPUDirect(DirectConnection), - ModManProxy(ProxiedConnection) + ModManProxy(ProxiedConnection), } #[derive(Debug, Clone)] @@ -52,11 +55,11 @@ pub enum ConnectionType { pub struct DirectConnection { /// Set to `@primary` to use the primary display configured by wayland. pub display_id: String, - pub windowed: bool + pub windowed: bool, } #[derive(Debug, Clone)] pub struct ProxiedConnection { pub bus_type: BusTypes, - pub component_id: String + pub component_id: String, } diff --git a/clover-hub/src/server/modman/components/video/mod.rs b/clover-hub/src/server/modman/components/video/mod.rs index fa49c23..d04b16e 100644 --- a/clover-hub/src/server/modman/components/video/mod.rs +++ b/clover-hub/src/server/modman/components/video/mod.rs @@ -1,2 +1,2 @@ -pub mod displays; pub mod cameras; +pub mod displays; diff --git a/clover-hub/src/server/modman/mod.rs b/clover-hub/src/server/modman/mod.rs index fdd68bf..376e6cd 100644 --- a/clover-hub/src/server/modman/mod.rs +++ b/clover-hub/src/server/modman/mod.rs @@ -1,16 +1,29 @@ -pub mod models; pub mod busses; pub mod components; +pub mod models; -use std::sync::Arc; -use log::{debug, error, info, warn}; +use crate::utils::send_ipc_message; +use log::{ + debug, + error, + info, + warn, +}; use models::Module; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use std::sync::Arc; +use tokio::sync::mpsc::{ + UnboundedReceiver, + UnboundedSender, + unbounded_channel, +}; use tokio_util::sync::CancellationToken; use url::Url; -use crate::utils::send_ipc_message; -use super::evtbuzz::models::{IPCMessageWithId, CoreUserConfig, Store}; +use super::evtbuzz::models::{ + CoreUserConfig, + IPCMessageWithId, + Store, +}; async fn init_module(store: &Store, id: String, module: Module) -> (bool, usize) { let mut initialized_module = module.initialized; @@ -18,16 +31,24 @@ async fn init_module(store: &Store, id: String, module: Module) -> (bool, usize) if !initialized_module { if module.components.len() == 0 { - warn!("Module: {}, does not have any components, skipping.", id.clone()); + warn!( + "Module: {}, does not have any components, skipping.", + id.clone() + ); initialized_module = true; } else { for (component_id, component) in module.components.iter() { // TODO: Add init functions for other component types. } - + if initialized_module_components != module.components.len() { if initialized_module_components > 0 { - warn!("Module: {}, only initialized {} out of {} components!", id.clone(), initialized_module_components, module.components.len()); + warn!( + "Module: {}, only initialized {} out of {} components!", + id.clone(), + initialized_module_components, + module.components.len() + ); initialized_module = true; } else { error!("Module: {}, failed to initialize!", id.clone()); @@ -39,14 +60,21 @@ async fn init_module(store: &Store, id: String, module: Module) -> (bool, usize) if initialized_module { // Update the store with new state of the module. if initialized_module { - store.modules.lock().await.insert(id.clone(), Module { - module_type: module.module_type.clone(), - pretty_name: module.pretty_name.clone(), - initialized: true, - components: module.components.clone(), - registered_by: module.registered_by.clone() - }); - info!("Module: {} ({}), Initialized!", module.pretty_name.clone(), id.clone()); + store.modules.lock().await.insert( + id.clone(), + Module { + module_type: module.module_type.clone(), + pretty_name: module.pretty_name.clone(), + initialized: true, + components: module.components.clone(), + registered_by: module.registered_by.clone(), + }, + ); + info!( + "Module: {} ({}), Initialized!", + module.pretty_name.clone(), + id.clone() + ); } } @@ -54,37 +82,46 @@ async fn init_module(store: &Store, id: String, module: Module) -> (bool, usize) } pub async fn modman_main( - ipc_tx: UnboundedSender, - mut ipc_rx: UnboundedReceiver, - store: Arc, + ipc_tx: UnboundedSender, + mut ipc_rx: UnboundedReceiver, + store: Arc, user_config: Arc, - cancellation_tokens: (CancellationToken, CancellationToken) + cancellation_tokens: (CancellationToken, CancellationToken), ) { info!("Starting ModMan..."); let init_store = Arc::new(store.clone()); let init_user = Arc::new(user_config.clone()); let (init_from_tx, mut init_from_rx) = unbounded_channel::(); - cancellation_tokens.0.run_until_cancelled(async move { - let modules = init_store.modules.lock().await; - if modules.len() > 0 { - // Initialize modules that were registered already via persistence. - for (id, module) in modules.iter() { - info!("Initializing pre configured module: {}:\n type: {}\n name: {}", id.clone(), module.module_type.clone(), module.pretty_name.clone()); - let _components_initialized = init_module(&init_store, id.clone(), module.clone()).await; + cancellation_tokens + .0 + .run_until_cancelled(async move { + let modules = init_store.modules.lock().await; + if modules.len() > 0 { + // Initialize modules that were registered already via persistence. + for (id, module) in modules.iter() { + info!( + "Initializing pre configured module: {}:\n type: {}\n name: {}", + id.clone(), + module.module_type.clone(), + module.pretty_name.clone() + ); + let _components_initialized = init_module(&init_store, id.clone(), module.clone()).await; + } + } else { + info!("No pre-configured modules to initialize."); } - } else { - info!("No pre-configured modules to initialize."); - } - let _ = send_ipc_message( - &init_store, - &init_user, - Arc::new(init_from_tx), - "clover://modman.clover.reboot-codes.com/status".to_string(), - "finished-init".to_string() - ).await; - }).await; + let _ = send_ipc_message( + &init_store, + &init_user, + Arc::new(init_from_tx), + "clover://modman.clover.reboot-codes.com/status".to_string(), + "finished-init".to_string(), + ) + .await; + }) + .await; let ipc_recv_token = cancellation_tokens.0.clone(); let ipc_recv_handle = tokio::task::spawn(async move { @@ -143,7 +180,7 @@ pub async fn modman_main( info!("De-initializing configured module: {}:\n type: {}\n name: {}", id.clone(), module.module_type.clone(), module.pretty_name.clone()); // let (de_initialized, _components_de_initialized) = de_init_module(&store, id.clone(), module.clone()).await; let de_initialized = true; - + // Update the store with new state of the module. if de_initialized { store.modules.lock().await.insert(id.clone(), Module { diff --git a/clover-hub/src/server/modman/models.rs b/clover-hub/src/server/modman/models.rs index e4a984b..cbb24de 100644 --- a/clover-hub/src/server/modman/models.rs +++ b/clover-hub/src/server/modman/models.rs @@ -16,5 +16,5 @@ pub enum Component { Audio, Video, Sensor, - Movement + Movement, } diff --git a/clover-hub/src/server/renderer/mod.rs b/clover-hub/src/server/renderer/mod.rs index 62dd065..548a8cf 100644 --- a/clover-hub/src/server/renderer/mod.rs +++ b/clover-hub/src/server/renderer/mod.rs @@ -2,117 +2,134 @@ mod system_ui; use self::system_ui::system_ui_main; use crate::{ - server::evtbuzz::models::{CoreUserConfig, IPCMessageWithId, Store}, - utils::{send_ipc_message, RecvSync}, + server::evtbuzz::models::{ + CoreUserConfig, + IPCMessageWithId, + Store, + }, + utils::{ + RecvSync, + send_ipc_message, + }, +}; +use log::{ + debug, + info, }; -use log::{debug, info}; use queues::*; use std::sync::Arc; -use system_ui::{CustomBevyIPC, ExitState}; -use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; +use system_ui::{ + CustomBevyIPC, + ExitState, +}; +use tokio::sync::mpsc::{ + UnboundedReceiver, + UnboundedSender, + unbounded_channel, +}; use tokio_util::sync::CancellationToken; use url::Url; pub async fn renderer_main( - ipc_tx: UnboundedSender, - mut ipc_rx: UnboundedReceiver, - store: Arc, - user_config: Arc, - cancellation_tokens: (CancellationToken, CancellationToken), + ipc_tx: UnboundedSender, + mut ipc_rx: UnboundedReceiver, + store: Arc, + user_config: Arc, + cancellation_tokens: (CancellationToken, CancellationToken), ) { - info!("Starting Renderer..."); - - let (bevy_cancel_tx, bevy_cancel_rx) = std::sync::mpsc::channel(); - let bevy_cancel_ipc = CustomBevyIPC { - exit_channel: RecvSync(bevy_cancel_rx), - display_registration_queue: queue![], - }; - - std::thread::spawn(|| system_ui_main(bevy_cancel_ipc)); - - // let display_handles = Arc::new(HashMap::new()); - let (from_tx, mut from_rx) = unbounded_channel::(); - - let init_store = Arc::new(store.clone()); - let init_user = Arc::new(user_config.clone()); - let init_from_tx = from_tx.clone(); - cancellation_tokens - .0 - .run_until_cancelled(async move { - let _ = send_ipc_message( - &init_store, - &init_user, - Arc::new(init_from_tx), - "clover://renderer.clover.reboot-codes.com/status".to_string(), - "finished-init".to_string(), - ) - .await; - }) - .await; - - let ipc_recv_token = cancellation_tokens.0.clone(); - let ipc_recv_handle = tokio::task::spawn(async move { - tokio::select! { - _ = ipc_recv_token.cancelled() => { - debug!("ipc_recv exited"); - }, - _ = async move { - while let Some(msg) = ipc_rx.recv().await { - let kind = Url::parse(&msg.kind.clone()).unwrap(); - - // Verify that we care about this event. - if kind.host().unwrap() == url::Host::Domain("renderer.clover.reboot-codes.com") { - debug!("Processing: {}", msg.kind.clone()); - - if kind.path() == "/register-display" { - - } - } - } - } => {} - } - }); - - let ipc_trans_token = cancellation_tokens.0.clone(); - let ipc_trans_tx = Arc::new(ipc_tx.clone()); - let ipc_trans_handle = tokio::task::spawn(async move { - tokio::select! { - _ = async move { - while let Some(msg) = from_rx.recv().await { - debug!("Sending message {} to IPC bus!", msg.kind.clone()); - match ipc_trans_tx.send(msg) { - Ok(_) => { - debug!("Sent message to IPC bus!"); - }, - Err(_) => { - debug!("Failed to send message to IPC bus!"); + info!("Starting Renderer..."); + + let (bevy_cancel_tx, bevy_cancel_rx) = std::sync::mpsc::channel(); + let bevy_cancel_ipc = CustomBevyIPC { + exit_channel: RecvSync(bevy_cancel_rx), + display_registration_queue: queue![], + }; + + std::thread::spawn(|| system_ui_main(bevy_cancel_ipc)); + + // let display_handles = Arc::new(HashMap::new()); + let (from_tx, mut from_rx) = unbounded_channel::(); + + let init_store = Arc::new(store.clone()); + let init_user = Arc::new(user_config.clone()); + let init_from_tx = from_tx.clone(); + cancellation_tokens + .0 + .run_until_cancelled(async move { + let _ = send_ipc_message( + &init_store, + &init_user, + Arc::new(init_from_tx), + "clover://renderer.clover.reboot-codes.com/status".to_string(), + "finished-init".to_string(), + ) + .await; + }) + .await; + + let ipc_recv_token = cancellation_tokens.0.clone(); + let ipc_recv_handle = tokio::task::spawn(async move { + tokio::select! { + _ = ipc_recv_token.cancelled() => { + debug!("ipc_recv exited"); + }, + _ = async move { + while let Some(msg) = ipc_rx.recv().await { + let kind = Url::parse(&msg.kind.clone()).unwrap(); + + // Verify that we care about this event. + if kind.host().unwrap() == url::Host::Domain("renderer.clover.reboot-codes.com") { + debug!("Processing: {}", msg.kind.clone()); + + if kind.path() == "/register-display" { + } } + } + } => {} + } + }); + + let ipc_trans_token = cancellation_tokens.0.clone(); + let ipc_trans_tx = Arc::new(ipc_tx.clone()); + let ipc_trans_handle = tokio::task::spawn(async move { + tokio::select! { + _ = async move { + while let Some(msg) = from_rx.recv().await { + debug!("Sending message {} to IPC bus!", msg.kind.clone()); + match ipc_trans_tx.send(msg) { + Ok(_) => { + debug!("Sent message to IPC bus!"); + }, + Err(_) => { + debug!("Failed to send message to IPC bus!"); } - } => {}, - _ = ipc_trans_token.cancelled() => { - debug!("ipc_trans exited"); } + } + } => {}, + _ = ipc_trans_token.cancelled() => { + debug!("ipc_trans exited"); } - }); + } + }); - let cleanup_token = cancellation_tokens.0.clone(); - tokio::select! { - _ = cleanup_token.cancelled() => { - ipc_recv_handle.abort(); - ipc_trans_handle.abort(); + let cleanup_token = cancellation_tokens.0.clone(); + tokio::select! { + _ = cleanup_token.cancelled() => { + ipc_recv_handle.abort(); + ipc_trans_handle.abort(); - info!("Cleaning up displays..."); + info!("Cleaning up displays..."); - let _ = bevy_cancel_tx.send(ExitState::Success); + let _ = bevy_cancel_tx.send(ExitState::Success); - // TODO: Clean up registered displays when server is shutting down. + // TODO: Clean up registered displays when server is shutting down. - std::mem::drop(store); + std::mem::drop(store); - cancellation_tokens.1.cancel(); - } - } + cancellation_tokens.1.cancel(); + } + } - info!("Renderer has stopped!"); + info!("Renderer has stopped!"); } diff --git a/clover-hub/src/server/renderer/system_ui/mod.rs b/clover-hub/src/server/renderer/system_ui/mod.rs index 5a55521..c8f205e 100644 --- a/clover-hub/src/server/renderer/system_ui/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/mod.rs @@ -4,74 +4,99 @@ pub mod systems; use crate::server::modman::components::video::displays::models::DisplayComponent; use crate::utils::RecvSync; use bevy::{ - app::{App, Startup}, - diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, - log::LogPlugin, - prelude::*, + app::{ + App, + Startup, + }, + diagnostic::{ + FrameTimeDiagnosticsPlugin, + LogDiagnosticsPlugin, + }, + log::LogPlugin, + prelude::*, }; use queues::*; -use systems::{displays::{vdisplay_registrar, display_registrar}, setup::setup}; +use systems::{ + displays::{ + display_registrar, + vdisplay_registrar, + }, + setup::setup, +}; pub enum ExitState { - Success, - Error, + Success, + Error, } unsafe impl Sync for ExitState {} #[derive(Resource)] pub struct CustomBevyIPC { - pub exit_channel: RecvSync, - pub display_registration_queue: Queue, + pub exit_channel: RecvSync, + pub display_registration_queue: Queue, } pub fn system_ui_main(custom_bevy_ipc: CustomBevyIPC) { - let mut app = App::new(); + let mut app = App::new(); - app.insert_resource(custom_bevy_ipc) - .add_systems(Startup, setup) - .add_systems(Update, (vdisplay_registrar, display_registrar).chain()); + app + .insert_resource(custom_bevy_ipc) + .add_systems(Startup, setup) + .add_systems(Update, (vdisplay_registrar, display_registrar).chain()); - #[cfg(feature = "compositor")] - { - use bevy::{ - window::{PresentMode, Window, WindowPlugin, WindowTheme}, - winit::{WakeUp, WinitPlugin}, - DefaultPlugins, - }; - use systems::simulated_controls::{make_visible, update_scroll_position}; + #[cfg(feature = "compositor")] + { + use bevy::{ + DefaultPlugins, + window::{ + PresentMode, + Window, + WindowPlugin, + WindowTheme, + }, + winit::{ + WakeUp, + WinitPlugin, + }, + }; + use systems::simulated_controls::{ + make_visible, + update_scroll_position, + }; - let mut modded_winit: WinitPlugin = Default::default(); - modded_winit.run_on_any_thread = true; + let mut modded_winit: WinitPlugin = Default::default(); + modded_winit.run_on_any_thread = true; - app.add_plugins(( - DefaultPlugins - .set(WindowPlugin { - primary_window: Some(Window { - title: "Clover Simulated Controls".into(), - resolution: (500., 200.).into(), - present_mode: PresentMode::AutoVsync, - // Tells Wasm to resize the window according to the available canvas - fit_canvas_to_parent: true, - // Tells Wasm not to override default event handling, like F5, Ctrl+R etc. - prevent_default_event_handling: false, - window_theme: Some(WindowTheme::Dark), - enabled_buttons: bevy::window::EnabledButtons { - maximize: false, - ..Default::default() - }, - visible: false, - ..Default::default() - }), - ..Default::default() - }) - .set(modded_winit) - .disable::(), - LogDiagnosticsPlugin::default(), - FrameTimeDiagnosticsPlugin, - )) - .add_systems(Update, (make_visible, update_scroll_position)); - } + app + .add_plugins(( + DefaultPlugins + .set(WindowPlugin { + primary_window: Some(Window { + title: "Clover Simulated Controls".into(), + resolution: (500., 200.).into(), + present_mode: PresentMode::AutoVsync, + // Tells Wasm to resize the window according to the available canvas + fit_canvas_to_parent: true, + // Tells Wasm not to override default event handling, like F5, Ctrl+R etc. + prevent_default_event_handling: false, + window_theme: Some(WindowTheme::Dark), + enabled_buttons: bevy::window::EnabledButtons { + maximize: false, + ..Default::default() + }, + visible: false, + ..Default::default() + }), + ..Default::default() + }) + .set(modded_winit) + .disable::(), + LogDiagnosticsPlugin::default(), + FrameTimeDiagnosticsPlugin, + )) + .add_systems(Update, (make_visible, update_scroll_position)); + } - app.run(); + app.run(); } diff --git a/clover-hub/src/server/renderer/system_ui/plugins/mod.rs b/clover-hub/src/server/renderer/system_ui/plugins/mod.rs index e69de29..8b13789 100644 --- a/clover-hub/src/server/renderer/system_ui/plugins/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/plugins/mod.rs @@ -0,0 +1 @@ + diff --git a/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs index e9c1b9c..c9ee9ee 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs @@ -1,119 +1,118 @@ +use crate::server::renderer::system_ui::CustomBevyIPC; use crate::server::{ - modman::components::video::displays::models::VirtualDisplayComponent, - renderer::system_ui::systems::view_management::Composition + modman::components::video::displays::models::VirtualDisplayComponent, + renderer::system_ui::systems::view_management::Composition, }; -use crate::server::renderer::system_ui::CustomBevyIPC; use bevy::prelude::*; use bevy::render::camera::RenderTarget; #[cfg(feature = "compositor")] -use bevy::window::{Monitor, WindowMode, WindowRef}; +use bevy::window::{ + Monitor, + WindowMode, + WindowRef, +}; use queues::*; -pub fn vdisplay_registrar( - mut commands: Commands, - mut ipc: ResMut, -) { - -} +pub fn vdisplay_registrar(mut commands: Commands, mut ipc: ResMut) {} pub fn display_registrar( - mut commands: Commands, - mut ipc: ResMut, - #[cfg(feature = "compositor")] monitor_entities_query: Query<(Entity, &Monitor)>, - vdisplay_query: Query<(Entity, &mut VirtualDisplayComponent)> + mut commands: Commands, + mut ipc: ResMut, + #[cfg(feature = "compositor")] monitor_entities_query: Query<(Entity, &Monitor)>, + vdisplay_query: Query<(Entity, &mut VirtualDisplayComponent)>, ) { - let display_queue = &mut ipc.display_registration_queue; - - let num_displays = display_queue.size(); + let display_queue = &mut ipc.display_registration_queue; - if num_displays > 0 { - let mut displays = vec![]; - for _ in 0..(num_displays - 1) { - match display_queue.remove() { - Ok(display) => displays.push(display.clone()), - Err(_) => {} - } - } + let num_displays = display_queue.size(); - for display in displays { - match display.connection { - #[cfg(feature = "compositor")] - crate::server::modman::components::video::displays::models::ConnectionType::GPUDirect(direct_connection) => { - let monitor = if direct_connection.display_id.clone() == "@primary" { - MonitorSelection::Primary - } else { - let mut chosen_monitor = None; - for (monitor_eid, monitor) in &monitor_entities_query { - if monitor.name == Some(direct_connection.display_id.clone()) { - chosen_monitor = Some(monitor_eid); - break; - } - } - - match chosen_monitor { - Some(monitor) => { - MonitorSelection::Entity(monitor) - }, - None => { - MonitorSelection::Primary - } - } - }; + if num_displays > 0 { + let mut displays = vec![]; + for _ in 0..(num_displays - 1) { + match display_queue.remove() { + Ok(display) => displays.push(display.clone()), + Err(_) => {} + } + } - let windowed = if direct_connection.windowed { - WindowMode::Windowed - } else { - WindowMode::BorderlessFullscreen(monitor.clone()) - }; - let position = if direct_connection.windowed { - WindowPosition::Centered(monitor.clone()) - } else { - WindowPosition::Automatic - }; + for display in displays { + match display.connection { + #[cfg(feature = "compositor")] + crate::server::modman::components::video::displays::models::ConnectionType::GPUDirect( + direct_connection, + ) => { + let monitor = if direct_connection.display_id.clone() == "@primary" { + MonitorSelection::Primary + } else { + let mut chosen_monitor = None; + for (monitor_eid, monitor) in &monitor_entities_query { + if monitor.name == Some(direct_connection.display_id.clone()) { + chosen_monitor = Some(monitor_eid); + break; + } + } - match display.virtual_display { - Some(vdisplay_id) => { - match vdisplay_query.get(Entity::from_bits(vdisplay_id)) { - Ok(vdisplay) => { - // TODO: Create a camera for this display. + match chosen_monitor { + Some(monitor) => MonitorSelection::Entity(monitor), + None => MonitorSelection::Primary, + } + }; - commands.get_entity(vdisplay.0).unwrap().insert(Window { - title: "Clover SystemUI".into(), - mode: windowed, - position, - ..Default::default() - }); - }, - Err(e) => {} - } - }, - None => { - // TODO: Spawn a composition. Create a camera for this display. + let windowed = if direct_connection.windowed { + WindowMode::Windowed + } else { + WindowMode::BorderlessFullscreen(monitor.clone()) + }; + let position = if direct_connection.windowed { + WindowPosition::Centered(monitor.clone()) + } else { + WindowPosition::Automatic + }; - let window = commands.spawn(Window { - title: format!("Clover SystemUI {}", direct_connection.display_id.clone()), - mode: windowed, - position, - ..Default::default() - }).id(); + match display.virtual_display { + Some(vdisplay_id) => { + match vdisplay_query.get(Entity::from_bits(vdisplay_id)) { + Ok(vdisplay) => { + // TODO: Create a camera for this display. - commands.spawn(( - Camera3d::default(), - Camera { - target: RenderTarget::Window(WindowRef::Entity(window)), - ..default() - }, - Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y) - )); - } - } + commands.get_entity(vdisplay.0).unwrap().insert(Window { + title: "Clover SystemUI".into(), + mode: windowed, + position, + ..Default::default() + }); + } + Err(e) => {} + } + } + None => { + // TODO: Spawn a composition. Create a camera for this display. - // TODO: Show new window! - }, - crate::server::modman::components::video::displays::models::ConnectionType::ModManProxy(proxied_connection) => { + let window = commands + .spawn(Window { + title: format!("Clover SystemUI {}", direct_connection.display_id.clone()), + mode: windowed, + position, + ..Default::default() + }) + .id(); + commands.spawn(( + Camera3d::default(), + Camera { + target: RenderTarget::Window(WindowRef::Entity(window)), + ..default() }, + Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), + )); } + } + + // TODO: Show new window! } + crate::server::modman::components::video::displays::models::ConnectionType::ModManProxy( + proxied_connection, + ) => {} + } } + } } diff --git a/clover-hub/src/server/renderer/system_ui/systems/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/mod.rs index 402a3bb..d90ac1e 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/mod.rs @@ -1,4 +1,4 @@ pub mod displays; -pub mod simulated_controls; pub mod setup; +pub mod simulated_controls; pub mod view_management; diff --git a/clover-hub/src/server/renderer/system_ui/systems/setup.rs b/clover-hub/src/server/renderer/system_ui/systems/setup.rs index 89cfb76..e1c87b5 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/setup.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/setup.rs @@ -4,39 +4,43 @@ use bevy::window::Monitor; use std::env; pub fn setup( - mut commands: Commands, - #[cfg(feature = "compositor")] monitor_entities_query: Query<(Entity, &Monitor)>, + mut commands: Commands, + #[cfg(feature = "compositor")] monitor_entities_query: Query<(Entity, &Monitor)>, ) { - #[cfg(feature = "compositor")] - { - debug!("Available Monitors:"); - for (monitor_eid, monitor) in &monitor_entities_query { - debug!("Monitor {} with EntityID of {}.", monitor.name.clone().unwrap_or("unknown".into()), monitor_eid.to_bits()); - } + #[cfg(feature = "compositor")] + { + debug!("Available Monitors:"); + for (monitor_eid, monitor) in &monitor_entities_query { + debug!( + "Monitor {} with EntityID of {}.", + monitor.name.clone().unwrap_or("unknown".into()), + monitor_eid.to_bits() + ); + } - if &env::var("CLOVER_SIMULATED_CONTROLS").unwrap_or("false".to_string()) == "true" { - // Camera - commands.spawn((Camera2d::default(), IsDefaultUiCamera)); + if &env::var("CLOVER_SIMULATED_CONTROLS").unwrap_or("false".to_string()) == "true" { + // Camera + commands.spawn((Camera2d::default(), IsDefaultUiCamera)); - // root node - commands - .spawn(Node { - width: Val::Percent(100.0), - height: Val::Percent(100.0), - justify_content: JustifyContent::SpaceBetween, - ..default() - }) - .insert(PickingBehavior::IGNORE) - .with_children(|parent| { - parent.spawn(( - Node { - width: Val::Px(200.), - border: UiRect::all(Val::Px(2.)), - ..default() - }, - BackgroundColor(Color::srgb(0.65, 0.65, 0.65)), - )); - }); - } + // root node + commands + .spawn(Node { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + justify_content: JustifyContent::SpaceBetween, + ..default() + }) + .insert(PickingBehavior::IGNORE) + .with_children(|parent| { + parent.spawn(( + Node { + width: Val::Px(200.), + border: UiRect::all(Val::Px(2.)), + ..default() + }, + BackgroundColor(Color::srgb(0.65, 0.65, 0.65)), + )); + }); } + } } diff --git a/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs index 79864e2..7fb7e57 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs @@ -1,48 +1,50 @@ use bevy::{ - core::FrameCount, - ecs::system::Query, - input::mouse::{MouseScrollUnit, MouseWheel}, - picking::focus::HoverMap, - prelude::*, - window::Window, + core::FrameCount, + ecs::system::Query, + input::mouse::{ + MouseScrollUnit, + MouseWheel, + }, + picking::focus::HoverMap, + prelude::*, + window::Window, }; pub fn make_visible(mut window: Query<&mut Window>, frames: Res) { - // info!("Frame: {}", frames.0); - // The delay may be different for your app or system. - if frames.0 == 3 { - // At this point the gpu is ready to show the app so we can make the window visible. - // Alternatively, you could toggle the visibility in Startup. - // It will work, but it will have one white frame before it starts rendering - window.get_single_mut().unwrap().visible = true; - } + // info!("Frame: {}", frames.0); + // The delay may be different for your app or system. + if frames.0 == 3 { + // At this point the gpu is ready to show the app so we can make the window visible. + // Alternatively, you could toggle the visibility in Startup. + // It will work, but it will have one white frame before it starts rendering + window.get_single_mut().unwrap().visible = true; + } } /// Updates the scroll position of scrollable nodes in response to mouse input pub fn update_scroll_position( - mut mouse_wheel_events: EventReader, - hover_map: Res, - mut scrolled_node_query: Query<&mut ScrollPosition>, - keyboard_input: Res>, + mut mouse_wheel_events: EventReader, + hover_map: Res, + mut scrolled_node_query: Query<&mut ScrollPosition>, + keyboard_input: Res>, ) { - for mouse_wheel_event in mouse_wheel_events.read() { - let (mut dx, mut dy) = match mouse_wheel_event.unit { - MouseScrollUnit::Line => (mouse_wheel_event.x * 20., mouse_wheel_event.y * 20.), - MouseScrollUnit::Pixel => (mouse_wheel_event.x, mouse_wheel_event.y), - }; + for mouse_wheel_event in mouse_wheel_events.read() { + let (mut dx, mut dy) = match mouse_wheel_event.unit { + MouseScrollUnit::Line => (mouse_wheel_event.x * 20., mouse_wheel_event.y * 20.), + MouseScrollUnit::Pixel => (mouse_wheel_event.x, mouse_wheel_event.y), + }; - if keyboard_input.pressed(KeyCode::ShiftLeft) || keyboard_input.pressed(KeyCode::ShiftRight) - { - std::mem::swap(&mut dx, &mut dy); - } + if keyboard_input.pressed(KeyCode::ShiftLeft) || keyboard_input.pressed(KeyCode::ShiftRight) { + std::mem::swap(&mut dx, &mut dy); + } - for (_pointer, pointer_map) in hover_map.iter() { - for (entity, _hit) in pointer_map.iter() { - if let Ok(mut scroll_position) = scrolled_node_query.get_mut(*entity) { - scroll_position.offset_x -= dx; - scroll_position.offset_y -= dy; - } - } + for (_pointer, pointer_map) in hover_map.iter() { + for (entity, _hit) in pointer_map.iter() { + if let Ok(mut scroll_position) = scrolled_node_query.get_mut(*entity) { + scroll_position.offset_x -= dx; + scroll_position.offset_y -= dy; } + } } + } } diff --git a/clover-hub/src/server/renderer/system_ui/systems/view_management/compose/components/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/view_management/compose/components/mod.rs index e69de29..8b13789 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/view_management/compose/components/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/view_management/compose/components/mod.rs @@ -0,0 +1 @@ + diff --git a/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs index 87d3f7c..d35894f 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs @@ -11,42 +11,42 @@ use stream::StreamView; /// A view is an area of the system UI that needs to have its components rendered in a display's composition. Depending on the type (Compose, Canvas, or Stream), it may need extra processing. pub trait ViewTrait { - fn new<'a>(commands: &mut Commands<'a, 'a>) -> EntityCommands<'a>; + fn new<'a>(commands: &mut Commands<'a, 'a>) -> EntityCommands<'a>; } pub enum View { - Canvas(CanvasView), - Compose(ComposeView), - Stream(StreamView) + Canvas(CanvasView), + Compose(ComposeView), + Stream(StreamView), } #[derive(Component)] pub struct ViewPosition { - x: f64, - y: f64, - z: f64 + x: f64, + y: f64, + z: f64, } #[derive(Component)] pub struct ViewDimensions { - x: i64, - y: i64, - /// A 0 here will be determined as a 2D view. - z: i64 + x: i64, + y: i64, + /// A 0 here will be determined as a 2D view. + z: i64, } #[derive(Component)] pub struct ViewMixingProps { - visible: bool + visible: bool, } /// Each (virtual) display must have a composition attached to it to have frames ready to send. #[derive(Component)] pub struct Composition { - /// Holds all Entity Ids for the Views within this Compositon. - views: HashMap, - /// Should this composition be trusted with user-focused content? This has nothing to do with being exclusive to CloverHub usage. - internal: bool, + /// Holds all Entity Ids for the Views within this Compositon. + views: HashMap, + /// Should this composition be trusted with user-focused content? This has nothing to do with being exclusive to CloverHub usage. + internal: bool, } /// Themes are set for each composition. A ThemeSet can be loaded for each type of composition. @@ -54,7 +54,7 @@ pub struct Composition { pub struct Theme {} impl Composition { - pub fn new() -> Self { - todo!(); - } + pub fn new() -> Self { + todo!(); + } } diff --git a/clover-hub/src/server/warehouse/config/models.rs b/clover-hub/src/server/warehouse/config/models.rs index 2501088..1ff90fd 100644 --- a/clover-hub/src/server/warehouse/config/models.rs +++ b/clover-hub/src/server/warehouse/config/models.rs @@ -1,35 +1,41 @@ use os_path::OsPath; use sea_orm::DatabaseConnection; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, sync::Arc}; +use serde::{ + Deserialize, + Serialize, +}; +use std::{ + collections::HashMap, + sync::Arc, +}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { - pub docker_daemon: String, - pub repos: HashMap, - #[serde(default)] - pub data_dir: OsPath, - #[serde(skip)] - pub db: Option>, + pub docker_daemon: String, + pub repos: HashMap, + #[serde(default)] + pub data_dir: OsPath, + #[serde(skip)] + pub db: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RepoSpec { - /// Friendly user-set name. (Can override the repo-name if the user so chooses.) - pub name: Option, - /// The source for this repo, be it a local directory, or a remote git repository via HTTP(S) or SSH. - pub src: String, - /// The branch to clone and merge - pub branch: String, + /// Friendly user-set name. (Can override the repo-name if the user so chooses.) + pub name: Option, + /// The source for this repo, be it a local directory, or a remote git repository via HTTP(S) or SSH. + pub src: String, + /// The branch to clone and merge + pub branch: String, } impl Default for Config { - fn default() -> Self { - Config { - docker_daemon: "/run/user/1000/podman/podman.sock".to_string(), - repos: HashMap::new(), - data_dir: OsPath::new(), - db: None, - } + fn default() -> Self { + Config { + docker_daemon: "/run/user/1000/podman/podman.sock".to_string(), + repos: HashMap::new(), + data_dir: OsPath::new(), + db: None, } + } } diff --git a/clover-hub/src/server/warehouse/mod.rs b/clover-hub/src/server/warehouse/mod.rs index 0fd368c..e172f51 100644 --- a/clover-hub/src/server/warehouse/mod.rs +++ b/clover-hub/src/server/warehouse/mod.rs @@ -1,52 +1,57 @@ pub mod config; -pub mod repos; pub mod db; +pub mod repos; +use crate::{ + server::evtbuzz::models::{ + IPCMessageWithId, + Store, + }, + utils::send_ipc_message, +}; use config::models::Config; -use repos::{download_repo_updates, update_repo_dir_structure}; +use log::{ + debug, + error, + info, +}; use os_path::OsPath; +use repos::{ + download_repo_updates, + update_repo_dir_structure, +}; use sea_orm::Database; -use tokio::{io::{AsyncReadExt, AsyncWriteExt}, sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}}; -use tokio_util::sync::CancellationToken; -use url::Url; +use simple_error::SimpleError; use std::sync::Arc; use tokio::fs; -use log::{debug, error, info}; -use simple_error::SimpleError; -use crate::{server::evtbuzz::models::{IPCMessageWithId, Store}, utils::send_ipc_message}; +use tokio::{ + io::{ + AsyncReadExt, + AsyncWriteExt, + }, + sync::mpsc::{ + UnboundedReceiver, + UnboundedSender, + unbounded_channel, + }, +}; +use tokio_util::sync::CancellationToken; +use url::Url; use super::evtbuzz::models::CoreUserConfig; // TODO: Move to snafu crate. #[derive(Debug, Clone)] pub enum Error { - FailedToCreateDataDir { - error: SimpleError - }, - FailedToOpenConfigFile { - error: SimpleError - }, - FailedToCreateConfigFile { - error: SimpleError - }, - FailedToWriteToConfigFile { - error: SimpleError - }, - FailedToParseConfigFile { - error: SimpleError - }, - FailedToReadConfigFile { - error: SimpleError - }, - FailedToCreateReposDir { - error: SimpleError - }, - FailedToDownloadAndRegisterRepos { - error: SimpleError - }, - FailedToUpdateRepoDirectoryStructure { - error: SimpleError - } + FailedToCreateDataDir { error: SimpleError }, + FailedToOpenConfigFile { error: SimpleError }, + FailedToCreateConfigFile { error: SimpleError }, + FailedToWriteToConfigFile { error: SimpleError }, + FailedToParseConfigFile { error: SimpleError }, + FailedToReadConfigFile { error: SimpleError }, + FailedToCreateReposDir { error: SimpleError }, + FailedToDownloadAndRegisterRepos { error: SimpleError }, + FailedToUpdateRepoDirectoryStructure { error: SimpleError }, } pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), Error> { @@ -61,11 +66,15 @@ pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), match fs::create_dir_all(data_dir.clone()).await { Ok(_) => { if !data_dir_path.exists() { - err = Some(Error::FailedToCreateDataDir { error: SimpleError::new("Check failed after creation!") }); + err = Some(Error::FailedToCreateDataDir { + error: SimpleError::new("Check failed after creation!"), + }); } - }, + } Err(e) => { - err = Some(Error::FailedToCreateDataDir { error: SimpleError::from(e) }); + err = Some(Error::FailedToCreateDataDir { + error: SimpleError::from(e), + }); } } } @@ -75,22 +84,33 @@ pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), // Read configuration and load defaults otherwise let config_file_path = warehouse_path.join("/config.jsonc"); match err { - Some(_) => {}, + Some(_) => {} None => { if !config_file_path.exists() { match fs::File::create(config_file_path.clone()).await { Ok(mut file) => { - match file.write_all(serde_jsonc::to_string::(&Default::default()).unwrap().as_bytes()).await { + match file + .write_all( + serde_jsonc::to_string::(&Default::default()) + .unwrap() + .as_bytes(), + ) + .await + { Ok(_) => { info!("Wrote default config!"); - }, + } Err(e) => { - err = Some(Error::FailedToWriteToConfigFile { error: SimpleError::from(e) }) + err = Some(Error::FailedToWriteToConfigFile { + error: SimpleError::from(e), + }) } } - }, + } Err(e) => { - err = Some(Error::FailedToCreateConfigFile { error: SimpleError::from(e) }) + err = Some(Error::FailedToCreateConfigFile { + error: SimpleError::from(e), + }) } } } @@ -98,32 +118,36 @@ pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), } match err { - Some(_) => {}, - None => { + Some(_) => {} + None => { match fs::File::open(config_file_path).await { Ok(mut config_file) => { let mut contents = String::new(); // TODO: Add repair option to fix broken config files. match config_file.read_to_string(&mut contents).await { - Ok(_) => { - match serde_jsonc::from_str::(&contents) { - Ok(config_values) => { - *store.config.lock().await = config_values; - debug!("Loaded config!"); - }, - Err(e) => { - err = Some(Error::FailedToParseConfigFile { error: SimpleError::from(e) }) - } + Ok(_) => match serde_jsonc::from_str::(&contents) { + Ok(config_values) => { + *store.config.lock().await = config_values; + debug!("Loaded config!"); + } + Err(e) => { + err = Some(Error::FailedToParseConfigFile { + error: SimpleError::from(e), + }) } }, Err(e) => { - err = Some(Error::FailedToReadConfigFile { error: SimpleError::from(e) }) + err = Some(Error::FailedToReadConfigFile { + error: SimpleError::from(e), + }) } } - }, + } Err(e) => { - err = Some(Error::FailedToOpenConfigFile { error: SimpleError::from(e) }) + err = Some(Error::FailedToOpenConfigFile { + error: SimpleError::from(e), + }) } } } @@ -134,15 +158,17 @@ pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), // Read repo data and load into applicable areas in the store. let repo_dir_path = warehouse_path.join("/repos/"); match err { - Some(_) => {}, + Some(_) => {} None => { if !repo_dir_path.exists() { match fs::create_dir(repo_dir_path.clone()).await { Ok(_) => { debug!("Created repo directory!"); - }, + } Err(e) => { - err = Some(Error::FailedToCreateReposDir { error: SimpleError::from(e) }); + err = Some(Error::FailedToCreateReposDir { + error: SimpleError::from(e), + }); } } } @@ -152,26 +178,24 @@ pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), debug!("Updating repo dir structure..."); match err { - Some(_) => {}, - None => { - match update_repo_dir_structure(repo_dir_path.clone(), store.clone()).await { - Ok(_) => { - debug!("Updated repo dir structure, downloading repo updates..."); + Some(_) => {} + None => match update_repo_dir_structure(repo_dir_path.clone(), store.clone()).await { + Ok(_) => { + debug!("Updated repo dir structure, downloading repo updates..."); - match download_repo_updates(store.clone(), repo_dir_path.clone()).await { - Ok(_) => { - info!("Loaded {} repo(s)!", store.repos.lock().await.len()); - }, - Err(error) => { - err = Some(Error::FailedToDownloadAndRegisterRepos { error: error.0 }); - } + match download_repo_updates(store.clone(), repo_dir_path.clone()).await { + Ok(_) => { + info!("Loaded {} repo(s)!", store.repos.lock().await.len()); + } + Err(error) => { + err = Some(Error::FailedToDownloadAndRegisterRepos { error: error.0 }); } - }, - Err(error) => { - err = Some(Error::FailedToUpdateRepoDirectoryStructure { error: error.0 }); } } - } + Err(error) => { + err = Some(Error::FailedToUpdateRepoDirectoryStructure { error: error.0 }); + } + }, } store.clone().config.lock().await.data_dir = OsPath::new().join(data_dir.clone()); @@ -181,44 +205,52 @@ pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), // Return any errors if they occurred match err { Some(e) => Err(e), - None => Ok(()) + None => Ok(()), } } pub async fn warehouse_main( - ipc_tx: UnboundedSender, - mut ipc_rx: UnboundedReceiver, - store: Arc, + ipc_tx: UnboundedSender, + mut ipc_rx: UnboundedReceiver, + store: Arc, user_config: Arc, - cancellation_tokens: (CancellationToken, CancellationToken) + cancellation_tokens: (CancellationToken, CancellationToken), ) { info!("Starting Warehouse..."); - let db_raw = Database::connect(format!("sqlite://{}?mode=rwc", store.config.lock().await.data_dir.join("/db.sqlite"))).await; + let db_raw = Database::connect(format!( + "sqlite://{}?mode=rwc", + store.config.lock().await.data_dir.join("/db.sqlite") + )) + .await; let init_store = Arc::new(store.clone()); let init_user = Arc::new(user_config.clone()); let (init_from_tx, mut init_from_rx) = unbounded_channel::(); let init_tokens = cancellation_tokens.clone(); - cancellation_tokens.0.run_until_cancelled(async move { - match db_raw { - Ok(db) => { - init_store.config.lock().await.db = Some(Arc::new(db)); - }, - Err(e) => { - error!("Failed to access db file, due to:\n{}", e); - init_tokens.0.cancel(); + cancellation_tokens + .0 + .run_until_cancelled(async move { + match db_raw { + Ok(db) => { + init_store.config.lock().await.db = Some(Arc::new(db)); + } + Err(e) => { + error!("Failed to access db file, due to:\n{}", e); + init_tokens.0.cancel(); + } } - } - let _ = send_ipc_message( - &init_store, - &init_user, - Arc::new(init_from_tx), - "clover://warehouse.clover.reboot-codes.com/status".to_string(), - "finished-init".to_string() - ).await; - }).await; + let _ = send_ipc_message( + &init_store, + &init_user, + Arc::new(init_from_tx), + "clover://warehouse.clover.reboot-codes.com/status".to_string(), + "finished-init".to_string(), + ) + .await; + }) + .await; let ipc_recv_token = cancellation_tokens.0.clone(); let ipc_recv_handle = tokio::task::spawn(async move { diff --git a/clover-hub/src/server/warehouse/repos/impls.rs b/clover-hub/src/server/warehouse/repos/impls.rs index 862c554..a3900e2 100644 --- a/clover-hub/src/server/warehouse/repos/impls.rs +++ b/clover-hub/src/server/warehouse/repos/impls.rs @@ -1,162 +1,214 @@ -use std::collections::HashMap; +use super::{ + models::*, + replace_simple_directives, + resolve_entry_value, + resolve_list_entry, +}; +use crate::server::{ + appd::models::{ + BuildConfig, + RepoCreds, + }, + warehouse::repos::builtin_rfqdn, +}; use log::debug; +use os_path::OsPath; use serde::Deserialize; use simple_error::SimpleError; -use os_path::OsPath; -use crate::server::{appd::models::{BuildConfig, RepoCreds}, warehouse::repos::builtin_rfqdn}; -use super::{models::*, replace_simple_directives, resolve_entry_value, resolve_list_entry}; +use std::collections::HashMap; impl ManifestCompilationFrom> for OptionalString { - async fn compile(spec: Option, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { - let mut err = None; - - let res = match spec.clone() { - Some(raw_str) => { - match resolve_entry_value(raw_str, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedMultiple(_) => { - err = Some(SimpleError::new("Glob import not supported at this level!")); - OptionalString::None - }, - Resolution::ImportedSingle((here, imported)) => { - match serde_jsonc::from_str::(&imported) { - Ok(val) => { - OptionalString::Some(val) - }, - Err(e) => { - err = Some(SimpleError::new(format!("OptionalString, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - OptionalString::None - } - } - }, - Resolution::NoImport(val) => { - OptionalString::Some(val) - } - } + async fn compile( + spec: Option, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { + let mut err = None; + + let res = match spec.clone() { + Some(raw_str) => { + match resolve_entry_value(raw_str, resolution_ctx.clone(), repo_dir_path.clone()).await { + Ok(resolution) => match resolution { + Resolution::ImportedMultiple(_) => { + err = Some(SimpleError::new("Glob import not supported at this level!")); + OptionalString::None + } + Resolution::ImportedSingle((here, imported)) => { + match serde_jsonc::from_str::(&imported) { + Ok(val) => OptionalString::Some(val), + Err(e) => { + err = Some(SimpleError::new(format!( + "OptionalString, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here }, - Err(e) => { - err = Some(e); - OptionalString::None - } + e + ))); + OptionalString::None } - }, - None => { OptionalString::None } - }; - - match err { - Some(e) => { Err(e) }, - None => { Ok(res) } + } + } + Resolution::NoImport(val) => OptionalString::Some(val), + }, + Err(e) => { + err = Some(e); + OptionalString::None + } } + } + None => OptionalString::None, + }; + + match err { + Some(e) => Err(e), + None => Ok(res), } + } } -impl Deserialize<'a>, K: ManifestCompilationFrom> ManifestCompilationFrom> for Optional { - async fn compile(spec: OptionalSingleManifestSpecEntry, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { - let mut err = None; +impl Deserialize<'a>, K: ManifestCompilationFrom> + ManifestCompilationFrom> for Optional +{ + async fn compile( + spec: OptionalSingleManifestSpecEntry, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { + let mut err = None; - let res = match spec.clone() { - OptionalSingleManifestSpecEntry::Some(raw_val) => { - match K::compile(raw_val, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(val) => { - Optional::Some(val) + let res = match spec.clone() { + OptionalSingleManifestSpecEntry::Some(raw_val) => { + match K::compile(raw_val, resolution_ctx.clone(), repo_dir_path.clone()).await { + Ok(val) => Optional::Some(val), + Err(e) => { + err = Some(e); + Optional::None + } + } + } + OptionalSingleManifestSpecEntry::ImportString(raw_str) => { + match resolve_entry_value(raw_str, resolution_ctx.clone(), repo_dir_path.clone()).await { + Ok(resolution) => match resolution { + Resolution::ImportedMultiple(_) => { + err = Some(SimpleError::new("Glob import not supported at this level!")); + Optional::None + } + Resolution::ImportedSingle((here, imported)) => { + match serde_jsonc::from_str(&imported) { + Ok(raw_val) => { + match K::compile( + raw_val, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here, }, + repo_dir_path.clone(), + ) + .await + { + Ok(val) => Optional::Some(val), Err(e) => { - err = Some(e); - Optional::None + err = Some(e); + Optional::None } + } } - }, - OptionalSingleManifestSpecEntry::ImportString(raw_str) => { - match resolve_entry_value(raw_str, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedMultiple(_) => { - err = Some(SimpleError::new("Glob import not supported at this level!")); - Optional::None - }, - Resolution::ImportedSingle((here, imported)) => { - match serde_jsonc::from_str(&imported) { - Ok(raw_val) => { - match K::compile(raw_val, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, repo_dir_path.clone()).await { - Ok(val) => { - Optional::Some(val) - }, - Err(e) => { - err = Some(e); - Optional::None - } - } - }, - Err(e) => { - err = Some(SimpleError::new(format!("Optional, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - Optional::None - } - } - }, - Resolution::NoImport(val_str) => { - match serde_jsonc::from_str(&val_str) { - Ok(raw_val) => { - match K::compile(raw_val, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(val) => { - Optional::Some(val) - }, - Err(e) => { - err = Some(e); - Optional::None - } - } - }, - Err(e) => { - err = Some(SimpleError::new(format!("Optional, ctx: {:#?}\nerr: {}", resolution_ctx.clone(), e))); - Optional::None - } - } - } - } + Err(e) => { + err = Some(SimpleError::new(format!( + "Optional, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here }, - Err(e) => { - err = Some(e); - Optional::None - } + e + ))); + Optional::None + } + } + } + Resolution::NoImport(val_str) => match serde_jsonc::from_str(&val_str) { + Ok(raw_val) => { + match K::compile(raw_val, resolution_ctx.clone(), repo_dir_path.clone()).await { + Ok(val) => Optional::Some(val), + Err(e) => { + err = Some(e); + Optional::None + } } + } + Err(e) => { + err = Some(SimpleError::new(format!( + "Optional, ctx: {:#?}\nerr: {}", + resolution_ctx.clone(), + e + ))); + Optional::None + } }, - OptionalSingleManifestSpecEntry::None => { Optional::None } - }; - - match err { - Some(e) => { Err(e) }, - None => { Ok(res) } + }, + Err(e) => { + err = Some(e); + Optional::None + } } + } + OptionalSingleManifestSpecEntry::None => Optional::None, + }; + + match err { + Some(e) => Err(e), + None => Ok(res), } + } } impl ManifestCompilationFrom for RequiredString { - async fn compile(spec: String, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { + async fn compile( + spec: String, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { let mut err = None; - let res = match resolve_entry_value(spec.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedMultiple(_) => { - err = Some(SimpleError::new("Glob import not supported at this level!")); - Default::default() - }, - Resolution::ImportedSingle((here, imported)) => { - match serde_jsonc::from_str::(&imported) { - Ok(val) => { - RequiredString(val) - }, - Err(e) => { - err = Some(SimpleError::new(format!("RequiredString, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - Default::default() - } + let res = match resolve_entry_value(spec.clone(), resolution_ctx.clone(), repo_dir_path.clone()) + .await + { + Ok(resolution) => match resolution { + Resolution::ImportedMultiple(_) => { + err = Some(SimpleError::new("Glob import not supported at this level!")); + Default::default() + } + Resolution::ImportedSingle((here, imported)) => { + match serde_jsonc::from_str::(&imported) { + Ok(val) => RequiredString(val), + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredString, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here + }, + e + ))); + Default::default() } - }, - Resolution::NoImport(val) => { - RequiredString(val) } } + Resolution::NoImport(val) => RequiredString(val), }, Err(e) => { err = Some(e); @@ -165,14 +217,21 @@ impl ManifestCompilationFrom for RequiredString { }; match err { - Some(e) => { Err(e) }, - None => { Ok(res) } + Some(e) => Err(e), + None => Ok(res), } } } impl ManifestCompilationFrom for OptionalStrStrHashMap { - async fn compile(spec: OptionalStringListManifestSpecEntry, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { + async fn compile( + spec: OptionalStringListManifestSpecEntry, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { let mut err = None; let ret: OptionalStrStrHashMap = match spec.clone() { @@ -182,95 +241,113 @@ impl ManifestCompilationFrom for OptionalSt for (raw_intent_id, raw_intent) in raw_intents { let intent_id = replace_simple_directives(raw_intent_id, resolution_ctx.clone()); - match resolve_entry_value(raw_intent.try_into().unwrap(), resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedMultiple(_) => { - err = Some(SimpleError::new("Glob import not supported at this level!")); - break; - }, - Resolution::ImportedSingle((here, imported)) => { - match serde_jsonc::from_str::(&imported) { - Ok(val) => { - entries.insert(intent_id, val); - }, - Err(e) => { - err = Some(SimpleError::new(format!("OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - break; - } + match resolve_entry_value( + raw_intent.try_into().unwrap(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { + Ok(resolution) => match resolution { + Resolution::ImportedMultiple(_) => { + err = Some(SimpleError::new("Glob import not supported at this level!")); + break; + } + Resolution::ImportedSingle((here, imported)) => { + match serde_jsonc::from_str::(&imported) { + Ok(val) => { + entries.insert(intent_id, val); + } + Err(e) => { + err = Some(SimpleError::new(format!( + "OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here + }, + e + ))); + break; } - }, - Resolution::NoImport(val) => { - entries.insert(intent_id, val); } } + Resolution::NoImport(val) => { + entries.insert(intent_id, val); + } }, Err(e) => { err = Some(e); - break + break; } } } match err { - Some(_) => { - OptionalStrStrHashMap::None - }, - None => { - OptionalStrStrHashMap::Some(entries) - } + Some(_) => OptionalStrStrHashMap::None, + None => OptionalStrStrHashMap::Some(entries), } - }, + } OptionalStringListManifestSpecEntry::ImportString(import_str) => { match resolve_entry_value(import_str, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedSingle((here, raw_val)) => { - match serde_jsonc::from_str::>(&raw_val) { - Ok(val) => { - OptionalStrStrHashMap::Some(val) - }, - Err(e) => { - err = Some(SimpleError::new(format!("OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - OptionalStrStrHashMap::None - } - } - }, - Resolution::ImportedMultiple((here, raw_vals)) => { - let mut entries = HashMap::new(); - - for (val_key, raw_val) in raw_vals { - match serde_jsonc::from_str::(&raw_val) { - Ok(val) => { - entries.insert(val_key, val); + Ok(resolution) => match resolution { + Resolution::ImportedSingle((here, raw_val)) => { + match serde_jsonc::from_str::>(&raw_val) { + Ok(val) => OptionalStrStrHashMap::Some(val), + Err(e) => { + err = Some(SimpleError::new(format!( + "OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here }, - Err(e) => { - err = Some(SimpleError::new(format!("OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - break; - } - } + e + ))); + OptionalStrStrHashMap::None } + } + } + Resolution::ImportedMultiple((here, raw_vals)) => { + let mut entries = HashMap::new(); - match err { - Some(_) => { - OptionalStrStrHashMap::None - }, - None => { - OptionalStrStrHashMap::Some(entries) - } - } - }, - Resolution::NoImport(raw_val) => { - match serde_jsonc::from_str::>(&raw_val) { + for (val_key, raw_val) in raw_vals { + match serde_jsonc::from_str::(&raw_val) { Ok(val) => { - OptionalStrStrHashMap::Some(val) - }, + entries.insert(val_key, val); + } Err(e) => { - err = Some(SimpleError::new(format!("OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", resolution_ctx.clone(), e))); - OptionalStrStrHashMap::None + err = Some(SimpleError::new(format!( + "OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here + }, + e + ))); + break; } } } + + match err { + Some(_) => OptionalStrStrHashMap::None, + None => OptionalStrStrHashMap::Some(entries), + } + } + Resolution::NoImport(raw_val) => { + match serde_jsonc::from_str::>(&raw_val) { + Ok(val) => OptionalStrStrHashMap::Some(val), + Err(e) => { + err = Some(SimpleError::new(format!( + "OptionalStrStrHashMap, ctx: {:#?}\nerr: {}", + resolution_ctx.clone(), + e + ))); + OptionalStrStrHashMap::None + } + } } }, Err(e) => { @@ -278,220 +355,354 @@ impl ManifestCompilationFrom for OptionalSt OptionalStrStrHashMap::None } } - }, - OptionalStringListManifestSpecEntry::None => { OptionalStrStrHashMap::None } + } + OptionalStringListManifestSpecEntry::None => OptionalStrStrHashMap::None, }; match err { - Some(e) => { Err(e) }, - None => { Ok(ret) } + Some(e) => Err(e), + None => Ok(ret), } } } -impl ManifestCompilationFrom> for OptionalStrTHashMap where - K: ManifestCompilationFrom + std::fmt::Debug, T: for<'a> Deserialize<'a> +impl ManifestCompilationFrom> + for OptionalStrTHashMap +where + K: ManifestCompilationFrom + std::fmt::Debug, + T: for<'a> Deserialize<'a>, { - async fn compile(spec: OptionalListManifestSpecEntry, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { + async fn compile( + spec: OptionalListManifestSpecEntry, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { let mut err = None; let mut entries = OptionalStrTHashMap::None; debug!("OptionalStrTHashMap (initial val): {:#?}", spec.clone()); match spec { - OptionalListManifestSpecEntry::Some(hash_map) => { - debug!("OptionalListManifestSpecEntry::Some: {:#?}", hash_map.clone()); + OptionalListManifestSpecEntry::Some(hash_map) => { + debug!( + "OptionalListManifestSpecEntry::Some: {:#?}", + hash_map.clone() + ); - match resolve_list_entry(hash_map, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(list) => { - debug!("serde_jsonc::from_str(): {:#?}", (&list)); + match resolve_list_entry(hash_map, resolution_ctx.clone(), repo_dir_path.clone()).await { + Ok(list) => { + debug!("serde_jsonc::from_str(): {:#?}", (&list)); - entries = OptionalStrTHashMap::Some(list); - }, - Err(e) => { - err = Some(e); - } + entries = OptionalStrTHashMap::Some(list); } - }, - OptionalListManifestSpecEntry::ImportString(raw_str) => { - debug!("OptionalListManifestSpecEntry::ImportString: {}", raw_str.clone()); - - match resolve_entry_value(raw_str.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedSingle((here, res_str)) => { - match serde_jsonc::from_str::>>(&res_str) { - Ok(hash_map) => { - debug!("serde_jsonc::from_str(): {:#?}", hash_map.clone()); - - match resolve_list_entry(hash_map, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, repo_dir_path.clone()).await { - Ok(list) => { - entries = OptionalStrTHashMap::Some(list); - }, - Err(e) => { - err = Some(e); - } - } + Err(e) => { + err = Some(e); + } + } + } + OptionalListManifestSpecEntry::ImportString(raw_str) => { + debug!( + "OptionalListManifestSpecEntry::ImportString: {}", + raw_str.clone() + ); + + match resolve_entry_value( + raw_str.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { + Ok(resolution) => match resolution { + Resolution::ImportedSingle((here, res_str)) => { + match serde_jsonc::from_str::>>( + &res_str, + ) { + Ok(hash_map) => { + debug!("serde_jsonc::from_str(): {:#?}", hash_map.clone()); + + match resolve_list_entry( + hash_map, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here, }, + repo_dir_path.clone(), + ) + .await + { + Ok(list) => { + entries = OptionalStrTHashMap::Some(list); + } Err(e) => { - err = Some(SimpleError::new(format!("OptionalStrTHashMap, ctx: {:#?}\nerr: {}", resolution_ctx.clone(), e))); + err = Some(e); } } - }, - Resolution::ImportedMultiple(_) => { - err = Some(SimpleError::new("Glob imports are not supported at this level.")) - }, - Resolution::NoImport(_) => { - err = Some(SimpleError::new("A string is not a valid value for this field unless it's an import.")); - }, + } + Err(e) => { + err = Some(SimpleError::new(format!( + "OptionalStrTHashMap, ctx: {:#?}\nerr: {}", + resolution_ctx.clone(), + e + ))); + } } - }, - Err(e) => { - debug!("OptionalListManifestSpecEntry::ImportString => resolve_list_entry(), failed: {}", e); - err = Some(e); } + Resolution::ImportedMultiple(_) => { + err = Some(SimpleError::new( + "Glob imports are not supported at this level.", + )) + } + Resolution::NoImport(_) => { + err = Some(SimpleError::new( + "A string is not a valid value for this field unless it's an import.", + )); + } + }, + Err(e) => { + debug!( + "OptionalListManifestSpecEntry::ImportString => resolve_list_entry(), failed: {}", + e + ); + err = Some(e); } - }, - OptionalListManifestSpecEntry::None => { - debug!("{:#?} => Empty manifest entry!", spec.clone()); - }, // No-op. + } + } + OptionalListManifestSpecEntry::None => { + debug!("{:#?} => Empty manifest entry!", spec.clone()); + } // No-op. } match err { - Some(e) => { Err(e) }, - None => { Ok(entries) } + Some(e) => Err(e), + None => Ok(entries), } } } -impl Deserialize<'a>, K: ManifestCompilationFrom + for<'a> Deserialize<'a>> ManifestCompilationFrom> for RequiredStrTHashMap { - async fn compile(spec: RequiredListManifestSpecEntry, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { +impl Deserialize<'a>, K: ManifestCompilationFrom + for<'a> Deserialize<'a>> + ManifestCompilationFrom> for RequiredStrTHashMap +{ + async fn compile( + spec: RequiredListManifestSpecEntry, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { let mut err = None; let res = match spec.clone() { RequiredListManifestSpecEntry::ImportString(spec_str) => { - match resolve_entry_value(spec_str.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedMultiple(_) => { - err = Some(SimpleError::new("Glob import not supported at this level!")); - RequiredStrTHashMap(HashMap::new()) - }, - Resolution::ImportedSingle((here, imported)) => { - match serde_jsonc::from_str::>>(&imported) { - Ok(spec_list) => { - let mut entries = HashMap::new(); - - for (entry_id, raw_entry) in spec_list { - match raw_entry { - RequiredSingleManifestEntry::Some(raw_entry_obj) => { - match K::compile(raw_entry_obj, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, repo_dir_path.clone()).await { - Ok(val) => { - entries.insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); - }, - Err(e) => { - err = Some(e); - } + match resolve_entry_value( + spec_str.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { + Ok(resolution) => match resolution { + Resolution::ImportedMultiple(_) => { + err = Some(SimpleError::new("Glob import not supported at this level!")); + RequiredStrTHashMap(HashMap::new()) + } + Resolution::ImportedSingle((here, imported)) => { + match serde_jsonc::from_str::>>( + &imported, + ) { + Ok(spec_list) => { + let mut entries = HashMap::new(); + + for (entry_id, raw_entry) in spec_list { + match raw_entry { + RequiredSingleManifestEntry::Some(raw_entry_obj) => { + match K::compile( + raw_entry_obj, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), + }, + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + entries + .insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); } - }, - RequiredSingleManifestEntry::ImportString(raw_entry_str) => { - match resolve_entry_value(raw_entry_str, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedSingle((here, val_str)) => { - match serde_jsonc::from_str(&val_str) { - Ok(raw_val) => { - match K::compile(raw_val, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, repo_dir_path.clone()).await { - Ok(val) => { - entries.insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); - }, - Err(e) => { - err = Some(e); - } - } + Err(e) => { + err = Some(e); + } + } + } + RequiredSingleManifestEntry::ImportString(raw_entry_str) => { + match resolve_entry_value( + raw_entry_str, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), + }, + repo_dir_path.clone(), + ) + .await + { + Ok(resolution) => match resolution { + Resolution::ImportedSingle((here, val_str)) => { + match serde_jsonc::from_str(&val_str) { + Ok(raw_val) => { + match K::compile( + raw_val, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), }, + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + entries.insert( + entry_id.clone(), + RequiredSingleManifestEntry::Some(val), + ); + } Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, e))); + err = Some(e); } } - }, - Resolution::ImportedMultiple((here, hash_map)) => { - for (val_id, val_str) in hash_map { - match serde_jsonc::from_str(&val_str) { - Ok(raw_val) => { - match K::compile(raw_val, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, repo_dir_path.clone()).await { - Ok(val) => { - entries.insert(val_id, RequiredSingleManifestEntry::Some(val)); - }, - Err(e) => { - err = Some(e); - } - } + } + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone() + }, + e + ))); + } + } + } + Resolution::ImportedMultiple((here, hash_map)) => { + for (val_id, val_str) in hash_map { + match serde_jsonc::from_str(&val_str) { + Ok(raw_val) => { + match K::compile( + raw_val, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), }, + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + entries + .insert(val_id, RequiredSingleManifestEntry::Some(val)); + } Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, e))); + err = Some(e); } } } - }, - Resolution::NoImport(val_str) => { - match serde_jsonc::from_str(&val_str) { - Ok(raw_val) => { - match K::compile(raw_val, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(val) => { - entries.insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); - }, - Err(e) => { - err = Some(e); - } - } - }, + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone() + }, + e + ))); + } + } + } + } + Resolution::NoImport(val_str) => { + match serde_jsonc::from_str(&val_str) { + Ok(raw_val) => { + match K::compile( + raw_val, + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + entries.insert( + entry_id.clone(), + RequiredSingleManifestEntry::Some(val), + ); + } Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap, ctx: {:#?}\nerr: {}", resolution_ctx.clone(), e))); + err = Some(e); } } - }, + } + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap, ctx: {:#?}\nerr: {}", + resolution_ctx.clone(), + e + ))); + } } - }, - Err(e) => { - err = Some(e); } + }, + Err(e) => { + err = Some(e); } - }, + } } } + } - if err != None { - RequiredStrTHashMap(HashMap::new()) - } else { - RequiredStrTHashMap(entries) - } - }, - Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap, ctx: {:#?}\nerr: {}", resolution_ctx.clone().base, e))); + if err != None { RequiredStrTHashMap(HashMap::new()) + } else { + RequiredStrTHashMap(entries) } } - }, - Resolution::NoImport(raw_val) => { - match serde_jsonc::from_str(&raw_val) { - Ok(val) => { - RequiredStrTHashMap(val) - }, - Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap, ctx: {:#?}\nerr: {}", resolution_ctx.clone().base, e))); - RequiredStrTHashMap(HashMap::new()) - } + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap, ctx: {:#?}\nerr: {}", + resolution_ctx.clone().base, + e + ))); + RequiredStrTHashMap(HashMap::new()) } } } + Resolution::NoImport(raw_val) => match serde_jsonc::from_str(&raw_val) { + Ok(val) => RequiredStrTHashMap(val), + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap, ctx: {:#?}\nerr: {}", + resolution_ctx.clone().base, + e + ))); + RequiredStrTHashMap(HashMap::new()) + } + }, }, Err(e) => { err = Some(e); RequiredStrTHashMap(HashMap::new()) } } - }, + } RequiredListManifestSpecEntry::Some(spec_list) => { let mut entries = HashMap::new(); @@ -501,76 +712,120 @@ impl Deserialize<'a>, K: ManifestCompilationFrom + for<'a> match K::compile(raw_entry_obj, resolution_ctx.clone(), repo_dir_path.clone()).await { Ok(val) => { entries.insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); - }, + } Err(e) => { err = Some(e); } } - }, + } RequiredSingleManifestEntry::ImportString(raw_entry_str) => { - match resolve_entry_value(raw_entry_str, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedSingle((here, val_str)) => { - match serde_jsonc::from_str(&val_str) { - Ok(raw_val) => { - match K::compile(raw_val, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, repo_dir_path.clone()).await { - Ok(val) => { - entries.insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); - }, - Err(e) => { - err = Some(e); - } - } - }, - Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap.RequiredSingleManifestEntry, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - } - } - }, - Resolution::ImportedMultiple((here, hash_map)) => { - for (val_id, val_str) in hash_map { - match serde_jsonc::from_str(&val_str) { - Ok(raw_val) => { - match K::compile(raw_val, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, repo_dir_path.clone()).await { - Ok(val) => { - entries.insert(val_id, RequiredSingleManifestEntry::Some(val)); - }, - Err(e) => { - err = Some(e); - } - } + match resolve_entry_value( + raw_entry_str, + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { + Ok(resolution) => match resolution { + Resolution::ImportedSingle((here, val_str)) => { + match serde_jsonc::from_str(&val_str) { + Ok(raw_val) => { + match K::compile( + raw_val, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here, }, + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + entries + .insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); + } Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap.RequiredSingleManifestEntry, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, e))); + err = Some(e); } } } - }, - Resolution::NoImport(val_str) => { + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap.RequiredSingleManifestEntry, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here + }, + e + ))); + } + } + } + Resolution::ImportedMultiple((here, hash_map)) => { + for (val_id, val_str) in hash_map { match serde_jsonc::from_str(&val_str) { Ok(raw_val) => { - match K::compile(raw_val, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(val) => { - entries.insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); + match K::compile( + raw_val, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), }, + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + entries.insert(val_id, RequiredSingleManifestEntry::Some(val)); + } Err(e) => { err = Some(e); } } - }, + } Err(e) => { - err = Some(SimpleError::new(format!("RequiredStrTHashMap.RequiredSingleManifestEntry, ctx: {:#?}\nerr: {}", resolution_ctx.clone(), e))); + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap.RequiredSingleManifestEntry, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone() + }, + e + ))); } } - }, + } } + Resolution::NoImport(val_str) => match serde_jsonc::from_str(&val_str) { + Ok(raw_val) => { + match K::compile(raw_val, resolution_ctx.clone(), repo_dir_path.clone()).await + { + Ok(val) => { + entries.insert(entry_id.clone(), RequiredSingleManifestEntry::Some(val)); + } + Err(e) => { + err = Some(e); + } + } + } + Err(e) => { + err = Some(SimpleError::new(format!( + "RequiredStrTHashMap.RequiredSingleManifestEntry, ctx: {:#?}\nerr: {}", + resolution_ctx.clone(), + e + ))); + } + }, }, Err(e) => { err = Some(e); } } - }, + } } } @@ -583,62 +838,74 @@ impl Deserialize<'a>, K: ManifestCompilationFrom + for<'a> }; match err { - Some(e) => { Err(e) }, - None => { Ok(res) } + Some(e) => Err(e), + None => Ok(res), } } } impl ManifestCompilationFrom> for OptionalBoolean { - async fn compile(spec: OptionalSingleManifestSpecEntry, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, Option: for<'a> Deserialize<'a> { + async fn compile( + spec: OptionalSingleManifestSpecEntry, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + Option: for<'a> Deserialize<'a>, + { let mut err = None; let ret = match spec { OptionalSingleManifestSpecEntry::Some(val) => OptionalBoolean::Some(val), OptionalSingleManifestSpecEntry::ImportString(raw_spec) => { match resolve_entry_value(raw_spec, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedMultiple(_) => { - err = Some(SimpleError::new("Glob imports are not supported at this level")); - OptionalBoolean::None - }, - Resolution::ImportedSingle((here, val_str)) => { - match serde_jsonc::from_str(&val_str) { - Ok(val) => { - OptionalBoolean::Some(val) - }, - Err(e) => { - err = Some(SimpleError::new(format!("OptionalBoolean, ctx: {:#?}\nerr: {}", ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here }, e))); - OptionalBoolean::None - } - } - }, - Resolution::NoImport(val_str) => { - match serde_jsonc::from_str(&val_str) { - Ok(val) => { - OptionalBoolean::Some(val) + Ok(resolution) => match resolution { + Resolution::ImportedMultiple(_) => { + err = Some(SimpleError::new( + "Glob imports are not supported at this level", + )); + OptionalBoolean::None + } + Resolution::ImportedSingle((here, val_str)) => match serde_jsonc::from_str(&val_str) { + Ok(val) => OptionalBoolean::Some(val), + Err(e) => { + err = Some(SimpleError::new(format!( + "OptionalBoolean, ctx: {:#?}\nerr: {}", + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here }, - Err(e) => { - err = Some(SimpleError::new(format!("OptionalBoolean, ctx: {:#?}\nerr: {}", resolution_ctx.clone(), e))); - OptionalBoolean::None - } - } + e + ))); + OptionalBoolean::None } - } + }, + Resolution::NoImport(val_str) => match serde_jsonc::from_str(&val_str) { + Ok(val) => OptionalBoolean::Some(val), + Err(e) => { + err = Some(SimpleError::new(format!( + "OptionalBoolean, ctx: {:#?}\nerr: {}", + resolution_ctx.clone(), + e + ))); + OptionalBoolean::None + } + }, }, Err(e) => { err = Some(e); OptionalBoolean::None } } - }, - OptionalSingleManifestSpecEntry::None => OptionalBoolean::None + } + OptionalSingleManifestSpecEntry::None => OptionalBoolean::None, }; match err { Some(e) => Err(e), - None => Ok(ret) + None => Ok(ret), } } } @@ -646,15 +913,29 @@ impl ManifestCompilationFrom> for Optional //* ---------------------------- impl Manifest { - pub async fn compile(spec: ManifestSpec, spec_path: OsPath, repo_dir_path: OsPath) -> Result { + pub async fn compile( + spec: ManifestSpec, + spec_path: OsPath, + repo_dir_path: OsPath, + ) -> Result { let mut err = None; debug!("Resolving manifest.base"); - let base = match OptionalString::compile(spec.base.clone(), ResolutionCtx { base: None, builtin: builtin_rfqdn(false), here: spec_path.clone() }, repo_dir_path.clone()).await { + let base = match OptionalString::compile( + spec.base.clone(), + ResolutionCtx { + base: None, + builtin: builtin_rfqdn(false), + here: spec_path.clone(), + }, + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved manifest.base"); val - }, + } Err(e) => { err = Some(e); OptionalString::None @@ -663,19 +944,25 @@ impl Manifest { let resolution_ctx = ResolutionCtx { base: match base.clone() { - OptionalString::Some(val) => { Some(val) }, - OptionalString::None => { None } + OptionalString::Some(val) => Some(val), + OptionalString::None => None, }, builtin: builtin_rfqdn(false), - here: spec_path.clone() + here: spec_path.clone(), }; debug!("Resolving manifest.name"); - let name = match OptionalString::compile(spec.name.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let name = match OptionalString::compile( + spec.name.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved manifest.name"); val - }, + } Err(e) => { err = Some(e); OptionalString::None @@ -683,22 +970,34 @@ impl Manifest { }; debug!("Resolving manifest.version"); - let version = match RequiredString::compile(spec.version.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let version = match RequiredString::compile( + spec.version.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved manifest.version"); val - }, + } Err(e) => { err = Some(e); Default::default() } }; - let directory = match Optional::compile(spec.directory.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let directory = match Optional::compile( + spec.directory.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved manifest.directory"); val - }, + } Err(e) => { err = Some(e); Default::default() @@ -706,84 +1005,123 @@ impl Manifest { }; match err { - Some(e) => { Err(e) }, - None => { - Ok(Manifest { - name, - version, - base, - directory - }) - } + Some(e) => Err(e), + None => Ok(Manifest { + name, + version, + base, + directory, + }), } } } impl ManifestCompilationFrom for DirectorySpec { - async fn compile(spec: RawDirectorySpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, RawDirectorySpec: for<'a> Deserialize<'a> { - let mut err = None; - - debug!("Resolving manifest.modules"); - let modules = match OptionalStrTHashMap::compile(spec.modules.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(val) => { - debug!("Resolved manifest.modules"); - val - }, - Err(e) => { - err = Some(e); - OptionalStrTHashMap::None - } - }; - - debug!("Resolving manifest.applications"); - let applications = match OptionalStrTHashMap::compile(spec.applications.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(val) => { - debug!("Resolved manifest.applications"); - val - }, - Err(e) => { - err = Some(e); - OptionalStrTHashMap::None - } - }; - - debug!("Resolving manifest.expression-packs"); - #[cfg(feature = "core")] - let expression_packs = match OptionalStrTHashMap::compile(spec.expression_packs.clone(), ResolutionCtx { base: resolution_ctx.clone().base, builtin: builtin_rfqdn(true), here: resolution_ctx.clone().here }, repo_dir_path.clone()).await { - Ok(val) => { - debug!("Resolved manifest.expression-packs"); - val - }, - Err(e) => { - err = Some(e); - OptionalStrTHashMap::None - } - }; - - match err { - Some(e) => { Err(e) }, - None => { - Ok(DirectorySpec { - modules, - applications, - #[cfg(feature = "core")] - expression_packs - }) - } + async fn compile( + spec: RawDirectorySpec, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + RawDirectorySpec: for<'a> Deserialize<'a>, + { + let mut err = None; + + debug!("Resolving manifest.modules"); + let modules = match OptionalStrTHashMap::compile( + spec.modules.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + debug!("Resolved manifest.modules"); + val + } + Err(e) => { + err = Some(e); + OptionalStrTHashMap::None + } + }; + + debug!("Resolving manifest.applications"); + let applications = match OptionalStrTHashMap::compile( + spec.applications.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + debug!("Resolved manifest.applications"); + val + } + Err(e) => { + err = Some(e); + OptionalStrTHashMap::None + } + }; + + debug!("Resolving manifest.expression-packs"); + #[cfg(feature = "core")] + let expression_packs = match OptionalStrTHashMap::compile( + spec.expression_packs.clone(), + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: builtin_rfqdn(true), + here: resolution_ctx.clone().here, + }, + repo_dir_path.clone(), + ) + .await + { + Ok(val) => { + debug!("Resolved manifest.expression-packs"); + val + } + Err(e) => { + err = Some(e); + OptionalStrTHashMap::None } + }; + + match err { + Some(e) => Err(e), + None => Ok(DirectorySpec { + modules, + applications, + #[cfg(feature = "core")] + expression_packs, + }), + } } } impl ManifestCompilationFrom for ApplicationSpec { - async fn compile(spec: RawApplicationSpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { + async fn compile( + spec: RawApplicationSpec, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { let mut err = None; debug!("Resolving application.name"); - let name = match RequiredString::compile(spec.name.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let name = match RequiredString::compile( + spec.name.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved application.name"); val - }, + } Err(e) => { err = Some(e); Default::default() @@ -791,11 +1129,17 @@ impl ManifestCompilationFrom for ApplicationSpec { }; debug!("Resolving application.version"); - let version = match RequiredString::compile(spec.version.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let version = match RequiredString::compile( + spec.version.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved application.version"); val - }, + } Err(e) => { err = Some(e); Default::default() @@ -803,11 +1147,17 @@ impl ManifestCompilationFrom for ApplicationSpec { }; debug!("Resolving application.intents"); - let intents = match OptionalStrStrHashMap::compile(spec.intents.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let intents = match OptionalStrStrHashMap::compile( + spec.intents.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved application.intents"); val - }, + } Err(e) => { err = Some(e); OptionalStrStrHashMap::None @@ -815,11 +1165,17 @@ impl ManifestCompilationFrom for ApplicationSpec { }; debug!("Resolving application.containers"); - let containers = match OptionalStrTHashMap::compile(spec.containers.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let containers = match OptionalStrTHashMap::compile( + spec.containers.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved application.containers"); val - }, + } Err(e) => { err = Some(e); OptionalStrTHashMap::None @@ -827,29 +1183,41 @@ impl ManifestCompilationFrom for ApplicationSpec { }; match err { - Some(e) => { Err(e) }, - None => { - Ok(Self { - name, - version, - intents, - containers - }) - } + Some(e) => Err(e), + None => Ok(Self { + name, + version, + intents, + containers, + }), } } } impl ManifestCompilationFrom for ContainerSpec { - async fn compile(spec: RawContainerSpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, RawContainerSpec: for<'a> Deserialize<'a> { + async fn compile( + spec: RawContainerSpec, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + RawContainerSpec: for<'a> Deserialize<'a>, + { let mut err = None; debug!("Resolving container.interface"); - let interface = match OptionalBoolean::compile(spec.interface.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let interface = match OptionalBoolean::compile( + spec.interface.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved container.interface"); val - }, + } Err(e) => { err = Some(e); OptionalBoolean::None @@ -857,11 +1225,17 @@ impl ManifestCompilationFrom for ContainerSpec { }; debug!("Resolving container.build-config"); - let build = match Optional::compile(spec.build.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let build = match Optional::compile( + spec.build.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved container.build-config"); val - }, + } Err(e) => { err = Some(e); Optional::None @@ -869,27 +1243,36 @@ impl ManifestCompilationFrom for ContainerSpec { }; match err { - Some(e) => { Err(e) }, - None => { - Ok(ContainerSpec { - interface, - build - }) - } + Some(e) => Err(e), + None => Ok(ContainerSpec { interface, build }), } } } impl ManifestCompilationFrom for BuildConfig { - async fn compile(spec: RawBuildConfig, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, RawBuildConfig: for<'a> Deserialize<'a> { + async fn compile( + spec: RawBuildConfig, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + RawBuildConfig: for<'a> Deserialize<'a>, + { let mut err = None; debug!("Resolving build-config.url"); - let url_field = match RequiredString::compile(spec.url.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let url_field = match RequiredString::compile( + spec.url.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved build-config.url"); val - }, + } Err(e) => { err = Some(e); Default::default() @@ -897,11 +1280,17 @@ impl ManifestCompilationFrom for BuildConfig { }; debug!("Resolving build-config.creds"); - let creds = match Optional::compile(spec.creds.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let creds = match Optional::compile( + spec.creds.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved build-config.creds"); val - }, + } Err(e) => { err = Some(e); Optional::None @@ -912,22 +1301,36 @@ impl ManifestCompilationFrom for BuildConfig { Some(e) => Err(e), None => Ok(BuildConfig { url: url_field, - creds - }) + creds, + }), } } } impl ManifestCompilationFrom for RepoCreds { - async fn compile(spec: RawRepoCreds, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, RawRepoCreds: for<'a> Deserialize<'a> { + async fn compile( + spec: RawRepoCreds, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + RawRepoCreds: for<'a> Deserialize<'a>, + { let mut err = None; debug!("Resolving repo-creds.username"); - let username = match OptionalString::compile(spec.username.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let username = match OptionalString::compile( + spec.username.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved repo-creds.username"); val - }, + } Err(e) => { err = Some(e); OptionalString::None @@ -935,11 +1338,17 @@ impl ManifestCompilationFrom for RepoCreds { }; debug!("Resolving repo-creds.key"); - let key = match RequiredString::compile(spec.key.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let key = match RequiredString::compile( + spec.key.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved repo-creds.key"); val - }, + } Err(e) => { err = Some(e); Default::default() @@ -948,25 +1357,35 @@ impl ManifestCompilationFrom for RepoCreds { match err { Some(e) => Err(e), - None => Ok(RepoCreds { - username, - key - }) + None => Ok(RepoCreds { username, key }), } } } // TODO: move to clover::server::modman::models::Module impl ManifestCompilationFrom for ModuleSpec { - async fn compile(spec: RawModuleSpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { + async fn compile( + spec: RawModuleSpec, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { let mut err = None; debug!("Resolving module.name"); - let name = match OptionalString::compile(spec.name.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let name = match OptionalString::compile( + spec.name.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved module.name"); val - }, + } Err(e) => { err = Some(e); OptionalString::None @@ -974,26 +1393,35 @@ impl ManifestCompilationFrom for ModuleSpec { }; match err { - Some(e) => { Err(e) }, - None => { - Ok(Self { - name - }) - } + Some(e) => Err(e), + None => Ok(Self { name }), } } } impl ManifestCompilationFrom for ExpressionPackSpec { - async fn compile(spec: RawExpressionPackSpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized { + async fn compile( + spec: RawExpressionPackSpec, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + { let mut err = None; debug!("Resolving expression-pack.name"); - let name = match OptionalString::compile(spec.name.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let name = match OptionalString::compile( + spec.name.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved expression-pack.name"); val - }, + } Err(e) => { err = Some(e); OptionalString::None @@ -1001,11 +1429,17 @@ impl ManifestCompilationFrom for ExpressionPackSpec { }; debug!("Resolving expression-pack.expressions"); - let expressions = match OptionalStrTHashMap::compile(spec.expressions.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let expressions = match OptionalStrTHashMap::compile( + spec.expressions.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved expression-pack.expressions"); val - }, + } Err(e) => { err = Some(e); OptionalStrTHashMap::None @@ -1013,54 +1447,79 @@ impl ManifestCompilationFrom for ExpressionPackSpec { }; match err { - Some(e) => { Err(e) }, - None => { - Ok(ExpressionPackSpec { - name, - expressions - }) - } + Some(e) => Err(e), + None => Ok(ExpressionPackSpec { name, expressions }), } } } impl ManifestCompilationFrom for ExpressionSpec { - async fn compile(spec: RawExpressionSpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, RawExpressionSpec: for<'a> Deserialize<'a> { + async fn compile( + spec: RawExpressionSpec, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + RawExpressionSpec: for<'a> Deserialize<'a>, + { let mut err = None; debug!("Resolving raw-expression-pack"); let ret = match spec { RawExpressionSpec::RawStaticExpressionSpec(raw_expression_spec) => { - match StaticExpressionSpec::compile(raw_expression_spec, resolution_ctx.clone(), repo_dir_path.clone()).await { + match StaticExpressionSpec::compile( + raw_expression_spec, + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved raw-expression-pack"); Self::StaticExpressionSpec(val) - }, + } Err(e) => { err = Some(e); - Self::StaticExpressionSpec(StaticExpressionSpec { static_url: RequiredString(String::from("bleh")) }) + Self::StaticExpressionSpec(StaticExpressionSpec { + static_url: RequiredString(String::from("bleh")), + }) } } - }, + } }; match err { Some(e) => Err(e), - None => Ok(ret) + None => Ok(ret), } } } impl ManifestCompilationFrom for StaticExpressionSpec { - async fn compile(spec: RawStaticExpressionSpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, RawStaticExpressionSpec: for<'a> Deserialize<'a> { + async fn compile( + spec: RawStaticExpressionSpec, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + RawStaticExpressionSpec: for<'a> Deserialize<'a>, + { let mut err = None; debug!("Resolving static-expression-pack.static-url"); - let static_url = match RequiredString::compile(spec.static_url.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await { + let static_url = match RequiredString::compile( + spec.static_url.clone(), + resolution_ctx.clone(), + repo_dir_path.clone(), + ) + .await + { Ok(val) => { debug!("Resolved static-expression-pack.static-url"); val - }, + } Err(e) => { err = Some(e); Default::default() @@ -1069,9 +1528,7 @@ impl ManifestCompilationFrom for StaticExpressionSpec { match err { Some(e) => Err(e), - None => Ok(Self { - static_url - }) + None => Ok(Self { static_url }), } } } diff --git a/clover-hub/src/server/warehouse/repos/mod.rs b/clover-hub/src/server/warehouse/repos/mod.rs index 963cf79..2a9c9fc 100644 --- a/clover-hub/src/server/warehouse/repos/mod.rs +++ b/clover-hub/src/server/warehouse/repos/mod.rs @@ -1,17 +1,46 @@ -pub mod models; pub mod impls; +pub mod models; -use tokio_stream::{wrappers::ReadDirStream, StreamExt}; -use std::{collections::HashMap, sync::Arc}; -use git2::{build::CheckoutBuilder, BranchType, Repository}; -use log::{debug, error, info, warn}; -use models::{Manifest, ManifestCompilationFrom, ManifestSpec, OptionalString, RequiredSingleManifestEntry, Resolution, ResolutionCtx}; +use crate::{ + server::evtbuzz::models::Store, + utils::read_file, +}; +use git2::{ + BranchType, + Repository, + build::CheckoutBuilder, +}; +use log::{ + debug, + error, + info, + warn, +}; +use models::{ + Manifest, + ManifestCompilationFrom, + ManifestSpec, + OptionalString, + RequiredSingleManifestEntry, + Resolution, + ResolutionCtx, +}; use os_path::OsPath; use regex::Regex; use serde::Deserialize; use simple_error::SimpleError; -use tokio::{fs, io::AsyncReadExt}; -use crate::{server::evtbuzz::models::Store, utils::read_file}; +use std::{ + collections::HashMap, + sync::Arc, +}; +use tokio::{ + fs, + io::AsyncReadExt, +}; +use tokio_stream::{ + StreamExt, + wrappers::ReadDirStream, +}; #[derive(PartialEq)] pub struct Error(pub SimpleError); @@ -31,7 +60,11 @@ pub fn builtin_rfqdn(is_core: bool) -> String { } pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) -> String { - debug!("replace_simple_directives (provided): {} + {:#?}", value.clone(), resolution_ctx.clone()); + debug!( + "replace_simple_directives (provided): {} + {:#?}", + value.clone(), + resolution_ctx.clone() + ); let base_re = Regex::new("(?\\@base)").unwrap(); let here_re = Regex::new("(?\\@here)").unwrap(); @@ -41,7 +74,7 @@ pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) - match resolution_ctx.base { Some(base) => { val = String::from(base_re.replace_all(&value.clone(), base)); - }, + } None => {} } @@ -52,12 +85,15 @@ pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) - let mut val_unsafe_path = resolution_ctx.here.join(&here_import_temp.to_string()); val_unsafe_path.resolve(); - if val_unsafe_path.to_string().starts_with(&resolution_ctx.here.to_string()) { + if val_unsafe_path + .to_string() + .starts_with(&resolution_ctx.here.to_string()) + { val_unsafe_path.to_string() } else { warn!( - "Directory resolution for \"{}\" (resolved to: \"{}\") is not confined to repo directory, removing entry value entirely since this is a security issue.", - binding1.clone(), + "Directory resolution for \"{}\" (resolved to: \"{}\") is not confined to repo directory, removing entry value entirely since this is a security issue.", + binding1.clone(), val_unsafe_path.to_string() ); "".to_string() @@ -70,19 +106,26 @@ pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) - let builtin_import_temp = builtin_re.replace_all(&binding2, ""); let val = if builtin_import_temp != val { match builtin_re.captures(&binding2).unwrap().name("domain") { - Some(domain) => { - if domain.as_str() == "core" { - builtin_re.replace_all(&binding2, builtin_rfqdn(true)).to_string() - } else if domain.as_str() == "clover" { - builtin_re.replace_all(&binding2, builtin_rfqdn(false)).to_string() - } else { - error!("No case for domain: \"{}\", yet it's in the directive regex, this is a bug, please report it; refusing to resolve built-in directive (value unchanged)!", domain.as_str()); - binding2.clone() - } - }, - None => { - builtin_re.replace_all(&binding2, resolution_ctx.builtin.to_string()).to_string() - }, + Some(domain) => { + if domain.as_str() == "core" { + builtin_re + .replace_all(&binding2, builtin_rfqdn(true)) + .to_string() + } else if domain.as_str() == "clover" { + builtin_re + .replace_all(&binding2, builtin_rfqdn(false)) + .to_string() + } else { + error!( + "No case for domain: \"{}\", yet it's in the directive regex, this is a bug, please report it; refusing to resolve built-in directive (value unchanged)!", + domain.as_str() + ); + binding2.clone() + } + } + None => builtin_re + .replace_all(&binding2, resolution_ctx.builtin.to_string()) + .to_string(), } } else { val @@ -93,8 +136,14 @@ pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) - String::from(val) } -pub async fn resolve_list_entry(raw_list: HashMap>, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result, SimpleError> - where K: ManifestCompilationFrom, T: for<'a> Deserialize<'a> +pub async fn resolve_list_entry( + raw_list: HashMap>, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, +) -> Result, SimpleError> +where + K: ManifestCompilationFrom, + T: for<'a> Deserialize<'a>, { let mut err = None; let mut entries = HashMap::new(); @@ -107,82 +156,117 @@ pub async fn resolve_list_entry(raw_list: HashMap { match resolve_entry_value(str, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(resolution) => { - match resolution { - Resolution::ImportedSingle((here, raw_obj)) => { - if is_glob { - err = Some(SimpleError::new("Resolved only one file for glob key import, import the root key instead!")); - } else { + Ok(resolution) => match resolution { + Resolution::ImportedSingle((here, raw_obj)) => { + if is_glob { + err = Some(SimpleError::new( + "Resolved only one file for glob key import, import the root key instead!", + )); + } else { + match serde_jsonc::from_str::(&raw_obj) { + Ok(obj_spec) => { + match K::compile( + obj_spec, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), + }, + repo_dir_path.clone(), + ) + .await + { + Ok(obj) => { + entries.insert( + replace_simple_directives( + key.clone(), + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), + }, + ), + obj, + ); + } + Err(e) => { + entry_err = Some(e); + } + } + } + Err(e) => { + entry_err = Some(SimpleError::from(e)); + } + } + } + } + Resolution::ImportedMultiple((here, raw_objs)) => { + if is_glob { + for (obj_key_seg, raw_obj) in raw_objs { match serde_jsonc::from_str::(&raw_obj) { Ok(obj_spec) => { - match K::compile(obj_spec, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, repo_dir_path.clone()).await { - Ok(obj) => { - entries.insert(replace_simple_directives(key.clone(), ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }), obj); + match K::compile( + obj_spec, + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), }, + repo_dir_path.clone(), + ) + .await + { + Ok(obj) => { + let obj_key_prod = replace_simple_directives( + ["@base".to_string(), obj_key_seg].join("."), + ResolutionCtx { + base: resolution_ctx.clone().base, + builtin: resolution_ctx.clone().builtin, + here: here.clone(), + }, + ); + debug!("Resolution::ImportedMultiple: {}", obj_key_prod); + + entries.insert(obj_key_prod, obj); + } Err(e) => { entry_err = Some(e); } - } - }, + } + } Err(e) => { entry_err = Some(SimpleError::from(e)); } } } - }, - Resolution::ImportedMultiple((here, raw_objs)) => { - if is_glob { - for (obj_key_seg, raw_obj) in raw_objs { - match serde_jsonc::from_str::(&raw_obj) { - Ok(obj_spec) => { - match K::compile(obj_spec, ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }, repo_dir_path.clone()).await { - Ok(obj) => { - let obj_key_prod = replace_simple_directives(["@base".to_string(), obj_key_seg].join("."), ResolutionCtx { base: resolution_ctx.clone().base, builtin: resolution_ctx.clone().builtin, here: here.clone() }); - debug!("Resolution::ImportedMultiple: {}", obj_key_prod); - - entries.insert(obj_key_prod, obj); - }, - Err(e) => { - entry_err = Some(e); - } - } - }, - Err(e) => { - entry_err = Some(SimpleError::from(e)); - } - } + } + } + Resolution::NoImport(raw_obj) => match serde_jsonc::from_str::(&raw_obj) { + Ok(obj_spec) => { + match K::compile(obj_spec, resolution_ctx.clone(), repo_dir_path.clone()).await { + Ok(obj) => { + entries.insert(key.clone(), obj); } - } - }, - Resolution::NoImport(raw_obj) => { - match serde_jsonc::from_str::(&raw_obj) { - Ok(obj_spec) => { - match K::compile(obj_spec, resolution_ctx.clone(), repo_dir_path.clone()).await { - Ok(obj) => { - entries.insert(key.clone(), obj); - }, - Err(e) => { - entry_err = Some(e); - } - } - }, Err(e) => { - entry_err = Some(SimpleError::from(e)); + entry_err = Some(e); } } } - } + Err(e) => { + entry_err = Some(SimpleError::from(e)); + } + }, }, Err(e) => { err = Some(e); } } - }, + } RequiredSingleManifestEntry::Some(obj_spec) => { match K::compile(obj_spec, resolution_ctx.clone(), repo_dir_path.clone()).await { Ok(obj) => { entries.insert(key.clone(), obj); - }, + } Err(e) => { entry_err = Some(e); } @@ -192,33 +276,46 @@ pub async fn resolve_list_entry(raw_list: HashMap { - error!("Error while parsing entry \"{}\", in {}:\n{}", key.clone(), resolution_ctx.here.to_string(), e); - }, + error!( + "Error while parsing entry \"{}\", in {}:\n{}", + key.clone(), + resolution_ctx.here.to_string(), + e + ); + } None => {} } } match err { - Some(e) => { Err(e) }, - None => { Ok(entries) } + Some(e) => Err(e), + None => Ok(entries), } } -pub async fn update_repo_dir_structure(repo_dir_path: OsPath, store: Arc) -> Result<(), Error> { +pub async fn update_repo_dir_structure( + repo_dir_path: OsPath, + store: Arc, +) -> Result<(), Error> { let mut err = None; let repos = store.config.lock().await.repos.clone(); for (repo_id, _repo_spec) in repos { - let repo_id_segments = OsPath::from(repo_dir_path.clone().to_string()).join(repo_id.split(".").collect::>().join("/")).join("@repo"); + let repo_id_segments = OsPath::from(repo_dir_path.clone().to_string()) + .join(repo_id.split(".").collect::>().join("/")) + .join("@repo"); if !repo_id_segments.exists() { match fs::create_dir_all(repo_id_segments.to_string()).await { Ok(_) => { debug!("Created directory: {}!", repo_id_segments.to_string()); - }, + } Err(e) => { err = Some(Error(SimpleError::from(e))); - error!("Failed to create repo directory: {}!", repo_id_segments.to_string()); + error!( + "Failed to create repo directory: {}!", + repo_id_segments.to_string() + ); break; } } @@ -226,13 +323,17 @@ pub async fn update_repo_dir_structure(repo_dir_path: OsPath, store: Arc) } match err { - Some(e) => { Err(e) }, - None => { Ok(()) } + Some(e) => Err(e), + None => Ok(()), } } /// Used to resolve repo manifest entry **values** that may have directives (`@import`, `@base`, `@here`) in them. -pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result { +pub async fn resolve_entry_value( + value: String, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, +) -> Result { let import_re = Regex::new("^\\@import\\(('|\"|`)(?.+)('|\"|`)\\)$").unwrap(); let mut ret: Resolution = Resolution::NoImport(value.clone()); let mut err = None; @@ -241,7 +342,19 @@ pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, r if import_re.is_match(&value.clone()) { let raw_import_path = OsPath::new().join( - resolution_ctx.here.parent().unwrap_or(OsPath::new().join("/")).join(import_re.captures(&value.clone()).unwrap().name("src").unwrap().as_str()).to_string() + resolution_ctx + .here + .parent() + .unwrap_or(OsPath::new().join("/")) + .join( + import_re + .captures(&value.clone()) + .unwrap() + .name("src") + .unwrap() + .as_str(), + ) + .to_string(), ); let mut import_path = OsPath::new(); let mut segments = 0; @@ -260,8 +373,15 @@ pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, r import_path.resolve(); - if !import_path.to_string().starts_with(&repo_dir_path.to_string()) { - return Err(SimpleError::new(format!("Path: \"{}\", is not confined within the repository root (\"{}\"). Refusing to evaluate.", import_path.to_string(), repo_dir_path.to_string()))); + if !import_path + .to_string() + .starts_with(&repo_dir_path.to_string()) + { + return Err(SimpleError::new(format!( + "Path: \"{}\", is not confined within the repository root (\"{}\"). Refusing to evaluate.", + import_path.to_string(), + repo_dir_path.to_string() + ))); } debug!("Attempting to import \"{}\"...", import_path.to_string()); @@ -273,7 +393,7 @@ pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, r let import_path_str = import_path.clone().to_string(); let import_captures = glob_import_re.captures(&import_path_str).unwrap(); let here = OsPath::new().join(import_captures.name("base").unwrap().as_str()); - + match fs::read_dir(&here.clone().to_path()).await { Ok(dir) => { let mut entries = HashMap::new(); @@ -286,40 +406,51 @@ pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, r let mut file_path = OsPath::from(entry.path()); let cap = match import_captures.name("cap") { Some(val) => val.as_str(), - None => "/manifest.clover.jsonc" + None => "/manifest.clover.jsonc", }; - debug!("Attempting to import from glob: \"{}\"...", entry.path().to_str().unwrap()); + debug!( + "Attempting to import from glob: \"{}\"...", + entry.path().to_str().unwrap() + ); if entry.path().is_dir() { file_path.push(cap); } let base = match import_captures.name("base") { - Some(val) => val.as_str(), - None => "", + Some(val) => val.as_str(), + None => "", }; - + if file_path.to_string().ends_with(&cap) { match read_file(file_path.clone()).await { Ok(contents) => { - debug!("resolve_entry_value {}:\n{}", file_path.clone().to_string(), contents.clone()); - entries.insert(replace_simple_directives(file_path.to_string().replace(base, "").replace(cap, ""), resolution_ctx.clone()), contents); - }, + debug!( + "resolve_entry_value {}:\n{}", + file_path.clone().to_string(), + contents.clone() + ); + entries.insert( + replace_simple_directives( + file_path.to_string().replace(base, "").replace(cap, ""), + resolution_ctx.clone(), + ), + contents, + ); + } Err(e) => { failed_entries.push(e); } } } - }, - Err(e) => { - failed_entries.push(SimpleError::from(e)) } + Err(e) => failed_entries.push(SimpleError::from(e)), } } ret = Resolution::ImportedMultiple((here, entries)); - }, + } Err(e) => { err = Some(SimpleError::from(e)); } @@ -329,21 +460,27 @@ pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, r Ok(contents) => { debug!("{}", contents.clone()); ret = Resolution::ImportedSingle((import_path.clone(), contents)); - }, + } Err(e) => { err = Some(e); } } } else { - err = Some(SimpleError::new(format!("Invalid import path: \"{}\"!", import_path.clone().to_string()))); + err = Some(SimpleError::new(format!( + "Invalid import path: \"{}\"!", + import_path.clone().to_string() + ))); } } else { - ret = Resolution::NoImport(replace_simple_directives(value.clone(), resolution_ctx.clone())); + ret = Resolution::NoImport(replace_simple_directives( + value.clone(), + resolution_ctx.clone(), + )); } match err { - Some(e) => { Err(e) }, - None => { Ok(ret) } + Some(e) => Err(e), + None => Ok(ret), } } @@ -357,13 +494,16 @@ pub async fn download_repo_updates(store: Arc, repo_dir_path: OsPath) -> for (repo_id, repo_spec) in repos.clone() { let mut repo_err = None; - let repo_path = OsPath::new().join(repo_dir_path.clone().to_string()).join(repo_id.split(".").collect::>().join("/")).join("/@repo/"); - + let repo_path = OsPath::new() + .join(repo_dir_path.clone().to_string()) + .join(repo_id.split(".").collect::>().join("/")) + .join("/@repo/"); + let repo_str; match repo_spec.name { Some(name) => { repo_str = format!("{} ({})", name, repo_id.clone()); - }, + } None => { repo_str = repo_id.clone(); } @@ -380,23 +520,27 @@ pub async fn download_repo_updates(store: Arc, repo_dir_path: OsPath) -> let mut main_remote = None; for remote_name in remotes.into_iter() { match remote_name { - None => {}, + None => {} Some(remote_name_str) => { match repo.find_remote(remote_name_str) { Ok(remote) => { - // Fetch the url + // Fetch the url match remote.url() { Some(remote_url) => { if remote_url == repo_spec.src { main_remote = Some(remote); break; } - }, + } None => {} } - }, + } Err(e) => { - error!("Repo: {}, failed to get specified remote, due to:\n{}", repo_str.clone(), e); + error!( + "Repo: {}, failed to get specified remote, due to:\n{}", + repo_str.clone(), + e + ); repo_err = Some(Error(SimpleError::from(e))); } } @@ -410,35 +554,44 @@ pub async fn download_repo_updates(store: Arc, repo_dir_path: OsPath) -> match remote.fetch(&[remote_branch_name.clone()], None, None) { Ok(_) => { - let remote_branch = repo.find_branch(&format!("{}/{}", remote.name().unwrap(), remote_branch_name.clone()), BranchType::Remote)?; - if remote_branch.is_head() && (remote_branch.get().resolve()?.target().unwrap() == repo.head().unwrap().resolve()?.target().unwrap()) { - + let remote_branch = repo.find_branch( + &format!("{}/{}", remote.name().unwrap(), remote_branch_name.clone()), + BranchType::Remote, + )?; + if remote_branch.is_head() + && (remote_branch.get().resolve()?.target().unwrap() + == repo.head().unwrap().resolve()?.target().unwrap()) + { } else { match repo.checkout_tree( - remote_branch.get().peel_to_tree().unwrap().as_object(), - Some(CheckoutBuilder::new().conflict_style_merge(true).force()), + remote_branch.get().peel_to_tree().unwrap().as_object(), + Some(CheckoutBuilder::new().conflict_style_merge(true).force()), ) { - Ok(_) => { - match repo.cleanup_state() { - Ok(_) => { - repos_updated += 1; - let comm = repo.head()?.peel_to_commit()?; - let comm_str; - match comm.message() { - Some(message) => { - comm_str = format!("{}, ({})", message, comm.id()); - }, - None => { - comm_str = comm.id().to_string(); - } + Ok(_) => match repo.cleanup_state() { + Ok(_) => { + repos_updated += 1; + let comm = repo.head()?.peel_to_commit()?; + let comm_str; + match comm.message() { + Some(message) => { + comm_str = format!("{}, ({})", message, comm.id()); + } + None => { + comm_str = comm.id().to_string(); } - - info!("Repo: {}, Updated, now using commit: {}!", repo_str, comm_str); - }, - Err(e) => { - error!("Repo: {}, failed to merge commits due to:\n{}", repo_str, e); - repo_err = Some(Error(SimpleError::from(e))); } + + info!( + "Repo: {}, Updated, now using commit: {}!", + repo_str, comm_str + ); + } + Err(e) => { + error!( + "Repo: {}, failed to merge commits due to:\n{}", + repo_str, e + ); + repo_err = Some(Error(SimpleError::from(e))); } }, Err(e) => { @@ -447,40 +600,59 @@ pub async fn download_repo_updates(store: Arc, repo_dir_path: OsPath) -> match repo.cleanup_state() { Ok(_) => { debug!("Repo: {}, Cleaned up.", repo_str.clone()); - }, + } Err(e) => { - error!("Repo: {}, failed to clean up, due to:\n{}", repo_str.clone(), e); + error!( + "Repo: {}, failed to clean up, due to:\n{}", + repo_str.clone(), + e + ); repo_err = Some(Error(SimpleError::from(e))); } } } } } - }, + } Err(e) => { if e.class() == git2::ErrorClass::Net { - info!("Repo: {}, was unable to connect to remote server, skipping updates...", repo_str.clone()); + info!( + "Repo: {}, was unable to connect to remote server, skipping updates...", + repo_str.clone() + ); debug!("Repo: {}, network error, due to:\n{}", repo_str.clone(), e); } else { - error!("Repo: {}, failed to fetch updates due to:\n{}", repo_str.clone(), e); + error!( + "Repo: {}, failed to fetch updates due to:\n{}", + repo_str.clone(), + e + ); repo_err = Some(Error(SimpleError::from(e))); } } } - }, + } None => { warn!("Repo: {}, No remote source!", repo_str.clone()); } } - }, + } Err(e) => { - error!("Repo: {}, Failed to get remotes, due to:\n{}", repo_str.clone(), e); + error!( + "Repo: {}, Failed to get remotes, due to:\n{}", + repo_str.clone(), + e + ); repo_err = Some(Error(SimpleError::from(e))); } } - }, + } Err(e) => { - error!("Repo: {}, failed to open git repository, due to:\n{}", repo_str.clone(),e); + error!( + "Repo: {}, failed to open git repository, due to:\n{}", + repo_str.clone(), + e + ); repo_err = Some(Error(SimpleError::from(e))); } } @@ -493,14 +665,17 @@ pub async fn download_repo_updates(store: Arc, repo_dir_path: OsPath) -> match comm.message() { Some(message) => { comm_str = format!("{}, ({})", message, comm.id()); - }, + } None => { comm_str = comm.id().to_string(); } } - info!("Repo: {}, Downloaded, now using commit: {}!", repo_str, comm_str); - }, + info!( + "Repo: {}, Downloaded, now using commit: {}!", + repo_str, comm_str + ); + } Err(e) => { error!("Repo: {}, failed to clone, due to:\n{}", repo_str, e); repo_err = Some(Error(SimpleError::from(e))); @@ -525,42 +700,64 @@ pub async fn download_repo_updates(store: Arc, repo_dir_path: OsPath) -> Ok(raw_manifest_values) => { debug!("{:#?}", raw_manifest_values.clone()); - match Manifest::compile(raw_manifest_values, manifest_path.clone(), manifest_path.parent().unwrap().clone()).await { + match Manifest::compile( + raw_manifest_values, + manifest_path.clone(), + manifest_path.parent().unwrap().clone(), + ) + .await + { Ok(manifest) => { let manifest_str; match manifest.name.clone() { OptionalString::Some(name) => { manifest_str = format!("{} ({})", name, repo_id.clone()); - }, + } OptionalString::None => { manifest_str = repo_id.clone(); } } - store.repos.lock().await.insert(repo_id.clone(), manifest.clone()); + store + .repos + .lock() + .await + .insert(repo_id.clone(), manifest.clone()); info!("Loaded manifest: {}!", manifest_str); debug!("Imported manifest: {:#?}", manifest.clone()); - }, + } Err(e) => { - error!("Repo: {}, failed to compile manifest, due to:\n{}", repo_str, e); + error!( + "Repo: {}, failed to compile manifest, due to:\n{}", + repo_str, e + ); repo_err = Some(Error(e)); } } - }, + } Err(e) => { - error!("Repo: {}, failed to parse manifest file, due to:\n{}", repo_str, e); + error!( + "Repo: {}, failed to parse manifest file, due to:\n{}", + repo_str, e + ); repo_err = Some(Error(SimpleError::from(e))); } } - }, + } Err(e) => { - error!("Repo: {}, failed to read manifest file, due to:\n{}", repo_str, e); + error!( + "Repo: {}, failed to read manifest file, due to:\n{}", + repo_str, e + ); repo_err = Some(Error(SimpleError::from(e))); } } - }, + } Err(e) => { - error!("Repo: {}, failed to open manifest file, due to:\n{}", repo_str, e); + error!( + "Repo: {}, failed to open manifest file, due to:\n{}", + repo_str, e + ); repo_err = Some(Error(SimpleError::from(e))); } } @@ -573,23 +770,28 @@ pub async fn download_repo_updates(store: Arc, repo_dir_path: OsPath) -> } if (repo_errors.len() == repos.len()) && (repos.len() > 0) { - err = Some(Error(SimpleError::new("Failed to download/update all repos... this may point to a larger problem!"))); + err = Some(Error(SimpleError::new( + "Failed to download/update all repos... this may point to a larger problem!", + ))); } match err { - Some(e) => { Err(e) }, + Some(e) => Err(e), None => { if repo_errors.len() > 0 { - warn!("{} repos failed to check for updates and/or to upgrade!", repo_errors.len()); + warn!( + "{} repos failed to check for updates and/or to upgrade!", + repo_errors.len() + ); } - if repos_updated > 0 { + if repos_updated > 0 { info!("Updated {} repo(s)!", repos_updated); } info!("Finished repo update and upgrade check!"); - Ok(()) + Ok(()) } } } diff --git a/clover-hub/src/server/warehouse/repos/models.rs b/clover-hub/src/server/warehouse/repos/models.rs index 2e53806..57caad3 100644 --- a/clover-hub/src/server/warehouse/repos/models.rs +++ b/clover-hub/src/server/warehouse/repos/models.rs @@ -1,8 +1,11 @@ -use std::collections::HashMap; -use serde::{Deserialize, Serialize}; -use simple_error::SimpleError; -use os_path::OsPath; use crate::server::appd::models::BuildConfig; +use os_path::OsPath; +use serde::{ + Deserialize, + Serialize, +}; +use simple_error::SimpleError; +use std::collections::HashMap; // TODO: Define defaults via `Default` trait impl for enums that returns its none variant. pub enum Resolution { @@ -13,7 +16,7 @@ pub enum Resolution { ImportedMultiple((OsPath, HashMap)), /// Every other case in which there was no `@import`. /// If there were other directives, they've been replaced with the correct value if provided in the ResolutionCtx. - NoImport(String) + NoImport(String), } #[derive(Debug, Clone)] pub struct ResolutionCtx { @@ -22,14 +25,14 @@ pub struct ResolutionCtx { /// Used for the `@here` directive, should contain the FS path to the manifest file being currently parsed, **NOT** to the repo. pub here: OsPath, /// Used for the `@builtin` directive, should contain the built-in context for the current key (either CloverHub or CORE). - pub builtin: String + pub builtin: String, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum RequiredSingleManifestEntry { Some(T), - ImportString(String) + ImportString(String), } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -38,7 +41,7 @@ pub enum OptionalSingleManifestSpecEntry { Some(T), ImportString(String), #[default] - None + None, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -46,7 +49,7 @@ pub enum OptionalSingleManifestSpecEntry { pub enum OptionalStrTHashMap { Some(HashMap), #[default] - None + None, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -57,14 +60,14 @@ pub struct RequiredStrTHashMap(pub HashMap { Some(HashMap>), ImportString(String), #[default] - None + None, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum RequiredListManifestSpecEntry { Some(HashMap>), - ImportString(String) + ImportString(String), } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -96,8 +99,8 @@ pub enum ManifestEntry { #[serde(untagged)] pub enum OptionalStrStrHashMap { Some(HashMap), - #[default] - None + #[default] + None, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -106,7 +109,7 @@ pub enum OptionalStringListManifestSpecEntry { Some(HashMap), ImportString(String), #[default] - None + None, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -114,7 +117,7 @@ pub enum OptionalStringListManifestSpecEntry { pub enum OptionalString { Some(String), #[default] - None + None, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -123,7 +126,7 @@ pub enum Optional { Some(T), ImportString(String), #[default] - None + None, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] pub struct RequiredString(pub String); @@ -134,7 +137,7 @@ pub struct ManifestSpec { pub version: String, pub base: Option, #[serde(default)] - pub directory: OptionalSingleManifestSpecEntry + pub directory: OptionalSingleManifestSpecEntry, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -145,7 +148,7 @@ pub struct RawDirectorySpec { pub applications: OptionalListManifestSpecEntry, #[cfg(feature = "core")] #[serde(rename = "expression-packs", default)] - pub expression_packs: OptionalListManifestSpecEntry + pub expression_packs: OptionalListManifestSpecEntry, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -177,7 +180,7 @@ pub struct RawBuildConfig { pub url: String, /// Optional repository creds #[serde(default)] - pub creds: OptionalSingleManifestSpecEntry + pub creds: OptionalSingleManifestSpecEntry, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -193,7 +196,7 @@ pub struct RawRepoCreds { pub struct RawExpressionPackSpec { pub name: Option, #[serde(default)] - pub expressions: OptionalListManifestSpecEntry + pub expressions: OptionalListManifestSpecEntry, } #[cfg(feature = "core")] @@ -207,7 +210,7 @@ pub enum RawExpressionSpec { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RawStaticExpressionSpec { #[serde(rename = "static-url")] - pub static_url: String + pub static_url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -229,7 +232,7 @@ pub struct DirectorySpec { pub applications: OptionalStrTHashMap, #[cfg(feature = "core")] #[serde(default)] - pub expression_packs: OptionalStrTHashMap + pub expression_packs: OptionalStrTHashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -262,7 +265,7 @@ pub struct ExpressionPackSpec { #[serde(default)] pub name: OptionalString, #[serde(default)] - pub expressions: OptionalStrTHashMap + pub expressions: OptionalStrTHashMap, } #[cfg(feature = "core")] @@ -275,9 +278,16 @@ pub enum ExpressionSpec { #[cfg(feature = "core")] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StaticExpressionSpec { - pub static_url: RequiredString + pub static_url: RequiredString, } pub trait ManifestCompilationFrom { - async fn compile(spec: T, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result where Self: Sized, T: for<'a> Deserialize<'a>; + async fn compile( + spec: T, + resolution_ctx: ResolutionCtx, + repo_dir_path: OsPath, + ) -> Result + where + Self: Sized, + T: for<'a> Deserialize<'a>; } diff --git a/clover-hub/src/tui/mod.rs b/clover-hub/src/tui/mod.rs index 9f5e459..0d84358 100644 --- a/clover-hub/src/tui/mod.rs +++ b/clover-hub/src/tui/mod.rs @@ -1,18 +1,53 @@ -use std::{io, thread, time::Duration}; +use crossterm::{ + event::{ + DisableMouseCapture, + EnableMouseCapture, + }, + execute, + terminal::{ + EnterAlternateScreen, + LeaveAlternateScreen, + disable_raw_mode, + enable_raw_mode, + }, +}; use log::info; +use std::{ + io, + thread, + time::Duration, +}; use tokio_util::sync::CancellationToken; use tui::{ - backend::CrosstermBackend, layout::{Constraint, Direction, Layout}, style::{Color, Style}, text::{Span, Spans}, widgets::{Block, Borders, Paragraph}, Terminal -}; -use crossterm::{ - event::{DisableMouseCapture, EnableMouseCapture}, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + Terminal, + backend::CrosstermBackend, + layout::{ + Constraint, + Direction, + Layout, + }, + style::{ + Color, + Style, + }, + text::{ + Span, + Spans, + }, + widgets::{ + Block, + Borders, + Paragraph, + }, }; -pub async fn tui_main(port: u16, host: Option, cancellation_token: CancellationToken) -> Result<(), io::Error> { +pub async fn tui_main( + port: u16, + host: Option, + cancellation_token: CancellationToken, +) -> Result<(), io::Error> { info!("Starting TUI..."); - + // setup terminal enable_raw_mode().err(); let mut stdout = io::stdout(); @@ -24,16 +59,13 @@ pub async fn tui_main(port: u16, host: Option, cancellation_token: Cance let chunks = Layout::default() .direction(Direction::Vertical) .margin(1) - .constraints( - [ - Constraint::Percentage(100), - ].as_ref() - ) + .constraints([Constraint::Percentage(100)].as_ref()) .split(f.size()); - let title = Paragraph::new(vec![ - Spans::from(Span::styled(format!("Connecting to {}:{}...", host.unwrap(), port), Style::default().fg(Color::Yellow))) - ]) - .block(Block::default().title("Clover TUI").borders(Borders::ALL)); + let title = Paragraph::new(vec![Spans::from(Span::styled( + format!("Connecting to {}:{}...", host.unwrap(), port), + Style::default().fg(Color::Yellow), + ))]) + .block(Block::default().title("Clover TUI").borders(Borders::ALL)); f.render_widget(title, chunks[0]); })?; @@ -45,7 +77,8 @@ pub async fn tui_main(port: u16, host: Option, cancellation_token: Cance terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture - ).err(); + ) + .err(); terminal.show_cursor().err(); Ok(()) diff --git a/clover-hub/src/utils.rs b/clover-hub/src/utils.rs index c8672e1..2f3c507 100644 --- a/clover-hub/src/utils.rs +++ b/clover-hub/src/utils.rs @@ -1,12 +1,33 @@ -use std::{hash::{DefaultHasher, Hash, Hasher}, sync::Arc}; -use tokio::{fs, io::AsyncReadExt}; -use api_key::types::{ApiKeyResults, Default, StringGenerator}; -use chrono::prelude::{DateTime, Utc}; +use crate::server::evtbuzz::models::{ + CoreUserConfig, + IPCMessageWithId, + Store, +}; +use api_key::types::{ + ApiKeyResults, + Default, + StringGenerator, +}; +use chrono::prelude::{ + DateTime, + Utc, +}; use os_path::OsPath; use simple_error::SimpleError; +use std::{ + hash::{ + DefaultHasher, + Hash, + Hasher, + }, + sync::Arc, +}; use tokio::sync::mpsc::UnboundedSender; +use tokio::{ + fs, + io::AsyncReadExt, +}; use uuid::Uuid; -use crate::server::evtbuzz::models::{CoreUserConfig, IPCMessageWithId, Store}; pub struct RecvSync(pub std::sync::mpsc::Receiver); @@ -33,7 +54,7 @@ pub fn gen_api_key() -> String { ..StringGenerator::default() }; let key: ApiKeyResults = api_key::string(options); - + match key { ApiKeyResults::String(res) => res, ApiKeyResults::StringArray(res_vec) => res_vec.join(""), @@ -45,7 +66,7 @@ pub async fn gen_api_key_with_check(store: &Store) -> String { loop { let api_key = gen_api_key(); match store.api_keys.lock().await.get(&api_key.clone()) { - Some(_) => {}, + Some(_) => {} None => { break api_key; } @@ -58,7 +79,7 @@ pub async fn gen_uid_with_check(store: &Store) -> String { loop { let uid = Uuid::new_v4().to_string(); match store.users.lock().await.get(&uid.clone()) { - Some(_) => {}, + Some(_) => {} None => { break uid; } @@ -70,7 +91,7 @@ pub async fn gen_message_id_with_check(store: &Store) -> String { loop { let message_id = Uuid::new_v4().to_string(); match store.messages.lock().await.get(&message_id.clone()) { - Some(_) => {}, + Some(_) => {} None => { break message_id; } @@ -78,13 +99,18 @@ pub async fn gen_message_id_with_check(store: &Store) -> String { } } -pub async fn gen_ipc_message(store: &Store, user_config: &CoreUserConfig, kind: String, message: String) -> IPCMessageWithId { +pub async fn gen_ipc_message( + store: &Store, + user_config: &CoreUserConfig, + kind: String, + message: String, +) -> IPCMessageWithId { let message_id = gen_message_id_with_check(&store.clone()).await; - IPCMessageWithId { + IPCMessageWithId { id: message_id.clone(), author: user_config.id.to_string(), kind, - message + message, } } @@ -92,7 +118,7 @@ pub async fn gen_cid_with_check(store: &Store) -> String { loop { let client_id = Uuid::new_v4().to_string(); match store.clients.lock().await.get(&client_id.clone()) { - Some(_) => {}, + Some(_) => {} None => { break client_id; } @@ -101,11 +127,11 @@ pub async fn gen_cid_with_check(store: &Store) -> String { } pub async fn send_ipc_message( - store: &Store, - user_config: &CoreUserConfig, - ipc_tx: Arc>, - kind: String, - message: String + store: &Store, + user_config: &CoreUserConfig, + ipc_tx: Arc>, + kind: String, + message: String, ) -> Result<(), tokio::sync::mpsc::error::SendError> { ipc_tx.send(gen_ipc_message(store, user_config, kind, message).await) } @@ -117,33 +143,27 @@ pub async fn read_file(path: OsPath) -> Result { if path.exists() { match fs::File::open(path.to_path()).await { - Ok(mut file) => { - match file.read_to_string(&mut contents).await { - Ok(_) => { - ret = Some(contents); - }, - Err(e) => { - err = Some(SimpleError::from(e)); - } + Ok(mut file) => match file.read_to_string(&mut contents).await { + Ok(_) => { + ret = Some(contents); + } + Err(e) => { + err = Some(SimpleError::from(e)); } }, - Err(e) => { - err = Some(SimpleError::from(e)) - } + Err(e) => err = Some(SimpleError::from(e)), } } else { err = Some(SimpleError::new("Path does not exist!")); } match err { - Some(e) => { Err(e) }, - None => { - match ret { - Some(val) => { Ok(val) }, - None => { - Err(SimpleError::new("Impossible state, no error reported but return value is missing!")) - } - } - } + Some(e) => Err(e), + None => match ret { + Some(val) => Ok(val), + None => Err(SimpleError::new( + "Impossible state, no error reported but return value is missing!", + )), + }, } } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..0a8a762 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +unstable_features = true +imports_layout = "Vertical" +tab_spaces = 2