Skip to content

Commit 1c1eda2

Browse files
committed
Add RawMessage support to the line renderer.
1 parent a1054e8 commit 1c1eda2

File tree

5 files changed

+108
-63
lines changed

5 files changed

+108
-63
lines changed

src/messages.rs

+28-13
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,31 @@ pub struct Message {
2626
pub message: String,
2727
}
2828

29+
impl Message {
30+
/// Create a new Message at the current time, with the given arguments.
31+
pub fn new(level: MessageLevel, origin: String, message: impl Into<String>) -> Self {
32+
Message {
33+
time: SystemTime::now(),
34+
level,
35+
origin,
36+
message: message.into(),
37+
}
38+
}
39+
}
40+
41+
/// A container for multiple message types.
42+
#[derive(Debug, Clone, Eq, PartialEq)]
43+
pub enum Envelope {
44+
/// A high level string message and its metadata.
45+
Message(Message),
46+
/// A single-line, raw message, with no metadata.
47+
RawMessage(String),
48+
}
49+
2950
/// A ring buffer for messages.
3051
#[derive(Debug, Clone, Eq, PartialEq)]
3152
pub struct MessageRingBuffer {
32-
pub(crate) buf: Vec<Message>,
53+
pub(crate) buf: Vec<Envelope>,
3354
cursor: usize,
3455
total: usize,
3556
}
@@ -44,25 +65,19 @@ impl MessageRingBuffer {
4465
}
4566
}
4667

