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