diff --git a/Cargo.lock b/Cargo.lock index 4a4d1dc..3290691 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,7 @@ dependencies = [ "directories", "edit", "futures", + "log", "parking_lot", "rand", "rusqlite", diff --git a/Cargo.toml b/Cargo.toml index 7e4fbe4..6ec330c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ crossterm = "0.23.2" directories = "4.0.1" edit = "0.1.4" futures = "0.3.21" +log = "0.4.17" parking_lot = "0.12.1" rand = "0.8.5" rusqlite = { version = "0.27.0", features = ["chrono"] } diff --git a/src/log.rs b/src/logger.rs similarity index 61% rename from src/log.rs rename to src/logger.rs index 7b7da8d..a6e0220 100644 --- a/src/log.rs +++ b/src/logger.rs @@ -3,6 +3,7 @@ use std::vec; use async_trait::async_trait; use chrono::{DateTime, Utc}; +use log::{Level, Log}; use parking_lot::Mutex; use crate::store::{Msg, MsgStore, Path, Tree}; @@ -11,7 +12,7 @@ use crate::store::{Msg, MsgStore, Path, Tree}; pub struct LogMsg { id: usize, time: DateTime, - topic: String, + level: Level, content: String, } @@ -31,7 +32,7 @@ impl Msg for LogMsg { } fn nick(&self) -> String { - self.topic.clone() + format!("{}", self.level) } fn content(&self) -> String { @@ -40,19 +41,17 @@ impl Msg for LogMsg { } #[derive(Debug, Clone)] -pub struct Log { - entries: Arc>>, -} +pub struct Logger(Arc>>); #[async_trait] -impl MsgStore for Log { +impl MsgStore for Logger { async fn path(&self, id: &usize) -> Path { Path::new(vec![*id]) } async fn tree(&self, root: &usize) -> Tree { let msgs = self - .entries + .0 .lock() .get(*root) .map(|msg| vec![msg.clone()]) @@ -65,35 +64,50 @@ impl MsgStore for Log { } async fn next_tree(&self, tree: &usize) -> Option { - let len = self.entries.lock().len(); + let len = self.0.lock().len(); tree.checked_add(1).filter(|t| *t < len) } async fn first_tree(&self) -> Option { - let empty = self.entries.lock().is_empty(); + let empty = self.0.lock().is_empty(); Some(0).filter(|_| !empty) } async fn last_tree(&self) -> Option { - self.entries.lock().len().checked_sub(1) + self.0.lock().len().checked_sub(1) } } -impl Log { - pub fn new() -> Self { - Self { - entries: Arc::new(Mutex::new(Vec::new())), - } +impl Log for Logger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + true } - pub fn log(&self, topic: S1, content: S2) { - let mut guard = self.entries.lock(); + fn log(&self, record: &log::Record) { + if !self.enabled(record.metadata()) { + return; + } + + let mut guard = self.0.lock(); let msg = LogMsg { id: guard.len(), time: Utc::now(), - topic: topic.to_string(), - content: content.to_string(), + level: record.level(), + content: format!("<{}> {}", record.target(), record.args()), }; guard.push(msg); } + + fn flush(&self) {} +} + +impl Logger { + pub fn init(level: Level) -> &'static Self { + let logger = Box::leak(Box::new(Self(Arc::new(Mutex::new(Vec::new()))))); + + log::set_logger(logger).expect("logger already set"); + log::set_max_level(level.to_level_filter()); + + logger + } } diff --git a/src/main.rs b/src/main.rs index dc92e6c..27bd993 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,18 +2,28 @@ mod chat; mod euph; -mod log; +mod logger; mod replies; mod store; mod ui; mod vault; use directories::ProjectDirs; +use log::info; use toss::terminal::Terminal; use ui::Ui; +use crate::logger::Logger; + #[tokio::main] async fn main() -> anyhow::Result<()> { + let logger = Logger::init(log::Level::Debug); + info!( + "Welcome to {} {}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + ); + let dirs = ProjectDirs::from("de", "plugh", "cove").expect("unable to determine directories"); println!("Data dir: {}", dirs.data_dir().to_string_lossy()); @@ -21,7 +31,7 @@ async fn main() -> anyhow::Result<()> { let mut terminal = Terminal::new()?; // terminal.set_measuring(true); - Ui::run(&mut terminal).await?; + Ui::run(&mut terminal, logger.clone()).await?; drop(terminal); // So the vault can print again vault.close().await; diff --git a/src/ui.rs b/src/ui.rs index 8b18194..db6c5a3 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -2,6 +2,7 @@ use std::sync::{Arc, Weak}; use std::time::Duration; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent}; +use log::debug; use parking_lot::FairMutex; use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; @@ -10,7 +11,7 @@ use toss::frame::{Frame, Pos, Size}; use toss::terminal::Terminal; use crate::chat::Chat; -use crate::log::{Log, LogMsg}; +use crate::logger::{LogMsg, Logger}; use crate::store::dummy::{DummyMsg, DummyStore}; #[derive(Debug)] @@ -31,20 +32,16 @@ enum Visible { pub struct Ui { event_tx: UnboundedSender, - log: Log, visible: Visible, chat: Chat, - log_chat: Chat, + log_chat: Chat, } impl Ui { const POLL_DURATION: Duration = Duration::from_millis(100); - pub async fn run(terminal: &mut Terminal) -> anyhow::Result<()> { - let log = Log::new(); - log.log("Hello", "world!"); - + pub async fn run(terminal: &mut Terminal, logger: Logger) -> anyhow::Result<()> { let (event_tx, event_rx) = mpsc::unbounded_channel(); let crossterm_lock = Arc::new(FairMutex::new(())); @@ -54,7 +51,6 @@ impl Ui { let crossterm_event_task = task::spawn_blocking(|| { Self::poll_crossterm_events(event_tx_clone, weak_crossterm_lock) }); - log.log("main", "Started input polling task"); // Prepare dummy message store and chat for testing let store = DummyStore::new() @@ -82,10 +78,9 @@ impl Ui { // the rest of the UI is also shut down and the client stops. let mut ui = Self { event_tx, - log: log.clone(), visible: Visible::Log, chat, - log_chat: Chat::new(log), + log_chat: Chat::new(logger), }; let result = tokio::select! { e = ui.run_main(terminal, event_rx, crossterm_lock) => e, @@ -184,7 +179,7 @@ impl Ui { } match event.code { - KeyCode::Char('e') => self.log.log("EE E", "E ee e!"), + KeyCode::Char('e') => debug!("{:#?}", event), KeyCode::F(1) => self.visible = Visible::Main, KeyCode::F(2) => self.visible = Visible::Log, _ => {}