From 416b5bf31e35595ff4019aefa84be844fda260a7 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 18 Jun 2022 16:55:00 +0200 Subject: [PATCH] Add simple logging --- cove-tui/src/log.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++ cove-tui/src/main.rs | 1 + cove-tui/src/ui.rs | 63 ++++++++++++++++++++++++---- 3 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 cove-tui/src/log.rs diff --git a/cove-tui/src/log.rs b/cove-tui/src/log.rs new file mode 100644 index 0000000..7b7da8d --- /dev/null +++ b/cove-tui/src/log.rs @@ -0,0 +1,99 @@ +use std::sync::Arc; +use std::vec; + +use async_trait::async_trait; +use chrono::{DateTime, Utc}; +use parking_lot::Mutex; + +use crate::store::{Msg, MsgStore, Path, Tree}; + +#[derive(Debug, Clone)] +pub struct LogMsg { + id: usize, + time: DateTime, + topic: String, + content: String, +} + +impl Msg for LogMsg { + type Id = usize; + + fn id(&self) -> Self::Id { + self.id + } + + fn parent(&self) -> Option { + None + } + + fn time(&self) -> DateTime { + self.time + } + + fn nick(&self) -> String { + self.topic.clone() + } + + fn content(&self) -> String { + self.content.clone() + } +} + +#[derive(Debug, Clone)] +pub struct Log { + entries: Arc>>, +} + +#[async_trait] +impl MsgStore for Log { + async fn path(&self, id: &usize) -> Path { + Path::new(vec![*id]) + } + + async fn tree(&self, root: &usize) -> Tree { + let msgs = self + .entries + .lock() + .get(*root) + .map(|msg| vec![msg.clone()]) + .unwrap_or_default(); + Tree::new(*root, msgs) + } + + async fn prev_tree(&self, tree: &usize) -> Option { + tree.checked_sub(1) + } + + async fn next_tree(&self, tree: &usize) -> Option { + let len = self.entries.lock().len(); + tree.checked_add(1).filter(|t| *t < len) + } + + async fn first_tree(&self) -> Option { + let empty = self.entries.lock().is_empty(); + Some(0).filter(|_| !empty) + } + + async fn last_tree(&self) -> Option { + self.entries.lock().len().checked_sub(1) + } +} + +impl Log { + pub fn new() -> Self { + Self { + entries: Arc::new(Mutex::new(Vec::new())), + } + } + + pub fn log(&self, topic: S1, content: S2) { + let mut guard = self.entries.lock(); + let msg = LogMsg { + id: guard.len(), + time: Utc::now(), + topic: topic.to_string(), + content: content.to_string(), + }; + guard.push(msg); + } +} diff --git a/cove-tui/src/main.rs b/cove-tui/src/main.rs index b1b7d93..5f75135 100644 --- a/cove-tui/src/main.rs +++ b/cove-tui/src/main.rs @@ -1,6 +1,7 @@ #![warn(clippy::use_self)] mod chat; +mod log; mod store; mod ui; mod vault; diff --git a/cove-tui/src/ui.rs b/cove-tui/src/ui.rs index 0184609..6824af6 100644 --- a/cove-tui/src/ui.rs +++ b/cove-tui/src/ui.rs @@ -10,6 +10,7 @@ use toss::frame::{Frame, Pos, Size}; use toss::terminal::Terminal; use crate::chat::Chat; +use crate::log::{Log, LogMsg}; use crate::store::dummy::{DummyMsg, DummyStore}; #[derive(Debug)] @@ -23,15 +24,27 @@ enum EventHandleResult { Stop, } +enum Visible { + Main, + Log, +} + pub struct Ui { event_tx: UnboundedSender, + log: Log, + + visible: Visible, 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!"); + let (event_tx, event_rx) = mpsc::unbounded_channel(); let crossterm_lock = Arc::new(FairMutex::new(())); @@ -41,6 +54,7 @@ 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() @@ -66,7 +80,13 @@ impl Ui { // // On the other hand, if the crossterm_event_task stops for any reason, // the rest of the UI is also shut down and the client stops. - let mut ui = Self { event_tx, chat }; + let mut ui = Self { + event_tx, + log: log.clone(), + visible: Visible::Log, + chat, + log_chat: Chat::new(log), + }; let result = tokio::select! { e = ui.run_main(terminal, event_rx, crossterm_lock) => e, Ok(e) = crossterm_event_task => e, @@ -112,6 +132,7 @@ impl Ui { Some(event) => event, None => return Ok(()), }; + terminal.autoresize()?; loop { let size = terminal.frame().size(); let result = match event { @@ -137,7 +158,14 @@ impl Ui { } async fn render(&mut self, frame: &mut Frame) -> anyhow::Result<()> { - self.chat.render(frame, Pos::new(0, 0), frame.size()).await; + match self.visible { + Visible::Main => self.chat.render(frame, Pos::new(0, 0), frame.size()).await, + Visible::Log => { + self.log_chat + .render(frame, Pos::new(0, 0), frame.size()) + .await + } + } Ok(()) } @@ -148,19 +176,40 @@ impl Ui { size: Size, crossterm_lock: &Arc>, ) -> EventHandleResult { + // Always exit when shift+q or ctrl+c are pressed let shift_q = event.code == KeyCode::Char('Q'); let ctrl_c = event.modifiers == KeyModifiers::CONTROL && event.code == KeyCode::Char('c'); if shift_q || ctrl_c { return EventHandleResult::Stop; } - // TODO Perform resulting action - self.chat - .handle_key_event(event, terminal, size, crossterm_lock) - .await; + + match event.code { + KeyCode::Char('e') => self.log.log("EE E", "E ee e!"), + KeyCode::F(1) => self.visible = Visible::Main, + KeyCode::F(2) => self.visible = Visible::Log, + _ => {} + } + + match self.visible { + Visible::Main => { + self.chat + .handle_key_event(event, terminal, size, crossterm_lock) + .await; + } + Visible::Log => { + self.log_chat + .handle_key_event(event, terminal, size, crossterm_lock) + .await; + } + } + EventHandleResult::Continue } - async fn handle_mouse_event(&mut self, event: MouseEvent) -> anyhow::Result { + async fn handle_mouse_event( + &mut self, + _event: MouseEvent, + ) -> anyhow::Result { Ok(EventHandleResult::Continue) } }