Skip to content

Commit

Permalink
Add RawMessage support to the line renderer.
Browse files Browse the repository at this point in the history
  • Loading branch information
stuhood committed Nov 2, 2021
1 parent a1054e8 commit 1b9ed14
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 59 deletions.
41 changes: 28 additions & 13 deletions src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,31 @@ pub struct Message {
pub message: String,
}

impl Message {
/// Create a new Message at the current time, with the given arguments.
pub fn new(level: MessageLevel, origin: String, message: impl Into<String>) -> Self {
Message {
time: SystemTime::now(),
level,
origin,
message: message.into(),
}
}
}

/// A container for multiple message types.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Envelope {
/// A high level string message and its metadata.
Message(Message),
/// A single-line, raw message, with no metadata.
RawMessage(String),
}

/// A ring buffer for messages.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MessageRingBuffer {
pub(crate) buf: Vec<Message>,
pub(crate) buf: Vec<Envelope>,
cursor: usize,
total: usize,
}
Expand All @@ -44,25 +65,19 @@ impl MessageRingBuffer {
}
}

/// Push a `message` from `origin` at severity `level` into the buffer, possibly overwriting the last message added.
pub fn push_overwrite(&mut self, level: MessageLevel, origin: String, message: impl Into<String>) {
let msg = Message {
time: SystemTime::now(),
level,
origin,
message: message.into(),
};
/// Push a `message` into the buffer, possibly overwriting the last message added.
pub fn push_overwrite(&mut self, message: Envelope) {
if self.has_capacity() {
self.buf.push(msg)
self.buf.push(message)
} else {
self.buf[self.cursor] = msg;
self.buf[self.cursor] = message;
self.cursor = (self.cursor + 1) % self.buf.len();
}
self.total = self.total.wrapping_add(1);
}

/// Copy all messages currently contained in the buffer to `out`.
pub fn copy_all(&self, out: &mut Vec<Message>) {
pub fn copy_all(&self, out: &mut Vec<Envelope>) {
out.clear();
if self.buf.is_empty() {
return;
Expand All @@ -75,7 +90,7 @@ impl MessageRingBuffer {

/// Copy all new messages into `out` that where received since the last time this method was called provided
/// its `previous` return value.
pub fn copy_new(&self, out: &mut Vec<Message>, previous: Option<MessageCopyState>) -> MessageCopyState {
pub fn copy_new(&self, out: &mut Vec<Envelope>, previous: Option<MessageCopyState>) -> MessageCopyState {
out.clear();
match previous {
Some(MessageCopyState { cursor, buf_len, total }) => {
Expand Down
78 changes: 44 additions & 34 deletions src/render/line/draw.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
messages::{Message, MessageCopyState, MessageLevel},
messages::{Envelope, Message, MessageCopyState, MessageLevel},
progress::{self, Value},
unit, Root, Throughput,
};
Expand All @@ -13,7 +13,7 @@ use unicode_width::UnicodeWidthStr;
#[derive(Default)]
pub struct State {
tree: Vec<(progress::Key, progress::Task)>,
messages: Vec<Message>,
messages: Vec<Envelope>,
for_next_copy: Option<MessageCopyState>,
/// The size of the message origin, tracking the terminal height so things potentially off screen don't influence width anymore.
message_origin_size: VecDeque<usize>,
Expand Down Expand Up @@ -52,42 +52,52 @@ fn messages(
}
let mut tokens: Vec<ANSIString<'_>> = Vec::with_capacity(6);
let mut current_maximum = state.message_origin_size.iter().max().cloned().unwrap_or(0);
for Message {
time,
level,
origin,
message,
} in &state.messages
{
for envelope in &state.messages {
tokens.clear();
let blocks_drawn_during_previous_tick = state.blocks_per_line.pop_front().unwrap_or(0);
let message_block_len = origin.width();
current_maximum = current_maximum.max(message_block_len);
if state.message_origin_size.len() == max_height {
state.message_origin_size.pop_front();
}
state.message_origin_size.push_back(message_block_len);

let color = to_color(*level);
tokens.push(" ".into());
if timestamp {
tokens.push(
brush
.style(color.dimmed().on(Color::Yellow))
.paint(crate::time::format_time_for_messages(*time)),
);
tokens.push(Style::default().paint(" "));
} else {
tokens.push("".into());
match envelope {
Envelope::RawMessage(content) => {
// RawMessages have already been split into single lines without newlines appended,
// and so can be rendered directly as ANSIStrings.
tokens.push(ANSIString::from(content))
}
Envelope::Message(Message {
time,
level,
origin,
message,
}) => {
let message_block_len = origin.width();
current_maximum = current_maximum.max(message_block_len);
if state.message_origin_size.len() == max_height {
state.message_origin_size.pop_front();
}
state.message_origin_size.push_back(message_block_len);

let color = to_color(*level);
tokens.push(" ".into());
if timestamp {
tokens.push(
brush
.style(color.dimmed().on(Color::Yellow))
.paint(crate::time::format_time_for_messages(*time)),
);
tokens.push(Style::default().paint(" "));
} else {
tokens.push("".into());
};
tokens.push(brush.style(Style::default().dimmed()).paint(format!(
"{:>fill_size$}{}",
"",
origin,
fill_size = current_maximum - message_block_len,
)));
tokens.push(" ".into());
tokens.push(brush.style(color.bold()).paint(message));
}
};
tokens.push(brush.style(Style::default().dimmed()).paint(format!(
"{:>fill_size$}{}",
"",
origin,
fill_size = current_maximum - message_block_len,
)));
tokens.push(" ".into());
tokens.push(brush.style(color.bold()).paint(message));

let message_block_count = block_count_sans_ansi_codes(&tokens);
write!(out, "{}", ANSIStrings(tokens.as_slice()))?;

Expand Down
13 changes: 9 additions & 4 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub trait Progress: Send + 'static {

/// Create a `message` of the given `level` and store it with the progress tree.
///
/// Use this to provide additional,human-readable information about the progress
/// Use this to provide additional, human-readable information about the progress
/// made, including indicating success or failure.
fn message(&mut self, level: MessageLevel, message: impl Into<String>);

Expand Down Expand Up @@ -126,7 +126,7 @@ pub trait Progress: Send + 'static {
}
}

use crate::messages::{Message, MessageCopyState};
use crate::messages::{Envelope, MessageCopyState};

/// The top level of a progress task hiearchy, with `progress::Task`s identified with `progress::Key`s
pub trait Root {
Expand All @@ -142,11 +142,16 @@ pub trait Root {
/// The `out` vec will be cleared automatically.
fn sorted_snapshot(&self, out: &mut Vec<(progress::Key, progress::Task)>);

/// Create a raw `message` and store it with the progress tree.
///
/// Use this to render additional unclassified output about the progress made.
fn message_raw(&mut self, message: impl Into<String>);

/// Copy all messages from the internal ring buffer into the given `out`
/// vector. Messages are ordered from oldest to newest.
fn copy_messages(&self, out: &mut Vec<Message>);
fn copy_messages(&self, out: &mut Vec<Envelope>);

/// Copy only new messages from the internal ring buffer into the given `out`
/// vector. Messages are ordered from oldest to newest.
fn copy_new_messages(&self, out: &mut Vec<Message>, prev: Option<MessageCopyState>) -> MessageCopyState;
fn copy_new_messages(&self, out: &mut Vec<Envelope>, prev: Option<MessageCopyState>) -> MessageCopyState;
}
6 changes: 3 additions & 3 deletions src/tree/item.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
messages::{MessageLevel, MessageRingBuffer},
messages::{Envelope, Message, MessageLevel, MessageRingBuffer},
progress::{key, Key, State, Step, Task, Value},
unit::Unit,
};
Expand Down Expand Up @@ -189,7 +189,7 @@ impl Item {
/// made, including indicating success or failure.
pub fn message(&mut self, level: MessageLevel, message: impl Into<String>) {
let message: String = message.into();
self.messages.lock().push_overwrite(
self.messages.lock().push_overwrite(Envelope::Message(Message::new(
level,
{
let name = self.tree.get(&self.key).map(|v| v.name.to_owned()).unwrap_or_default();
Expand All @@ -203,7 +203,7 @@ impl Item {
name
},
message,
)
)))
}

/// Create a message indicating the task is done
Expand Down
25 changes: 20 additions & 5 deletions src/tree/root.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
messages::{Message, MessageCopyState, MessageRingBuffer},
messages::{Envelope, MessageCopyState, MessageRingBuffer},
progress::{Key, Task},
tree::Item,
};
Expand Down Expand Up @@ -55,15 +55,26 @@ impl Root {
out.sort_by_key(|t| t.0);
}

/// Create a raw `message` and store it with the progress tree.
///
/// Use this to render additional unclassified output about the progress made.
fn message_raw(&mut self, message: impl Into<String>) {
let inner = self.inner.lock();
let mut messages = inner.messages.lock();
for line in message.into().lines() {
messages.push_overwrite(Envelope::RawMessage(line.to_owned()));
}
}

/// Copy all messages from the internal ring buffer into the given `out`
/// vector. Messages are ordered from oldest to newest.
pub fn copy_messages(&self, out: &mut Vec<Message>) {
pub fn copy_messages(&self, out: &mut Vec<Envelope>) {
self.inner.lock().messages.lock().copy_all(out);
}

/// Copy only new messages from the internal ring buffer into the given `out`
/// vector. Messages are ordered from oldest to newest.
pub fn copy_new_messages(&self, out: &mut Vec<Message>, prev: Option<MessageCopyState>) -> MessageCopyState {
pub fn copy_new_messages(&self, out: &mut Vec<Envelope>, prev: Option<MessageCopyState>) -> MessageCopyState {
self.inner.lock().messages.lock().copy_new(out, prev)
}

Expand Down Expand Up @@ -139,11 +150,15 @@ impl crate::Root for Root {
self.sorted_snapshot(out)
}

fn copy_messages(&self, out: &mut Vec<Message>) {
fn message_raw(&mut self, message: impl Into<String>) {
self.message_raw(message)
}

fn copy_messages(&self, out: &mut Vec<Envelope>) {
self.copy_messages(out)
}

fn copy_new_messages(&self, out: &mut Vec<Message>, prev: Option<MessageCopyState>) -> MessageCopyState {
fn copy_new_messages(&self, out: &mut Vec<Envelope>, prev: Option<MessageCopyState>) -> MessageCopyState {
self.copy_new_messages(out, prev)
}
}

0 comments on commit 1b9ed14

Please sign in to comment.