// mod to communicate with api-grub config file // 1) check changes in unix-socket // 2) save changes in local config file use anyhow::{Error, Ok, Result}; use log::{info, warn, error}; use std::{fs, path::Path}; use serde_json::from_str; use tokio::{io::AsyncReadExt, net::UnixListener}; use tokio::time::{sleep, Duration}; use std::result::Result::Ok as stdOk; use tokio::sync::mpsc::Sender; use integr_structs::api::v3::Config; const CONFIG_PATH: &str = "config_api.json"; const SOCKET_PATH: &str = "api-grub.sock"; // TODO: rewrite to use current_exe /// # Fn `pull_local_config` /// /// ## function to one-time pulling local config by straight reading /// /// ### Dev-Info : /// /// *input* : - /// /// *output* : `anyhow::Result` /// /// *initiator* : fn `main` /// /// *managing* : - /// pub async fn pull_local_config() -> Result { let path = Path::new(CONFIG_PATH); if path.exists() && path.is_file() { let config: Config = from_str( &fs::read_to_string(CONFIG_PATH)? )?; Ok(config) } else { Err(Error::msg("No local conf was found")) } } // for config pulling /// # Fn `pull_local_config` /// /// ## function to init Unix-Socket listening for grabbing new configs /// /// ### Dev-Info : /// /// *input* : `&tokio::sync::mpsc::Sender` /// /// *output* : `anyhow::Result<()>` /// /// *initiator* : fn `main` /// /// *managing* : - /// /// *depends on* : `const SOCKET_PATH` /// pub async fn init_config_grub_mechanism(tx: &Sender) -> Result<()> { info!("Initializing Unix-Socket listening for pulling new configs..."); let server = init_unix_listener().await?; info!("Listening Unix-Socket..."); let mut buffer = String::new(); loop { if let stdOk((mut stream, _)) = server.accept().await { if let Err(er) = stream.read_to_string(&mut buffer).await { warn!("Cannot read config from stream due to {}", er); } else { let config: Result = from_str(&buffer); if let stdOk(conf) = config { info!("New config was pulled from Unix-Stream. Saving it locally and sharing with API-grub module..."); if let Err(er) = save_new_config(&buffer).await { error!("Cannot save new config locally due to: {}", er); } let _ = tx.send(conf).await; } else if let Err(er) = config { warn!("Invalid config was pulled. Error: {}", er); } } } sleep(Duration::from_millis(500)).await; } } // saving new config locally /// # Fn `save_new_config` /// /// ## function for saving new config locally /// /// ### Dev-Info : /// /// *input* : `&String | &str` /// /// *output* : `anyhow::Result<()>` /// /// *initiator* : fn `main` /// /// *managing* : - /// /// *depends on* : `const CONFIG_PATH` /// async fn save_new_config(config: &String) -> Result<()> { fs::write(CONFIG_PATH, config)?; Ok(()) } /// # Fn `pull_local_config` /// /// ## function for saving new config locally /// /// ### Dev-Info : /// /// *input* : `&tokio::sync::mpsc::Sender` /// /// *output* : `anyhow::Result` /// /// *initiator* : fn `init_config_grub_mechanism` /// /// *managing* : - /// /// *depends on* : `const SOCKET_PATH` /// async fn init_unix_listener() -> Result { let _ = fs::remove_file(SOCKET_PATH); Ok(UnixListener::bind(SOCKET_PATH)?) } #[cfg(test)] mod config_unittests { use super::*; use tokio::test; #[test] async fn check_init_unix_listener() { let res = init_unix_listener().await; if res.is_ok() { assert!(fs::remove_file(SOCKET_PATH).is_ok()) } else { assert!(false) } } #[test] async fn check_save_new_config() { use std::fs; use integr_structs::api::v3::Config; use serde_json::to_string; let test_config_path = "test_config_api.json"; // config gen let config = to_string::(&Config::default()); assert!(config.is_ok()); let config = config.unwrap(); // config file gen and write assert!(fs::File::create(test_config_path).is_ok()); assert!(fs::write(test_config_path, config).is_ok()); // config file reading and checking content let file = fs::read_to_string(test_config_path); assert!(file.is_ok()); let file = file.unwrap(); assert_ne!(file.len(), 0); // deleting test config file assert!(fs::remove_file(test_config_path).is_ok()) } #[test] async fn check_pull_local_config() { use std::path::Path; let local_config = Path::new(CONFIG_PATH); assert_eq!(local_config.is_file() && local_config.exists(), pull_local_config().await.is_ok()) } }