diff --git a/src/app.rs b/src/app.rs index 2045854..577debc 100644 --- a/src/app.rs +++ b/src/app.rs @@ -17,7 +17,6 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::RwLock; use color_eyre::eyre::{eyre, Result, WrapErr}; use crossterm::event::KeyEvent; @@ -56,7 +55,7 @@ impl App { pub async fn new( tick_rate: f64, frame_rate: f64, - mem_log: &'static RwLock>, + log_receiver: mpsc::UnboundedReceiver, ) -> Result { let home = Home::new(); let fps = FpsCounter::default(); @@ -132,7 +131,7 @@ impl App { components: vec![Box::new(home), Box::new(fps)], should_quit: AtomicBool::new(false), should_suspend: false, - buffers: Buffers::new(Box::new(LogBuffer::new(mem_log))), + buffers: Buffers::new(Box::new(LogBuffer::new(log_receiver))), clients, config, commands: RataCommands::new(), diff --git a/src/buffers/log.rs b/src/buffers/log.rs index 63ab0b3..7a9fbfd 100644 --- a/src/buffers/log.rs +++ b/src/buffers/log.rs @@ -18,34 +18,52 @@ use std::collections::VecDeque; use std::sync::Arc; use std::sync::RwLock; +use matrix_sdk::async_trait; use ratatui::text::Text; +use tokio::sync::mpsc::UnboundedReceiver; use tracing_error::ErrorLayer; use tracing_subscriber::prelude::*; use super::Buffer; +/// Maximum number of log lines to be stored in memory +const MAX_MEM_LOG_LINES: usize = 100; + pub struct LogBuffer { - lines: &'static RwLock>, + lines: VecDeque, + receiver: UnboundedReceiver, } impl LogBuffer { - pub fn new(lines: &'static RwLock>) -> Self { - LogBuffer { lines } + pub fn new(receiver: UnboundedReceiver) -> Self { + LogBuffer { + lines: VecDeque::new(), + receiver, + } } } +#[async_trait] impl Buffer for LogBuffer { fn short_name(&self) -> String { "ratatrix".to_owned() } + async fn poll_updates(&mut self) { + let line = self + .receiver + .recv() + .await + .expect("LogBuffer's channel was closed"); + if self.lines.len() >= MAX_MEM_LOG_LINES { + self.lines.pop_front(); + } + self.lines.push_back(line); + } + fn content(&self) -> Vec { use ansi_to_tui::IntoText; - let lines = self - .lines - .read() - .expect("LogBuffer could not get log's RwLock as it is poisoned"); - let (slice1, slice2) = lines.as_slices(); + let (slice1, slice2) = self.lines.as_slices(); slice1 .into_iter() .chain(slice2.into_iter()) diff --git a/src/buffers/mod.rs b/src/buffers/mod.rs index b84d3a0..92b90a8 100644 --- a/src/buffers/mod.rs +++ b/src/buffers/mod.rs @@ -29,9 +29,7 @@ pub trait Buffer: Send + Sync { /// A short human-readable name for the room, eg. to show in compact buflist fn short_name(&self) -> String; /// Returns if there are any updates to apply. - async fn poll_updates(&mut self) { - std::future::pending().await - } + async fn poll_updates(&mut self); fn content(&self) -> Vec; // TODO: make this lazy, only the last few are used /// Called when the user is being showned the oldest items this buffer returned. /// diff --git a/src/log.rs b/src/log.rs index 8835e2d..dc22359 100644 --- a/src/log.rs +++ b/src/log.rs @@ -18,23 +18,21 @@ use std::collections::VecDeque; use std::sync::RwLock; use color_eyre::eyre::Result; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tracing_error::ErrorLayer; use tracing_subscriber::prelude::*; use crate::utils::{get_data_dir, LOG_ENV, LOG_FILE}; -/// Maximum number of log lines to be stored in memory -const MAX_MEM_LOG_LINES: usize = 100; - pub struct LogWriter { - lines: &'static RwLock>, + sender: UnboundedSender, last_line: Vec, } impl LogWriter { - fn new(lines: &'static RwLock>) -> Self { + fn new(sender: UnboundedSender) -> Self { LogWriter { - lines, + sender, last_line: Vec::new(), } } @@ -55,20 +53,12 @@ impl std::io::Write for LogWriter { // No new line, nothing to do } else { let last_line = new_lines.pop().expect("Split returned empty vec").to_vec(); - let mut lines = self.lines.write().map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "LogWriter could not get log's RwLock as it is poisoned: {}", - e - ), - ) - })?; for new_line in new_lines.into_iter() { - if lines.len() >= MAX_MEM_LOG_LINES { - lines.pop_front(); - } - lines.push_back(String::from_utf8_lossy(new_line).to_string()); + let new_line = String::from_utf8_lossy(new_line).to_string(); + self + .sender + .send(new_line) + .unwrap_or_else(|e| panic!("Could not push log line: {:?}", e)); } self.last_line.clear(); self.last_line.extend(&last_line[..]); @@ -78,7 +68,7 @@ impl std::io::Write for LogWriter { } } -pub fn initialize_logging() -> Result<&'static RwLock>> { +pub fn initialize_logging() -> Result> { let directory = get_data_dir(); std::fs::create_dir_all(directory.clone())?; let log_path = directory.join(LOG_FILE.clone()); @@ -98,15 +88,13 @@ pub fn initialize_logging() -> Result<&'static RwLock>> { .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env()); // We keep the log until the application stops, so we might as well return it as &'static - let lines = Box::leak(Box::new(RwLock::new(VecDeque::with_capacity( - MAX_MEM_LOG_LINES, - )))); + let (sender, receiver) = unbounded_channel(); let mem_subscriber = tracing_subscriber::fmt::layer() .with_ansi(true) .with_file(true) .with_line_number(true) .with_target(false) - .with_writer(|| LogWriter::new(lines)) + .with_writer(move || LogWriter::new(sender.clone())) .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env()); tracing_subscriber::registry() @@ -114,5 +102,5 @@ pub fn initialize_logging() -> Result<&'static RwLock>> { .with(file_subscriber) .with(ErrorLayer::default()) .init(); - Ok(lines) + Ok(receiver) }