diff --git a/src/gui/elements/console_card.rs b/src/gui/elements/console_card.rs index 3a2955c..c425dd3 100644 --- a/src/gui/elements/console_card.rs +++ b/src/gui/elements/console_card.rs @@ -1,19 +1,32 @@ -use actix_web::web::block; -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use crossterm::event::{KeyCode, KeyEvent}; +use json::JsonValue; use ratatui::{ Frame, - layout::{Alignment, Constraint, Layout, Rect}, + layout::Rect, style::{Color, Style}, text::{Line, Span}, widgets::{Block, Borders, Paragraph}, }; -use crate::gui::{ - elements::elements::{Element, InteractableElement, JoinableElement}, - interaction_result::InteractionResult, - util::borders::draw_block_joins, +use crate::{ + ACTIVE_TASKS, + data::communication::{CommunicationType, CommunicationValue, DataTypes}, + gui::{ + elements::elements::{Element, InteractableElement, JoinableElement}, + interaction_result::InteractionResult, + ui::{FPS, UI}, + util::borders::draw_block_joins, + }, + log, log_cv, + omikron::omikron_connection::OMIKRON_CONNECTION, + users::{user_manager, user_profile::UserProfile}, + util::file_util, +}; +use std::{ + any::Any, + sync::Arc, + time::{Duration, SystemTime}, }; -use std::any::Any; pub struct ConsoleCard { focused: bool, @@ -115,6 +128,10 @@ impl InteractableElement for ConsoleCard { fn interact(&mut self, key: KeyEvent) -> InteractionResult { match key.code { KeyCode::Enter => { + let command = self.content.clone(); + tokio::spawn(async move { + run_command(&command).await; + }); self.content = "".to_string(); InteractionResult::Handled } @@ -145,3 +162,114 @@ impl InteractableElement for ConsoleCard { self.focused = f; } } +pub async fn run_command(command: &str) { + log!(":{}", command); + + let parts = command.split(" ").collect::>(); + + match parts.as_slice() { + ["tasks"] => { + let active_tasks: Vec = + ACTIVE_TASKS.clone().iter().map(|v| v.to_string()).collect(); + log!("Active tasks: {:?}", active_tasks); + } + ["fps"] => { + log!("FPS: {}", *FPS.read().await); + } + ["ping"] => { + let time = 20; + + let now = SystemTime::now(); + + let conn = { + let guard = OMIKRON_CONNECTION.read().await; + guard.as_ref().cloned() + }; + + let conn = match conn { + Some(c) => c, + None => return, + }; + + let response_cv = conn + .await_response( + &CommunicationValue::new(CommunicationType::ping), + Some(Duration::from_secs(time)), + ) + .await; + + let elapsed = now.elapsed().unwrap_or(Duration::ZERO); + + match response_cv { + Ok(response) => log_cv!(response.add_data( + DataTypes::get_time, + JsonValue::from(elapsed.as_millis() as i64) + )), + Err(err) => log!("Ping error: {:?}", err), + } + } + ["ping", time] => { + let time = time.parse::().unwrap_or(20); + + let conn = { + let guard = OMIKRON_CONNECTION.read().await; + guard.as_ref().cloned() + }; + + let conn = match conn { + Some(c) => c, + None => return, + }; + + let response_cv = conn + .await_response( + &CommunicationValue::new(CommunicationType::ping), + Some(Duration::from_secs(time)), + ) + .await; + match response_cv { + Ok(response) => log_cv!(response), + Err(err) => log!("Ping error: {:?}", err), + } + } + ["user", "add", username] => { + if let (Some(user), Some(_)) = user_manager::create_user(username).await { + log!("Created user {}", user.user_id); + } else { + log!("Failed to create user"); + } + } + ["user", "remove", username] => { + if let Some(user) = user_manager::get_user_by_username(username) { + user_manager::remove_user(user.user_id); + log!("Removed user {}", user.user_id); + } else { + log!("Failed to find user"); + } + } + ["user", "list"] => { + let users: Vec = user_manager::get_users(); + for user in users { + let storage = file_util::get_designed_storage(user.user_id); + log!( + "> Username: {}, ID: {}, created at: {}, storage: {}", + user.username, + user.user_id, + user.created_at, + storage + ); + } + } + ["user", "info", username] => { + if let Some(user) = user_manager::get_user_by_username(username) { + user_manager::remove_user(user.user_id); + log!("Removed user {}", user.user_id); + } else { + log!("Failed to find user"); + } + } + _ => { + log!("Unknown command"); + } + } +} diff --git a/src/gui/input_handler.rs b/src/gui/input_handler.rs index 5205dda..d499f56 100644 --- a/src/gui/input_handler.rs +++ b/src/gui/input_handler.rs @@ -7,10 +7,7 @@ use std::time::Duration; pub fn setup_input_handler(ui: Arc) { tokio::spawn(async move { - ACTIVE_TASKS - .lock() - .unwrap() - .push("Input Handler".to_string()); + ACTIVE_TASKS.insert("Input Handler".to_string()); loop { { @@ -42,8 +39,7 @@ pub fn setup_input_handler(ui: Arc) { } } { - let mut tasks = ACTIVE_TASKS.lock().unwrap(); - tasks.retain(|t| t != "Input Handler"); + ACTIVE_TASKS.remove("Input Handler"); } }); } diff --git a/src/gui/screens/main_screen.rs b/src/gui/screens/main_screen.rs index acec65d..8543451 100644 --- a/src/gui/screens/main_screen.rs +++ b/src/gui/screens/main_screen.rs @@ -1,7 +1,7 @@ use crate::gui::{ elements::{ console_card::ConsoleCard, - elements::{Element, InteractableElement, JoinableElement}, + elements::{InteractableElement, JoinableElement}, log_card::LogCard, }, interaction_result::InteractionResult, diff --git a/src/gui/ui.rs b/src/gui/ui.rs index 89ecb74..50c1d6b 100644 --- a/src/gui/ui.rs +++ b/src/gui/ui.rs @@ -1,5 +1,5 @@ use crate::{ - SHUTDOWN, + ACTIVE_TASKS, SHUTDOWN, gui::{ input_handler::setup_input_handler, interaction_result::InteractionResult, screens::screens::Screen, @@ -13,10 +13,12 @@ use std::{ sync::{Arc, Mutex}, time::Duration, }; -use tokio::sync::RwLock; +use tokio::{sync::RwLock, time::Instant}; /// UI state and rendering pub static UNIQUE: Lazy> = Lazy::new(|| RwLock::new(true)); + +pub static FPS: Lazy> = Lazy::new(|| RwLock::new(0.0)); pub struct UI { pub terminal: Arc>>>, screen: Arc>>>, @@ -26,6 +28,9 @@ pub fn start_tui() -> Arc { let ui = Arc::new(UI::new()); let uic = ui.clone(); tokio::spawn(async move { + ACTIVE_TASKS.insert("UI Renderer".to_string()); + let mut last_render = Instant::now(); + let mut last: Vec = Vec::new(); loop { if *SHUTDOWN.read().await { break; @@ -33,10 +38,19 @@ pub fn start_tui() -> Arc { if *UNIQUE.read().await { uic.render().await; - } else { - tokio::time::sleep(Duration::from_millis(50)).await; + let elapsed = last_render.elapsed().as_secs_f64(); + if elapsed > 0.0 { + last.push(1.0 / elapsed); + } + if last.len() > 10 { + last.remove(0); + } + *FPS.write().await = last.iter().sum::() / last.len() as f64; + last_render = Instant::now(); } + tokio::time::sleep(Duration::from_millis(16)).await; } + ACTIVE_TASKS.remove("UI Renderer"); ratatui::restore(); }); setup_input_handler(ui.clone()); diff --git a/src/main.rs b/src/main.rs index b9b4ffd..082f7db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use dashmap::DashSet; use once_cell::sync::Lazy; use pnet::datalink::NetworkInterface; use std::sync::Arc; @@ -37,7 +38,7 @@ pub static APP_STATE: LazyLock>> = pub static SHUTDOWN: Lazy> = Lazy::new(|| RwLock::new(false)); pub static RELOAD: Lazy> = Lazy::new(|| RwLock::new(true)); -pub static ACTIVE_TASKS: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); +pub static ACTIVE_TASKS: Lazy> = Lazy::new(|| DashSet::new()); #[tokio::main(flavor = "multi_thread", worker_threads = 8)] #[allow(unused_must_use, dead_code)] @@ -48,20 +49,12 @@ async fn main() { let ui = start_tui(); - let ui_clone = ui.clone(); - tokio::spawn(async move { - loop { - ui_clone.render().await; - sleep(Duration::from_millis(16)).await; - } - }); - let (eula, tos_pp) = consent_state::check(ui.clone()).await; if !eula { *SHUTDOWN.write().await = true; loop { - if ACTIVE_TASKS.lock().unwrap().is_empty() { + if ACTIVE_TASKS.is_empty() { break; } sleep(Duration::from_secs(1)).await; @@ -73,7 +66,7 @@ async fn main() { if !tos_pp { *SHUTDOWN.write().await = true; loop { - if ACTIVE_TASKS.lock().unwrap().is_empty() { + if ACTIVE_TASKS.is_empty() { break; } sleep(Duration::from_millis(100)).await; @@ -171,8 +164,10 @@ async fn main() { } let omikron: Arc = Arc::new(OmikronConnection::new()); omikron.connect().await; - let mut omikron_connection = OMIKRON_CONNECTION.write().await; - *omikron_connection = Some(omikron.clone()); + { + let mut omikron_connection = OMIKRON_CONNECTION.write().await; + *omikron_connection = Some(omikron.clone()); + } log_t!("setup_completed"); loop { if *SHUTDOWN.read().await { @@ -186,7 +181,7 @@ async fn main() { } if *RELOAD.read().await { loop { - if ACTIVE_TASKS.lock().unwrap().is_empty() { + if ACTIVE_TASKS.is_empty() { break; } sleep(Duration::from_secs(1)).await; diff --git a/src/omikron/omikron_connection.rs b/src/omikron/omikron_connection.rs index f7b0c99..ae8d071 100644 --- a/src/omikron/omikron_connection.rs +++ b/src/omikron/omikron_connection.rs @@ -4,9 +4,8 @@ use crate::util::chat_files::{MessageState, change_message_state}; use crate::util::chats_util::{get_user, mod_user}; use crate::util::crypto_util::{DataFormat, SecurePayload}; use crate::util::file_util::{get_children, load_file, save_file}; -use crate::util::logger::PrintType; use crate::util::{chat_files, chats_util}; -use crate::{ACTIVE_TASKS, SHUTDOWN, log, log_cv, log_t}; +use crate::{ACTIVE_TASKS, SHUTDOWN, log, log_cv_in, log_cv_out, log_t}; use crate::{ data::communication::{CommunicationType, CommunicationValue, DataTypes}, util::{config_util::CONFIG, crypto_helper}, @@ -166,6 +165,9 @@ impl OmikronConnection { } pub async fn send_message(&self, cv: &CommunicationValue) { + if !cv.is_type(CommunicationType::ping) { + log_cv_out!(cv); + } Self::send_message_static( &self.writer, Arc::clone(&self.is_connected), @@ -184,31 +186,31 @@ impl OmikronConnection { let sel_out = self.clone(); { - ACTIVE_TASKS.lock().unwrap().push("Listener".to_string()); + ACTIVE_TASKS.insert("Omikron Listener".to_string()); } tokio::spawn(async move { while let Some(msg) = read_half.next().await { if *SHUTDOWN.read().await { break; } - sel_out.clone().handle_message( - msg, - waiting_out.clone(), - writer_out.clone(), - is_connected_out.clone(), - ); + sel_out + .clone() + .handle_message( + msg, + waiting_out.clone(), + writer_out.clone(), + is_connected_out.clone(), + ) + .await; } *is_connected_out.lock().await = false; log!("Connection closed."); + { + ACTIVE_TASKS.remove("Omikron Listener"); + } }); - { - ACTIVE_TASKS - .lock() - .unwrap() - .retain(|t| !t.eq(&"Listener".to_string())); - } } - pub fn handle_message( + pub async fn handle_message( self: Arc, msg: Result, waiting: Arc>>, @@ -219,495 +221,473 @@ impl OmikronConnection { >, is_connected: Arc>, ) { - tokio::spawn(async move { - match msg { - Ok(Message::Close(Some(frame))) => { - log!("[Omikron] Closed: {:?}", frame); - *is_connected.lock().await = false; + match msg { + Ok(Message::Close(Some(frame))) => { + log!("[Omikron] Closed: {:?}", frame); + *is_connected.lock().await = false; + return; + } + Ok(Message::Text(text)) => { + let cv = CommunicationValue::from_json(&text); + if let Some((_, y)) = waiting.remove(&cv.get_id()) { + y(cv); return; } - Ok(Message::Text(text)) => { - let cv = CommunicationValue::from_json(&text); - if cv.is_type(CommunicationType::pong) { - self.handle_pong(&cv, true).await; - return; - } - if cv.is_type(CommunicationType::challenge) { - let conf = CONFIG.read().await; - let private_key = conf.get_private_key().unwrap(); - drop(conf); + if cv.is_type(CommunicationType::pong) { + self.handle_pong(&cv, true).await; + return; + } + log_cv_in!(&cv); + if cv.is_type(CommunicationType::challenge) { + let conf = CONFIG.read().await; + let private_key = conf.get_private_key().unwrap(); + drop(conf); - let omikron_public_key = cv - .get_data(DataTypes::public_key) - .unwrap() - .as_str() - .unwrap(); - let encrypted_challenge = - cv.get_data(DataTypes::challenge).unwrap().as_str().unwrap(); + let omikron_public_key = cv + .get_data(DataTypes::public_key) + .unwrap() + .as_str() + .unwrap(); + let encrypted_challenge = + cv.get_data(DataTypes::challenge).unwrap().as_str().unwrap(); - let solved_challenge = { - if let Ok(decrypted) = SecurePayload::new( - encrypted_challenge, - DataFormat::Base64, - crypto_helper::load_secret_key(&private_key).unwrap(), + let solved_challenge = { + if let Ok(decrypted) = SecurePayload::new( + encrypted_challenge, + DataFormat::Base64, + crypto_helper::load_secret_key(&private_key).unwrap(), + ) { + if let Ok(decrypted) = decrypted.decrypt_x448( + crypto_helper::load_public_key(omikron_public_key).unwrap(), ) { - if let Ok(decrypted) = decrypted.decrypt_x448( - crypto_helper::load_public_key(omikron_public_key).unwrap(), - ) { - Some(decrypted) - } else { - None - } + Some(decrypted) } else { None } - }; - - if let Some(decrypted) = solved_challenge { - let response = - CommunicationValue::new(CommunicationType::challenge_response) - .with_id(cv.get_id()) - .add_data( - DataTypes::challenge, - JsonValue::String(decrypted.export(DataFormat::Base64)), - ); - - self.send_message(&response).await; } else { - log!("Failed to decrypt challenge"); + None } + }; - return; - } - if cv.is_type(CommunicationType::success) { - let iota_id = cv - .get_data(DataTypes::iota_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); - if iota_id != 0 { - let mut conf = CONFIG.write().await; - conf.change("iota_id", JsonValue::Number(iota_id.into())); - conf.update(); - log!("Iota registered with ID: {}", iota_id); + if let Some(decrypted) = solved_challenge { + let response = + CommunicationValue::new(CommunicationType::challenge_response) + .with_id(cv.get_id()) + .add_data( + DataTypes::challenge, + JsonValue::String(decrypted.export(DataFormat::Base64)), + ); - let login_message = - CommunicationValue::new(CommunicationType::identification) - .add_data( - DataTypes::iota_id, - JsonValue::Number(json::number::Number::from(iota_id)), - ); - - let self_clone = self.clone(); - tokio::spawn(async move { - self_clone.send_message(&login_message).await; - }); - } else { - log("Iota registration failed."); - } - return; - } - if cv.is_type(CommunicationType::identification_response) { - if let Some(accepted) = cv.get_data(DataTypes::accepted) { - log!("Omikron connected: {}", accepted.to_string()); - } - return; + self.send_message(&response).await; + } else { + log!("Failed to decrypt challenge"); } - // ************************************************ // - // Direct messages // - // ************************************************ // - log_cv!(PrintType::General, &cv); - if let Some((_, y)) = waiting.remove(&cv.get_id()) { - y(cv); - return; - } - if cv.is_type(CommunicationType::message_state) { - let sender_id = &cv.get_sender(); - let receiver_id = &cv.get_receiver(); + return; + } + if cv.is_type(CommunicationType::success) { + let iota_id = cv + .get_data(DataTypes::iota_id) + .unwrap_or(&JsonValue::Null) + .as_i64() + .unwrap_or(0); + if iota_id != 0 { + let mut conf = CONFIG.write().await; + conf.change("iota_id", JsonValue::Number(iota_id.into())); + conf.update(); + log!("Iota registered with ID: {}", iota_id); - let _ = chat_files::change_message_state( - cv.get_data(DataTypes::send_time) - .unwrap_or(&JsonValue::new_object()) - .as_i64() - .unwrap_or(0) as i64, - *receiver_id, - *sender_id, - MessageState::from_str( - cv.get_data(DataTypes::message_state) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ), - ); + let login_message = + CommunicationValue::new(CommunicationType::identification).add_data( + DataTypes::iota_id, + JsonValue::Number(json::number::Number::from(iota_id)), + ); + + let self_clone = self.clone(); + tokio::spawn(async move { + self_clone.send_message(&login_message).await; + }); + } else { + log("Iota registration failed."); } - if cv.is_type(CommunicationType::message_other_iota) { - let sender_id = &cv.get_sender(); - let receiver_id = &cv.get_receiver(); - let timestamp = cv - .get_data(DataTypes::send_time) + return; + } + if cv.is_type(CommunicationType::identification_response) { + if let Some(accepted) = cv.get_data(DataTypes::accepted) { + log!("Omikron connected: {}", accepted.to_string()); + } + return; + } + + // ************************************************ // + // Direct messages // + // ************************************************ // + if cv.is_type(CommunicationType::message_state) { + let sender_id = &cv.get_sender(); + let receiver_id = &cv.get_receiver(); + + let _ = chat_files::change_message_state( + cv.get_data(DataTypes::send_time) .unwrap_or(&JsonValue::new_object()) .as_i64() - .unwrap_or( - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as i64, - ); - chat_files::add_message( - timestamp as u128, - false, - *receiver_id, - *sender_id, - cv.get_data(DataTypes::content).unwrap().as_str().unwrap(), - ); - let user_forward = CommunicationValue::new(CommunicationType::message_live) - .with_id(cv.get_id()) - .with_receiver(*receiver_id) - .add_data( - DataTypes::send_time, - cv.get_data(DataTypes::send_time).unwrap().clone(), - ) - .add_data( - DataTypes::message, - cv.get_data(DataTypes::content).unwrap().clone(), - ) - .add_data( - DataTypes::sender_id, - JsonValue::Number(Number::from(cv.get_sender())), - ); - let user_resp = self - .clone() - .await_response(&user_forward, Some(Duration::from_secs(10))) - .await; - - if let Ok(user_resp) = user_resp { - let ms = MessageState::from_str( - user_resp - .get_data(DataTypes::message_state) - .unwrap_or(&JsonValue::Null) - .as_str() - .unwrap_or(""), - ) - .upgrade(MessageState::Received); - let _ = change_message_state( - timestamp, - *receiver_id, - *sender_id, - ms.clone(), - ); - self.send_message( - &CommunicationValue::new(CommunicationType::message_state) - .with_id(cv.get_id()) - .with_receiver(*sender_id) - .with_sender(*receiver_id) - .add_data( - DataTypes::send_time, - cv.get_data(DataTypes::send_time).unwrap().clone(), - ) - .add_data( - DataTypes::message_state, - JsonValue::from(ms.as_str()), - ), - ) - .await; - } else { - self.send_message( - &CommunicationValue::new(CommunicationType::message_state) - .with_id(cv.get_id()) - .with_receiver(*sender_id) - .with_sender(*receiver_id) - .add_data( - DataTypes::send_time, - cv.get_data(DataTypes::send_time).unwrap().clone(), - ) - .add_data( - DataTypes::message_state, - JsonValue::from(MessageState::Sent.as_str()), - ), - ) - .await; - } - return; - } - - if cv.is_type(CommunicationType::message_send) { - let my_id = cv.get_sender(); - let other_id = cv - .get_data(DataTypes::receiver_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); - let now_ms = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as u128; - - chat_files::add_message( - now_ms, - true, - my_id, - other_id, - &*cv.get_data(DataTypes::content).unwrap().to_string(), - ); - - let ack = CommunicationValue::new(CommunicationType::success) - .with_id(cv.get_id()) - .with_receiver(my_id); - Self::send_message_static( - &writer.clone(), - Arc::clone(&is_connected), - ack.to_json().to_string(), - ) - .await; - - let forward = - CommunicationValue::new(CommunicationType::message_other_iota) - .with_id(cv.get_id()) - .with_receiver(other_id) - .add_data( - DataTypes::receiver_id, - JsonValue::Number(Number::from(other_id)), - ) - .with_sender(my_id) - .add_data( - DataTypes::send_time, - JsonValue::String(now_ms.to_string()), - ) - .add_data( - DataTypes::sender_id, - JsonValue::Number(Number::from(my_id)), - ) - .add_data( - DataTypes::content, - JsonValue::String( - cv.get_data(DataTypes::content).unwrap().to_string(), - ), - ); - Self::send_message_static( - &writer.clone(), - is_connected, - forward.to_json().to_string(), - ) - .await; - return; - } - - if cv.is_type(CommunicationType::messages_get) { - let my_id = cv.get_sender(); - let partner_id = cv - .get_data(DataTypes::user_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); - let offset = cv - .get_data(DataTypes::offset) - .unwrap_or(&JsonValue::Null) - .to_string() - .parse::() - .unwrap_or(0); - let amount = cv - .get_data(DataTypes::amount) - .unwrap_or(&JsonValue::Null) - .to_string() - .parse::() - .unwrap_or(0); - let messages = chat_files::get_messages(my_id, partner_id, offset, amount); - let resp = CommunicationValue::new(CommunicationType::messages_get) - .with_id(cv.get_id()) - .with_receiver(my_id) - .add_data(DataTypes::messages, messages); - - Self::send_message_static( - &writer.clone(), - is_connected, - resp.to_json().to_string(), - ) - .await; - return; - } - - if cv.is_type(CommunicationType::get_chats) { - let user_id = cv.get_sender(); - let users = chats_util::get_users(user_id); - let resp = CommunicationValue::new(CommunicationType::get_chats) - .with_id(cv.get_id()) - .with_receiver(user_id) - .add_data(DataTypes::user_ids, users); - Self::send_message_static( - &writer.clone(), - is_connected, - resp.to_json().to_string(), - ) - .await; - return; - } - - if cv.is_type(CommunicationType::add_conversation) { - let user_id = cv.get_sender(); - let other_id = cv - .get_data(DataTypes::chat_partner_id) - .unwrap_or(&JsonValue::Null) - .as_i64() - .unwrap_or(0); - let mut contact = - get_user(user_id, other_id).unwrap_or(Contact::new(other_id)); - contact.set_last_message_at( + .unwrap_or(0) as i64, + *receiver_id, + *sender_id, + MessageState::from_str( + cv.get_data(DataTypes::message_state) + .unwrap_or(&JsonValue::Null) + .as_str() + .unwrap_or(""), + ), + ); + } + if cv.is_type(CommunicationType::message_other_iota) { + let sender_id = &cv.get_sender(); + let receiver_id = &cv.get_receiver(); + let timestamp = cv + .get_data(DataTypes::send_time) + .unwrap_or(&JsonValue::new_object()) + .as_i64() + .unwrap_or( SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis() as i64, ); - mod_user(user_id, &contact); - let resp = CommunicationValue::new(CommunicationType::add_conversation) - .with_id(cv.get_id()) - .with_receiver(user_id); - Self::send_message_static( - &writer.clone(), - is_connected, - resp.to_json().to_string(), + chat_files::add_message( + timestamp as u128, + false, + *receiver_id, + *sender_id, + cv.get_data(DataTypes::content).unwrap().as_str().unwrap(), + ); + let user_forward = CommunicationValue::new(CommunicationType::message_live) + .with_id(cv.get_id()) + .with_receiver(*receiver_id) + .add_data( + DataTypes::send_time, + cv.get_data(DataTypes::send_time).unwrap().clone(), ) - .await; - return; - } - - if cv.is_type(CommunicationType::add_community) { - UserCommunityUtil::add_community( - cv.get_sender(), - cv.get_data(DataTypes::community_address) - .unwrap() - .to_string(), - cv.get_data(DataTypes::community_title).unwrap().to_string(), - cv.get_data(DataTypes::position).unwrap().to_string(), + .add_data( + DataTypes::message, + cv.get_data(DataTypes::content).unwrap().clone(), + ) + .add_data( + DataTypes::sender_id, + JsonValue::Number(Number::from(cv.get_sender())), ); - let resp = CommunicationValue::new(CommunicationType::add_community) - .with_id(cv.get_id()) - .with_receiver(cv.get_sender()); - Self::send_message_static( - &writer.clone(), - is_connected, - resp.to_json().to_string(), + let user_resp = self + .clone() + .await_response(&user_forward, Some(Duration::from_secs(10))) + .await; + + if let Ok(user_resp) = user_resp { + let ms = MessageState::from_str( + user_resp + .get_data(DataTypes::message_state) + .unwrap_or(&JsonValue::Null) + .as_str() + .unwrap_or(""), + ) + .upgrade(MessageState::Received); + let _ = + change_message_state(timestamp, *receiver_id, *sender_id, ms.clone()); + self.send_message( + &CommunicationValue::new(CommunicationType::message_state) + .with_id(cv.get_id()) + .with_receiver(*sender_id) + .with_sender(*receiver_id) + .add_data( + DataTypes::send_time, + cv.get_data(DataTypes::send_time).unwrap().clone(), + ) + .add_data(DataTypes::message_state, JsonValue::from(ms.as_str())), ) .await; - return; - } - - if cv.is_type(CommunicationType::get_communities) { - let resp = CommunicationValue::new(CommunicationType::get_communities) - .with_id(cv.get_id()) - .with_receiver(cv.get_sender()) - .add_array( - DataTypes::communities, - UserCommunityUtil::get_communities(cv.get_sender()), - ); - Self::send_message_static( - &writer.clone(), - is_connected, - resp.to_json().to_string(), + } else { + self.send_message( + &CommunicationValue::new(CommunicationType::message_state) + .with_id(cv.get_id()) + .with_receiver(*sender_id) + .with_sender(*receiver_id) + .add_data( + DataTypes::send_time, + cv.get_data(DataTypes::send_time).unwrap().clone(), + ) + .add_data( + DataTypes::message_state, + JsonValue::from(MessageState::Sent.as_str()), + ), ) .await; - return; } - - if cv.is_type(CommunicationType::remove_community) { - UserCommunityUtil::remove_community( - cv.get_sender(), - cv.get_data(DataTypes::community_address) - .unwrap() - .to_string(), - ); // needs UserCommunityUtil - let resp = CommunicationValue::new(CommunicationType::remove_community) - .with_id(cv.get_id()) - .with_receiver(cv.get_sender()); - Self::send_message_static( - &writer.clone(), - is_connected, - resp.to_json().to_string(), - ) - .await; - return; - } - - if cv.is_type(CommunicationType::settings_save) { - let my_id = cv.get_sender(); - let settings_name = - cv.get_data(DataTypes::settings_name).unwrap().to_string(); - let settings_value = cv.get_data(DataTypes::payload).unwrap().to_string(); - - save_file( - &format!("users/{}/settings/", my_id), - &format!("{}.settings", settings_name), - &settings_value, - ); - - let response = CommunicationValue::new(CommunicationType::settings_save) - .with_receiver(my_id) - .with_id(cv.get_id()); - - Self::send_message_static( - &writer.clone(), - is_connected, - response.to_json().to_string(), - ) - .await; - return; - } - if cv.is_type(CommunicationType::settings_load) { - let my_id = cv.get_sender(); - let settings_name = - cv.get_data(DataTypes::settings_name).unwrap().to_string(); - let settings_value_str = load_file( - &format!("users/{}/settings/", my_id), - &format!("{}.settings", settings_name), - ); - let settings_value_json = JsonValue::from(settings_value_str); - let response = CommunicationValue::new(CommunicationType::settings_load) - .with_id(cv.get_id()) - .with_receiver(my_id) - .add_data(DataTypes::payload, settings_value_json) - .add_data_str(DataTypes::settings_name, settings_name); - - Self::send_message_static( - &writer.clone(), - is_connected, - response.to_json().to_string(), - ) - .await; - return; - } - if cv.is_type(CommunicationType::settings_list) { - let my_id = cv.get_sender(); - let settings = get_children(&format!("users/{}/settings/", my_id)); - let mut settings_json = JsonValue::new_array(); - for s in settings { - let s = s.replace(".settings", ""); - if s.is_empty() { - continue; - } - let _ = settings_json.push(JsonValue::String(s)); - } - let response = CommunicationValue::new(CommunicationType::settings_list) - .with_id(cv.get_id()) - .with_receiver(my_id) - .add_data(DataTypes::settings, settings_json); - - Self::send_message_static( - &writer.clone(), - is_connected, - response.to_json().to_string(), - ) - .await; - return; - } - } - Err(e) => { - log!("Omikron] Error: {}", e); - *is_connected.lock().await = false; return; } - _ => {} + + if cv.is_type(CommunicationType::message_send) { + let my_id = cv.get_sender(); + let other_id = cv + .get_data(DataTypes::receiver_id) + .unwrap_or(&JsonValue::Null) + .as_i64() + .unwrap_or(0); + let now_ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as u128; + + chat_files::add_message( + now_ms, + true, + my_id, + other_id, + &*cv.get_data(DataTypes::content).unwrap().to_string(), + ); + + let ack = CommunicationValue::new(CommunicationType::success) + .with_id(cv.get_id()) + .with_receiver(my_id); + Self::send_message_static( + &writer.clone(), + Arc::clone(&is_connected), + ack.to_json().to_string(), + ) + .await; + + let forward = CommunicationValue::new(CommunicationType::message_other_iota) + .with_id(cv.get_id()) + .with_receiver(other_id) + .add_data( + DataTypes::receiver_id, + JsonValue::Number(Number::from(other_id)), + ) + .with_sender(my_id) + .add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string())) + .add_data(DataTypes::sender_id, JsonValue::Number(Number::from(my_id))) + .add_data( + DataTypes::content, + JsonValue::String(cv.get_data(DataTypes::content).unwrap().to_string()), + ); + Self::send_message_static( + &writer.clone(), + is_connected, + forward.to_json().to_string(), + ) + .await; + return; + } + + if cv.is_type(CommunicationType::messages_get) { + let my_id = cv.get_sender(); + let partner_id = cv + .get_data(DataTypes::user_id) + .unwrap_or(&JsonValue::Null) + .as_i64() + .unwrap_or(0); + let offset = cv + .get_data(DataTypes::offset) + .unwrap_or(&JsonValue::Null) + .to_string() + .parse::() + .unwrap_or(0); + let amount = cv + .get_data(DataTypes::amount) + .unwrap_or(&JsonValue::Null) + .to_string() + .parse::() + .unwrap_or(0); + let messages = chat_files::get_messages(my_id, partner_id, offset, amount); + let resp = CommunicationValue::new(CommunicationType::messages_get) + .with_id(cv.get_id()) + .with_receiver(my_id) + .add_data(DataTypes::messages, messages); + + Self::send_message_static( + &writer.clone(), + is_connected, + resp.to_json().to_string(), + ) + .await; + return; + } + + if cv.is_type(CommunicationType::get_chats) { + let user_id = cv.get_sender(); + let users = chats_util::get_users(user_id); + let resp = CommunicationValue::new(CommunicationType::get_chats) + .with_id(cv.get_id()) + .with_receiver(user_id) + .add_data(DataTypes::user_ids, users); + Self::send_message_static( + &writer.clone(), + is_connected, + resp.to_json().to_string(), + ) + .await; + return; + } + + if cv.is_type(CommunicationType::add_conversation) { + let user_id = cv.get_sender(); + let other_id = cv + .get_data(DataTypes::chat_partner_id) + .unwrap_or(&JsonValue::Null) + .as_i64() + .unwrap_or(0); + let mut contact = get_user(user_id, other_id).unwrap_or(Contact::new(other_id)); + contact.set_last_message_at( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64, + ); + mod_user(user_id, &contact); + let resp = CommunicationValue::new(CommunicationType::add_conversation) + .with_id(cv.get_id()) + .with_receiver(user_id); + Self::send_message_static( + &writer.clone(), + is_connected, + resp.to_json().to_string(), + ) + .await; + return; + } + + if cv.is_type(CommunicationType::add_community) { + UserCommunityUtil::add_community( + cv.get_sender(), + cv.get_data(DataTypes::community_address) + .unwrap() + .to_string(), + cv.get_data(DataTypes::community_title).unwrap().to_string(), + cv.get_data(DataTypes::position).unwrap().to_string(), + ); + let resp = CommunicationValue::new(CommunicationType::add_community) + .with_id(cv.get_id()) + .with_receiver(cv.get_sender()); + Self::send_message_static( + &writer.clone(), + is_connected, + resp.to_json().to_string(), + ) + .await; + return; + } + + if cv.is_type(CommunicationType::get_communities) { + let resp = CommunicationValue::new(CommunicationType::get_communities) + .with_id(cv.get_id()) + .with_receiver(cv.get_sender()) + .add_array( + DataTypes::communities, + UserCommunityUtil::get_communities(cv.get_sender()), + ); + Self::send_message_static( + &writer.clone(), + is_connected, + resp.to_json().to_string(), + ) + .await; + return; + } + + if cv.is_type(CommunicationType::remove_community) { + UserCommunityUtil::remove_community( + cv.get_sender(), + cv.get_data(DataTypes::community_address) + .unwrap() + .to_string(), + ); // needs UserCommunityUtil + let resp = CommunicationValue::new(CommunicationType::remove_community) + .with_id(cv.get_id()) + .with_receiver(cv.get_sender()); + Self::send_message_static( + &writer.clone(), + is_connected, + resp.to_json().to_string(), + ) + .await; + return; + } + + if cv.is_type(CommunicationType::settings_save) { + let my_id = cv.get_sender(); + let settings_name = cv.get_data(DataTypes::settings_name).unwrap().to_string(); + let settings_value = cv.get_data(DataTypes::payload).unwrap().to_string(); + + save_file( + &format!("users/{}/settings/", my_id), + &format!("{}.settings", settings_name), + &settings_value, + ); + + let response = CommunicationValue::new(CommunicationType::settings_save) + .with_receiver(my_id) + .with_id(cv.get_id()); + + Self::send_message_static( + &writer.clone(), + is_connected, + response.to_json().to_string(), + ) + .await; + return; + } + if cv.is_type(CommunicationType::settings_load) { + let my_id = cv.get_sender(); + let settings_name = cv.get_data(DataTypes::settings_name).unwrap().to_string(); + let settings_value_str = load_file( + &format!("users/{}/settings/", my_id), + &format!("{}.settings", settings_name), + ); + let settings_value_json = JsonValue::from(settings_value_str); + let response = CommunicationValue::new(CommunicationType::settings_load) + .with_id(cv.get_id()) + .with_receiver(my_id) + .add_data(DataTypes::payload, settings_value_json) + .add_data_str(DataTypes::settings_name, settings_name); + + Self::send_message_static( + &writer.clone(), + is_connected, + response.to_json().to_string(), + ) + .await; + return; + } + if cv.is_type(CommunicationType::settings_list) { + let my_id = cv.get_sender(); + let settings = get_children(&format!("users/{}/settings/", my_id)); + let mut settings_json = JsonValue::new_array(); + for s in settings { + let s = s.replace(".settings", ""); + if s.is_empty() { + continue; + } + let _ = settings_json.push(JsonValue::String(s)); + } + let response = CommunicationValue::new(CommunicationType::settings_list) + .with_id(cv.get_id()) + .with_receiver(my_id) + .add_data(DataTypes::settings, settings_json); + + Self::send_message_static( + &writer.clone(), + is_connected, + response.to_json().to_string(), + ) + .await; + return; + } } - }); + Err(e) => { + log!("Omikron] Error: {}", e); + *is_connected.lock().await = false; + return; + } + _ => {} + } } pub async fn send_message_static( @@ -718,27 +698,26 @@ impl OmikronConnection { msg: String, ) { let mut guard = writer.lock().await; + if let Some(writer) = guard.as_mut() { - match writer.send(Message::Text(Utf8Bytes::from(msg))).await { - Ok(_) => match writer.flush().await { - Ok(_) => return, - Err(e) => { - log_t!("send_message_failed", e.to_string()); - *connected.lock().await = false; - } - }, - Err(e) => { - log_t!("send_message_failed", e.to_string()); - *connected.lock().await = false; - } + if let Err(e) = writer.send(Message::Text(Utf8Bytes::from(msg))).await { + log_t!("send_message_failed", e.to_string()); + *connected.lock().await = false; + return; + } + + if let Err(e) = writer.flush().await { + log_t!("send_message_failed", e.to_string()); + *connected.lock().await = false; } } else { - log_t!("send_message_failed", "Immutable Writer".to_string()); + log_t!("send_message_failed", "Writer not initialized".to_string()); *connected.lock().await = false; } } + pub async fn await_response( - self: Arc, + &self, cv: &CommunicationValue, timeout_duration: Option, ) -> Result { diff --git a/src/server/server.rs b/src/server/server.rs index 57b7462..b74facf 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -66,9 +66,9 @@ pub async fn start(port: u16) -> bool { let server_handle = server.handle(); tx.send(server_handle).unwrap(); - ACTIVE_TASKS.lock().unwrap().push("WebServer".into()); + ACTIVE_TASKS.insert("WebServer".into()); server.await.unwrap(); - ACTIVE_TASKS.lock().unwrap().retain(|t| t != "WebServer"); + ACTIVE_TASKS.remove("WebServer"); log!("Web Server shutdown complete."); }); diff --git a/src/server/web_path_parser.rs b/src/server/web_path_parser.rs index 50a4db2..a82c8db 100755 --- a/src/server/web_path_parser.rs +++ b/src/server/web_path_parser.rs @@ -1,5 +1,4 @@ -use actix_web::{HttpRequest, HttpResponse, web}; -use json::JsonValue; +use actix_web::{HttpRequest, HttpResponse}; use std::path::{Path, PathBuf}; use crate::util::file_util::load_file_vec; @@ -17,32 +16,21 @@ fn codec_for_ext(ext: &str) -> &'static str { } } -pub async fn handle(req: HttpRequest, body: web::Bytes) -> HttpResponse { - // Optional JSON parsing - let _body_json: Option = if !body.is_empty() { - json::parse(std::str::from_utf8(&body).unwrap_or("")).ok() - } else { - None - }; - +pub async fn handle(req: HttpRequest) -> HttpResponse { let req_path = req.path().trim_start_matches('/'); - // 1️⃣ Resolve the filesystem path let mut fs_path = PathBuf::from("web"); - // Boolean P: no path provided → redirect to index.html if req_path.is_empty() { fs_path.push("index.html"); } else { fs_path.extend(req_path.split('/')); } - // Boolean D: path is directory → serve index.html inside if fs_path.is_dir() { fs_path.push("index.html"); } - // Boolean E: extension provided let ext_opt = fs_path.extension().and_then(|e| e.to_str()); let mut final_name = fs_path .file_name() @@ -51,7 +39,6 @@ pub async fn handle(req: HttpRequest, body: web::Bytes) -> HttpResponse { .to_string(); if ext_opt.is_none() { - // No extension provided → try HTML if final_name.is_empty() { final_name = "index.html".to_string(); } else { @@ -68,12 +55,10 @@ pub async fn handle(req: HttpRequest, body: web::Bytes) -> HttpResponse { let dir = fs_path.parent().unwrap_or(Path::new("web")); - // 2️⃣ Try to load the resolved file match load_file_vec(dir.to_str().unwrap_or("web"), &final_name) { Ok(content) => HttpResponse::Ok().content_type(content_type).body(content), Err(_) => { - // For static assets, return plain 404 let ext = fs_path.extension().and_then(|e| e.to_str()).unwrap_or(""); if matches!(ext, "js" | "css" | "woff2") { return HttpResponse::NotFound() @@ -81,7 +66,6 @@ pub async fn handle(req: HttpRequest, body: web::Bytes) -> HttpResponse { .body("Not found"); } - // 3️⃣ Try to serve 404.html from web folder let fallback = load_file_vec("web", "404.html") .unwrap_or_else(|_| include_bytes!("../../static/web/404.html").to_vec()); diff --git a/src/users/user_manager.rs b/src/users/user_manager.rs index e42d428..95da132 100644 --- a/src/users/user_manager.rs +++ b/src/users/user_manager.rs @@ -1,10 +1,11 @@ use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; -use crate::log; use crate::omikron::omikron_connection::{OMIKRON_CONNECTION, OmikronConnection}; use crate::users::user_profile::UserProfile; use crate::util::crypto_helper::{self, public_key_to_base64}; use crate::util::file_util::{load_file, save_file}; +use crate::util::logger::PrintType; use crate::{RELOAD, SHUTDOWN}; +use crate::{log, log_cv}; use base64::{Engine as _, engine::general_purpose::STANDARD}; use hex::{self}; use json::JsonValue; @@ -47,26 +48,37 @@ pub async fn load_from_tu(username: &str) -> Result<(), ()> { USERS.lock().unwrap().push(user_profile); Ok(()) } + pub async fn create_user(username: &str) -> (Option, Option) { - let omikron_con: Arc = - OMIKRON_CONNECTION.read().await.as_ref().unwrap().clone(); - let register_cv = if let Ok(register_cv) = omikron_con - .clone() - .await_response( - &CommunicationValue::new(CommunicationType::get_register), - Some(Duration::from_secs(20)), - ) + let register_cv = CommunicationValue::new(CommunicationType::get_register); + + let conn = { + let guard = OMIKRON_CONNECTION.read().await; + guard.as_ref().cloned() + }; + + let conn = match conn { + Some(c) => c, + None => return (None, None), + }; + + let response_cv = match conn + .await_response(®ister_cv, Some(Duration::from_secs(20))) .await { - register_cv - } else { - return (None, None); + Ok(cv) => cv, + Err(_) => return (None, None), }; - let user_id = register_cv - .get_data(DataTypes::register_id) + log_cv!(PrintType::Omega, response_cv); + + let user_id = match response_cv + .get_data(DataTypes::user_id) .unwrap_or(&JsonValue::Null) .as_i64() - .unwrap_or(0); + { + Some(id) => id, + None => return (None, None), + }; let mut buf = [0u8; 56]; let mut rng = OsRng; rng.fill_bytes(&mut buf); @@ -101,10 +113,22 @@ pub async fn create_user(username: &str) -> (Option, Option .add_data(DataTypes::iota_id, JsonValue::Number(Number::from(user_id))) .add_data(DataTypes::reset_token, JsonValue::String(reset_token)); - let response_cv = omikron_con + let conn = { + let guard = OMIKRON_CONNECTION.read().await; + guard.as_ref().cloned() + }; + + let conn = match conn { + Some(c) => c, + None => return (None, None), + }; + + let response_cv = conn .await_response(&cv, Some(Duration::from_secs(20))) .await; + if let Ok(resp) = response_cv { + log_cv!(PrintType::Omega, resp); if !resp.is_type(CommunicationType::success) { return (None, None); } @@ -125,6 +149,15 @@ pub async fn create_user(username: &str) -> (Option, Option (Some(up), Some(STANDARD.encode(&private_key.as_bytes()))) } +pub fn get_user_by_username(username: &str) -> Option { + USERS + .lock() + .unwrap() + .iter() + .cloned() + .find(|u| u.username == username) +} + pub fn get_user(user_id: i64) -> Option { USERS .lock() diff --git a/src/util/file_util.rs b/src/util/file_util.rs index f51c863..2d00718 100755 --- a/src/util/file_util.rs +++ b/src/util/file_util.rs @@ -34,7 +34,7 @@ fn delete_dir_recursive(directory: &Path) -> bool { } #[allow(dead_code)] -pub fn delete_user_directory(user_id: Uuid) { +pub fn delete_user_directory(user_id: i64) { let user_dir = Path::new(&get_directory()) .join("users") .join(user_id.to_string()); @@ -189,7 +189,7 @@ pub fn get_directory_size(directory: &Path) -> u64 { } #[allow(dead_code)] -pub fn get_designed_storage(user_id: Uuid) -> String { +pub fn get_designed_storage(user_id: i64) -> String { let user_dir = Path::new(&get_directory()) .join("users") .join(user_id.to_string()); diff --git a/src/util/logger.rs b/src/util/logger.rs index 40a2952..700f4d2 100644 --- a/src/util/logger.rs +++ b/src/util/logger.rs @@ -30,7 +30,7 @@ pub enum PrintType { struct LogMessage { timestamp_ms: u128, - prefix: &'static str, + prefix: String, kind: PrintType, is_error: bool, @@ -116,7 +116,7 @@ fn fixed_box(content: &str, width: usize) -> String { pub fn log_internal_translated( kind: PrintType, - prefix: &'static str, + prefix: String, is_error: bool, key: &str, args: Vec, @@ -140,7 +140,7 @@ pub fn log_internal_translated( } } -pub fn log_internal(kind: PrintType, prefix: &'static str, is_error: bool, message: String) { +pub fn log_internal(kind: PrintType, prefix: String, is_error: bool, message: String) { if let Some(tx) = LOGGER.get() { tokio::spawn(async move { *UNIQUE.write().await = true; @@ -162,12 +162,12 @@ pub fn log_internal(kind: PrintType, prefix: &'static str, is_error: bool, messa use crate::data::communication::CommunicationValue; use json::JsonValue; -pub fn log_cv_internal(cv: &CommunicationValue, print_type: Option) { +pub fn log_cv_internal(prefix: String, cv: &CommunicationValue, print_type: Option) { let formatted = format_cv(cv); log_internal( print_type.unwrap_or(PrintType::General), - "", + prefix, false, formatted, ); @@ -211,10 +211,28 @@ pub fn format_cv(cv: &CommunicationValue) -> String { #[macro_export] macro_rules! log_cv { ($kind:expr, $cv:expr) => { - $crate::util::logger::log_cv_internal(&$cv, Some($kind)) + $crate::util::logger::log_cv_internal("".to_string(), &$cv, Some($kind)) }; ($cv:expr) => { - $crate::util::logger::log_cv_internal(&$cv, None) + $crate::util::logger::log_cv_internal("".to_string(), &$cv, None) + }; +} +#[macro_export] +macro_rules! log_cv_in { + ($kind:expr, $cv:expr) => { + $crate::util::logger::log_cv_internal("> ".to_string(), &$cv, Some($kind)) + }; + ($cv:expr) => { + $crate::util::logger::log_cv_internal("> ".to_string(), &$cv, None) + }; +} +#[macro_export] +macro_rules! log_cv_out { + ($kind:expr, $cv:expr) => { + $crate::util::logger::log_cv_internal("< ".to_string(), &$cv, Some($kind)) + }; + ($cv:expr) => { + $crate::util::logger::log_cv_internal("< ".to_string(), &$cv, None) }; } @@ -223,7 +241,7 @@ macro_rules! log_t { ($key:expr) => { $crate::util::logger::log_internal_translated( $crate::util::logger::PrintType::General, - "", + "".to_string(), false, $key, vec![] @@ -233,7 +251,7 @@ macro_rules! log_t { ($key:expr, $($arg:expr),+) => { $crate::util::logger::log_internal_translated( $crate::util::logger::PrintType::General, - "", + "".to_string(), false, $key, vec![$($arg),+] @@ -245,7 +263,7 @@ macro_rules! log_t_err { ($key:expr) => { $crate::util::logger::log_internal_translated( $crate::util::logger::PrintType::General, - ">>", + "".to_string(), true, $key, vec![] @@ -255,7 +273,7 @@ macro_rules! log_t_err { ($key:expr, $($arg:expr),+) => { $crate::util::logger::log_internal_translated( $crate::util::logger::PrintType::General, - ">>", + "".to_string(), true, $key, vec![$($arg.to_string()),+] @@ -267,7 +285,7 @@ macro_rules! log_t_err { #[macro_export] macro_rules! log { ($($arg:tt)*) => { - $crate::util::logger::log_internal($crate::util::logger::PrintType::General, "", false, format!($($arg)*)) + $crate::util::logger::log_internal($crate::util::logger::PrintType::General, "".to_string(), false, format!($($arg)*)) }; } /// Log an inbound message (`>`). @@ -276,7 +294,7 @@ macro_rules! log_in { ($($arg:tt)*) => { $crate::util::logger::log_internal( $crate::util::logger::PrintType::General, - ">", + ">".to_string(), false, format!($($arg)*) ) @@ -288,7 +306,7 @@ macro_rules! log_out { ($($arg:tt)*) => { $crate::util::logger::log_internal( $crate::util::logger::PrintType::General, - "<", + "<".to_string(), false, format!($($arg)*) ) @@ -300,7 +318,7 @@ macro_rules! log_err { ($($arg:tt)*) => { $crate::util::logger::log_internal( $crate::util::logger::PrintType::General, - ">>", + ">>".to_string(), true, format!($($arg)*) )