47-
/// Push a `message` from `origin` at severity `level` into the buffer, possibly overwriting the last message added.
48-
pub fn push_overwrite(&mut self, level: MessageLevel, origin: String, message: impl Into<String>) {
49-
let msg = Message {
50-
time: SystemTime::now(),
51-
level,
52-
origin,
53-
message: message.into(),
54-
};
68+
/// Push a `message` into the buffer, possibly overwriting the last message added.
69+
pub fn push_overwrite(&mut self, message: Envelope) {
5570
if self.has_capacity() {
56-
self.buf.push(msg)
71+
self.buf.push(message)
5772
} else {
58-
self.buf[self.cursor] = msg;
73+
self.buf[self.cursor] = message;
5974
self.cursor = (self.cursor + 1) % self.buf.len();
6075
}
6176
self.total = self.total.wrapping_add(1);
6277
}
6378

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

7691
/// Copy all new messages into `out` that where received since the last time this method was called provided
7792
/// its `previous` return value.
78-
pub fn copy_new(&self, out: &mut Vec<Message>, previous: Option<MessageCopyState>) -> MessageCopyState {
93+
pub fn copy_new(&self, out: &mut Vec<Envelope>, previous: Option<MessageCopyState>) -> MessageCopyState {
7994
out.clear();
8095
match previous {
8196
Some(MessageCopyState { cursor, buf_len, total }) => {

src/render/line/draw.rs

+48-38
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
messages::{Message, MessageCopyState, MessageLevel},
2+
messages::{Envelope, Message, MessageCopyState, MessageLevel},
33
progress::{self, Value},
44
unit, Root, Throughput,
55
};
@@ -13,7 +13,7 @@ use unicode_width::UnicodeWidthStr;
1313
#[derive(Default)]
1414
pub struct State {
1515
tree: Vec<(progress::Key, progress::Task)>,
16-
messages: Vec<Message>,
16+
messages: Vec<Envelope>,
1717
for_next_copy: Option<MessageCopyState>,
1818
/// The size of the message origin, tracking the terminal height so things potentially off screen don't influence width anymore.
1919
message_origin_size: VecDeque<usize>,
@@ -52,47 +52,57 @@ fn messages(
5252
}
5353
let mut tokens: Vec<ANSIString<'_>> = Vec::with_capacity(6);
5454
let mut current_maximum = state.message_origin_size.iter().max().cloned().unwrap_or(0);
55-
for Message {
56-
time,
57-
level,
58-
origin,
59-
message,
60-
} in &state.messages
61-
{
55+
for envelope in &state.messages {
6256
tokens.clear();
6357
let blocks_drawn_during_previous_tick = state.blocks_per_line.pop_front().unwrap_or(0);
64-
let message_block_len = origin.width();
65-
current_maximum = current_maximum.max(message_block_len);
66-
if state.message_origin_size.len() == max_height {
67-
state.message_origin_size.pop_front();
68-
}
69-
state.message_origin_size.push_back(message_block_len);
7058

71-
let color = to_color(*level);
72-
tokens.push(" ".into());
73-
if timestamp {
74-
tokens.push(
75-
brush
76-
.style(color.dimmed().on(Color::Yellow))
77-
.paint(crate::time::format_time_for_messages(*time)),
78-
);
79-
tokens.push(Style::default().paint(" "));
80-
} else {
81-
tokens.push("".into());
59+
match envelope {
60+
Envelope::RawMessage(content) => {
61+
// RawMessages have already been split into single lines without newlines appended,
62+
// and so can be rendered directly as ANSIStrings.
63+
tokens.push(ANSIString::from(content))
64+
}
65+
Envelope::Message(Message {
66+
time,
67+
level,
68+
origin,
69+
message,
70+
}) => {
71+
let message_block_len = origin.width();
72+
current_maximum = current_maximum.max(message_block_len);
73+
if state.message_origin_size.len() == max_height {
74+
state.message_origin_size.pop_front();
75+
}
76+
state.message_origin_size.push_back(message_block_len);
77+
78+
let color = to_color(*level);
79+
tokens.push(" ".into());
80+
if timestamp {
81+
tokens.push(
82+
brush
83+
.style(color.dimmed().on(Color::Yellow))
84+
.paint(crate::time::format_time_for_messages(*time)),
85+
);
86+
tokens.push(Style::default().paint(" "));
87+
} else {
88+
tokens.push("".into());
89+
};
90+
tokens.push(brush.style(Style::default().dimmed()).paint(format!(
91+
"{:>fill_size$}{}",
92+
"",
93+
origin,
94+
fill_size = current_maximum - message_block_len,
95+
)));
96+
tokens.push(" ".into());
97+
tokens.push(brush.style(color.bold()).paint(message));
98+
}
8299
};
83-
tokens.push(brush.style(Style::default().dimmed()).paint(format!(
84-
"{:>fill_size$}{}",
85-
"",
86-
origin,
87-
fill_size = current_maximum - message_block_len,
88-
)));
89-
tokens.push(" ".into());
90-
tokens.push(brush.style(color.bold()).paint(message));
100+
91101
let message_block_count = block_count_sans_ansi_codes(&tokens);
92102
write!(out, "{}", ANSIStrings(tokens.as_slice()))?;
93103

94104
if blocks_drawn_during_previous_tick > message_block_count {
95-
newline_with_overdraw(out, &tokens, blocks_drawn_during_previous_tick)?;
105+
newline_with_overdraw(out, message_block_count, blocks_drawn_during_previous_tick)?;
96106
} else {
97107
writeln!(out)?;
98108
}
@@ -161,7 +171,8 @@ pub fn all(
161171
);
162172
write!(out, "{}", ANSIStrings(tokens.as_slice()))?;
163173

164-
**blocks_in_last_iteration = newline_with_overdraw(out, &tokens, **blocks_in_last_iteration)?;
174+
**blocks_in_last_iteration =
175+
newline_with_overdraw(out, block_count_sans_ansi_codes(&tokens), **blocks_in_last_iteration)?;
165176
}
166177
if let Some(tp) = state.throughput.as_mut() {
167178
tp.reconcile(&state.tree);
@@ -186,10 +197,9 @@ pub fn all(
186197
/// Must be called directly after `tokens` were drawn, without newline. Takes care of adding the newline.
187198
fn newline_with_overdraw(
188199
out: &mut impl io::Write,
189-
tokens: &[ANSIString<'_>],
200+
current_block_count: u16,
190201
blocks_in_last_iteration: u16,
191202
) -> io::Result<u16> {
192-
let current_block_count = block_count_sans_ansi_codes(tokens);
193203
if blocks_in_last_iteration > current_block_count {
194204
// fill to the end of line to overwrite what was previously there
195205
writeln!(

src/traits.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub trait Progress: Send + 'static {
6565

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

@@ -126,7 +126,7 @@ pub trait Progress: Send + 'static {
126126
}
127127
}
128128

129-
use crate::messages::{Message, MessageCopyState};
129+
use crate::messages::{Envelope, MessageCopyState};
130130

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

145+
/// Create a raw `message` and store it with the progress tree.
146+
///
147+
/// Use this to render additional unclassified output about the progress made.
148+
fn message_raw(&mut self, message: impl Into<String>);
149+
145150
/// Copy all messages from the internal ring buffer into the given `out`
146151
/// vector. Messages are ordered from oldest to newest.
147-
fn copy_messages(&self, out: &mut Vec<Message>);
152+
fn copy_messages(&self, out: &mut Vec<Envelope>);
148153

149154
/// Copy only new messages from the internal ring buffer into the given `out`
150155
/// vector. Messages are ordered from oldest to newest.
151-
fn copy_new_messages(&self, out: &mut Vec<Message>, prev: Option<MessageCopyState>) -> MessageCopyState;
156+
fn copy_new_messages(&self, out: &mut Vec<Envelope>, prev: Option<MessageCopyState>) -> MessageCopyState;
152157
}

src/tree/item.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
messages::{MessageLevel, MessageRingBuffer},
2+
messages::{Envelope, Message, MessageLevel, MessageRingBuffer},
33
progress::{key, Key, State, Step, Task, Value},
44
unit::Unit,
55
};
@@ -189,7 +189,7 @@ impl Item {
189189
/// made, including indicating success or failure.
190190
pub fn message(&mut self, level: MessageLevel, message: impl Into<String>) {
191191
let message: String = message.into();
192-
self.messages.lock().push_overwrite(
192+
self.messages.lock().push_overwrite(Envelope::Message(Message::new(
193193
level,
194194
{
195195
let name = self.tree.get(&self.key).map(|v| v.name.to_owned()).unwrap_or_default();
@@ -203,7 +203,7 @@ impl Item {
203203
name
204204
},
205205
message,
206-
)
206+
)))
207207
}
208208

209209
/// Create a message indicating the task is done

src/tree/root.rs

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
messages::{Message, MessageCopyState, MessageRingBuffer},
2+
messages::{Envelope, MessageCopyState, MessageRingBuffer},
33
progress::{Key, Task},
44
tree::Item,
55
};
@@ -55,15 +55,26 @@ impl Root {
5555
out.sort_by_key(|t| t.0);
5656
}
5757

58+
/// Create a raw `message` and store it with the progress tree.
59+
///
60+
/// Use this to render additional unclassified output about the progress made.
61+
fn message_raw(&mut self, message: impl Into<String>) {
62+
let inner = self.inner.lock();
63+
let mut messages = inner.messages.lock();
64+
for line in message.into().lines() {
65+
messages.push_overwrite(Envelope::RawMessage(line.trim_end().to_owned()));
66+
}
67+
}
68+
5869
/// Copy all messages from the internal ring buffer into the given `out`
5970
/// vector. Messages are ordered from oldest to newest.
60-
pub fn copy_messages(&self, out: &mut Vec<Message>) {
71+
pub fn copy_messages(&self, out: &mut Vec<Envelope>) {
6172
self.inner.lock().messages.lock().copy_all(out);
6273
}
6374

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

@@ -139,11 +150,15 @@ impl crate::Root for Root {
139150
self.sorted_snapshot(out)
140151
}
141152

142-
fn copy_messages(&self, out: &mut Vec<Message>) {
153+
fn message_raw(&mut self, message: impl Into<String>) {
154+
self.message_raw(message)
155+
}
156+
157+
fn copy_messages(&self, out: &mut Vec<Envelope>) {
143158
self.copy_messages(out)
144159
}
145160

146-
fn copy_new_messages(&self, out: &mut Vec<Message>, prev: Option<MessageCopyState>) -> MessageCopyState {
161+
fn copy_new_messages(&self, out: &mut Vec<Envelope>, prev: Option<MessageCopyState>) -> MessageCopyState {
147162
self.copy_new_messages(out, prev)
148163
}
149164
}

0 commit comments

Comments
 (0)