Add simple logging

This commit is contained in:
Joscha 2022-06-18 16:55:00 +02:00
parent 27769d38d2
commit 416b5bf31e
3 changed files with 156 additions and 7 deletions

99
cove-tui/src/log.rs Normal file
View file

@ -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<Utc>,
topic: String,
content: String,
}
impl Msg for LogMsg {
type Id = usize;
fn id(&self) -> Self::Id {
self.id
}
fn parent(&self) -> Option<Self::Id> {
None
}
fn time(&self) -> DateTime<Utc> {
self.time
}
fn nick(&self) -> String {
self.topic.clone()
}
fn content(&self) -> String {
self.content.clone()
}
}
#[derive(Debug, Clone)]
pub struct Log {
entries: Arc<Mutex<Vec<LogMsg>>>,
}
#[async_trait]
impl MsgStore<LogMsg> for Log {
async fn path(&self, id: &usize) -> Path<usize> {
Path::new(vec![*id])
}
async fn tree(&self, root: &usize) -> Tree<LogMsg> {
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<usize> {
tree.checked_sub(1)
}
async fn next_tree(&self, tree: &usize) -> Option<usize> {
let len = self.entries.lock().len();
tree.checked_add(1).filter(|t| *t < len)
}
async fn first_tree(&self) -> Option<usize> {
let empty = self.entries.lock().is_empty();
Some(0).filter(|_| !empty)
}
async fn last_tree(&self) -> Option<usize> {
self.entries.lock().len().checked_sub(1)
}
}
impl Log {
pub fn new() -> Self {
Self {
entries: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn log<S1: ToString, S2: ToString>(&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);
}
}

View file

@ -1,6 +1,7 @@
#![warn(clippy::use_self)] #![warn(clippy::use_self)]
mod chat; mod chat;
mod log;
mod store; mod store;
mod ui; mod ui;
mod vault; mod vault;

View file

@ -10,6 +10,7 @@ use toss::frame::{Frame, Pos, Size};
use toss::terminal::Terminal; use toss::terminal::Terminal;
use crate::chat::Chat; use crate::chat::Chat;
use crate::log::{Log, LogMsg};
use crate::store::dummy::{DummyMsg, DummyStore}; use crate::store::dummy::{DummyMsg, DummyStore};
#[derive(Debug)] #[derive(Debug)]
@ -23,15 +24,27 @@ enum EventHandleResult {
Stop, Stop,
} }
enum Visible {
Main,
Log,
}
pub struct Ui { pub struct Ui {
event_tx: UnboundedSender<UiEvent>, event_tx: UnboundedSender<UiEvent>,
log: Log,
visible: Visible,
chat: Chat<DummyMsg, DummyStore>, chat: Chat<DummyMsg, DummyStore>,
log_chat: Chat<LogMsg, Log>,
} }
impl Ui { impl Ui {
const POLL_DURATION: Duration = Duration::from_millis(100); const POLL_DURATION: Duration = Duration::from_millis(100);
pub async fn run(terminal: &mut Terminal) -> anyhow::Result<()> { 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 (event_tx, event_rx) = mpsc::unbounded_channel();
let crossterm_lock = Arc::new(FairMutex::new(())); let crossterm_lock = Arc::new(FairMutex::new(()));
@ -41,6 +54,7 @@ impl Ui {
let crossterm_event_task = task::spawn_blocking(|| { let crossterm_event_task = task::spawn_blocking(|| {
Self::poll_crossterm_events(event_tx_clone, weak_crossterm_lock) 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 // Prepare dummy message store and chat for testing
let store = DummyStore::new() let store = DummyStore::new()
@ -66,7 +80,13 @@ impl Ui {
// //
// On the other hand, if the crossterm_event_task stops for any reason, // 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. // 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! { let result = tokio::select! {
e = ui.run_main(terminal, event_rx, crossterm_lock) => e, e = ui.run_main(terminal, event_rx, crossterm_lock) => e,
Ok(e) = crossterm_event_task => e, Ok(e) = crossterm_event_task => e,
@ -112,6 +132,7 @@ impl Ui {
Some(event) => event, Some(event) => event,
None => return Ok(()), None => return Ok(()),
}; };
terminal.autoresize()?;
loop { loop {
let size = terminal.frame().size(); let size = terminal.frame().size();
let result = match event { let result = match event {
@ -137,7 +158,14 @@ impl Ui {
} }
async fn render(&mut self, frame: &mut Frame) -> anyhow::Result<()> { 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(()) Ok(())
} }
@ -148,19 +176,40 @@ impl Ui {
size: Size, size: Size,
crossterm_lock: &Arc<FairMutex<()>>, crossterm_lock: &Arc<FairMutex<()>>,
) -> EventHandleResult { ) -> EventHandleResult {
// Always exit when shift+q or ctrl+c are pressed
let shift_q = event.code == KeyCode::Char('Q'); let shift_q = event.code == KeyCode::Char('Q');
let ctrl_c = event.modifiers == KeyModifiers::CONTROL && event.code == KeyCode::Char('c'); let ctrl_c = event.modifiers == KeyModifiers::CONTROL && event.code == KeyCode::Char('c');
if shift_q || ctrl_c { if shift_q || ctrl_c {
return EventHandleResult::Stop; return EventHandleResult::Stop;
} }
// TODO Perform resulting action
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 self.chat
.handle_key_event(event, terminal, size, crossterm_lock) .handle_key_event(event, terminal, size, crossterm_lock)
.await; .await;
}
Visible::Log => {
self.log_chat
.handle_key_event(event, terminal, size, crossterm_lock)
.await;
}
}
EventHandleResult::Continue EventHandleResult::Continue
} }
async fn handle_mouse_event(&mut self, event: MouseEvent) -> anyhow::Result<EventHandleResult> { async fn handle_mouse_event(
&mut self,
_event: MouseEvent,
) -> anyhow::Result<EventHandleResult> {
Ok(EventHandleResult::Continue) Ok(EventHandleResult::Continue)
} }
} }