Skip to content

Commit

Permalink
new: formatting improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
pamburus committed Jun 11, 2024
1 parent 8d45a82 commit 1d162df
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 61 deletions.
2 changes: 1 addition & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,7 @@ mod tests {
});
app.run(vec![input], &mut output).unwrap();
let actual = std::str::from_utf8(&output).unwrap();
let expected = "\u{1b}[0;2;3m2023-12-07 20:07:05.949 \u{1b}[0;36m|INF| \u{1b}[0;32mmsg\u{1b}[0;2m=\u{1b}[0;93m[\u{1b}[0;39mx\u{1b}[0;2;3m \u{1b}[0;39my\u{1b}[0;93m]\u{1b}[0;2;3m \u{1b}[0;32mduration\u{1b}[0;2m=\u{1b}[0;39m15d\u{1b}[0;2;3m @ main.go:539\u{1b}[0m\n";
let expected = "\u{1b}[0;2;3m2023-12-07 20:07:05.949 \u{1b}[0;36m|INF|\u{1b}[0;2;3m \u{1b}[0;32mmsg\u{1b}[0;2m=\u{1b}[0;93m[\u{1b}[0;39mx\u{1b}[0;2;3m \u{1b}[0;39my\u{1b}[0;93m]\u{1b}[0;2;3m \u{1b}[0;32mduration\u{1b}[0;2m=\u{1b}[0;39m15d\u{1b}[0;2;3m @ main.go:539\u{1b}[0m\n";
assert_eq!(actual, expected);
}

Expand Down
18 changes: 9 additions & 9 deletions src/fmtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,33 +448,33 @@ enum AlignerBuffer<T> {
// ---

#[inline]
pub fn aligned<'a, T, O, F>(out: &'a mut O, adjustment: Option<Adjustment<T>>, f: F)
pub fn aligned<'a, T, O, R, F>(out: &'a mut O, adjustment: Option<Adjustment<T>>, f: F) -> R
where
T: Clone,
O: Push<T>,
F: FnOnce(Aligner<'a, T, O>),
F: FnOnce(Aligner<'a, T, O>) -> R,
{
f(Aligner::new(out, adjustment));
f(Aligner::new(out, adjustment))
}

#[inline]
pub fn aligned_left<'a, T, O, F>(out: &'a mut O, width: usize, pad: T, f: F)
pub fn aligned_left<'a, T, O, R, F>(out: &'a mut O, width: usize, pad: T, f: F) -> R
where
T: Clone,
O: Push<T>,
F: FnOnce(UnbufferedAligner<'a, T, O>),
F: FnOnce(UnbufferedAligner<'a, T, O>) -> R,
{
f(UnbufferedAligner::new(out, Padding::new(pad, width)));
f(UnbufferedAligner::new(out, Padding::new(pad, width)))
}

#[inline]
pub fn centered<'a, T, O, F>(out: &'a mut O, width: usize, pad: T, f: F)
pub fn centered<'a, T, O, R, F>(out: &'a mut O, width: usize, pad: T, f: F) -> R
where
T: Clone,
O: Push<T>,
F: FnOnce(BufferedAligner<'a, T, O>),
F: FnOnce(BufferedAligner<'a, T, O>) -> R,
{
f(BufferedAligner::new(out, Padding::new(pad, width), Alignment::Center));
f(BufferedAligner::new(out, Padding::new(pad, width), Alignment::Center))
}

#[cfg(test)]
Expand Down
158 changes: 107 additions & 51 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
model::{self, Caller, Level, RawValue},
settings::{ExpandOption, FlattenOption, Formatting, Punctuation},
syntax::*,
theme::{Element, StylingPush, Theme},
theme::{Element, Styler, StylingPush, Theme},
IncludeExcludeKeyFilter,
};

Expand Down Expand Up @@ -138,36 +138,15 @@ impl RecordFormatter {
//
// time
//
if let Some(ts) = &rec.ts {
fs.ts_width = self.ts_width.chars;
fs.add_element(|| {});
s.element(Element::Time, |s| {
s.batch(|buf| {
aligned_left(buf, self.ts_width.bytes, b' ', |mut buf| {
if ts
.as_rfc3339()
.and_then(|ts| self.cfg.ts_formatter.reformat_rfc3339(&mut buf, ts))
.is_none()
{
if let Some(ts) = ts.parse() {
self.cfg.ts_formatter.format(&mut buf, ts);
} else {
buf.extend_from_slice(ts.raw().as_bytes());
}
}
});
})
});
} else if self.cfg.always_show_time {
fs.ts_width = self.ts_width.chars;
fs.add_element(|| {});
s.element(Element::Time, |s| {
s.batch(|buf| {
centered(buf, self.ts_width.bytes, b'-', |mut buf| {
buf.extend_from_slice(b"-");
});
})
});
if !fs.transact(s, |fs, s| self.format_timestamp(rec, fs, s)) {
if let Some(ts) = &rec.ts {
fs.x_fields
.push(("ts", RawValue::String(EncodedString::raw(ts.raw()))))
.ok();
}
if self.cfg.always_show_time {
self.format_timestamp_stub(&mut fs, s);
}
}

//
Expand All @@ -178,7 +157,7 @@ impl RecordFormatter {
Some(Level::Info) => Some(LEVEL_INFO.as_bytes()),
Some(Level::Warning) => Some(LEVEL_WARNING.as_bytes()),
Some(Level::Error) => Some(LEVEL_ERROR.as_bytes()),
_ => None,
None => None,
};
let level = level.or_else(|| self.cfg.always_show_level.then(|| LEVEL_UNKNOWN.as_bytes()));
if let Some(level) = level {
Expand All @@ -203,8 +182,11 @@ impl RecordFormatter {
// message text
//
if let Some(value) = &rec.message {
self.format_message(s, &mut fs, *value);
fs.first_line_used = true;
if fs.transact(s, |fs, s| self.format_message(s, fs, *value)) {
fs.first_line_used = true;
} else {
fs.x_fields.push(("msg", *value)).ok();
}
} else {
s.reset();
}
Expand All @@ -231,13 +213,9 @@ impl RecordFormatter {
//
// fields
//
if fs.format_message_as_field {
if let Some(value) = &rec.message {
_ = self.format_field(s, "msg", *value, &mut fs, None);
}
}
let mut some_fields_hidden = false;
for (k, v) in rec.fields() {
let x_fields = std::mem::take(&mut fs.x_fields);
for (k, v) in x_fields.iter().chain(rec.fields()) {
for _ in 0..2 {
if !self.cfg.hide_empty_fields || !v.is_empty() {
let cp = fs.checkpoint(s);
Expand Down Expand Up @@ -276,6 +254,48 @@ impl RecordFormatter {
});
}

#[inline]
#[must_use]
fn format_timestamp<S: StylingPush<Buf>>(&self, rec: &model::Record, fs: &mut FormattingState, s: &mut S) -> bool {
let Some(ts) = &rec.ts else {
return false;
};

fs.ts_width = self.ts_width.chars;
fs.add_element(|| {});
s.element(Element::Time, |s| {
s.batch(|buf| {
aligned_left(buf, self.ts_width.bytes, b' ', |mut buf| {
if ts
.as_rfc3339()
.and_then(|ts| self.cfg.ts_formatter.reformat_rfc3339(&mut buf, ts))
.is_some()
{
true
} else if let Some(ts) = ts.parse() {
self.cfg.ts_formatter.format(&mut buf, ts);
true
} else {
false
}
})
})
})
}

#[inline]
fn format_timestamp_stub<S: StylingPush<Buf>>(&self, fs: &mut FormattingState, s: &mut S) {
fs.ts_width = self.ts_width.chars;
fs.add_element(|| {});
s.element(Element::Time, |s| {
s.batch(|buf| {
centered(buf, self.ts_width.chars, b'-', |mut buf| {
buf.extend_from_slice(b"-");
});
})
});
}

#[inline]
fn complexity(&self, rec: &model::Record, filter: Option<&IncludeExcludeKeyFilter>) -> usize {
let mut result = 0;
Expand Down Expand Up @@ -316,7 +336,7 @@ impl RecordFormatter {
}

#[inline]
fn format_caller(&self, s: &mut crate::theme::Styler<Vec<u8>>, caller: &Caller) {
fn format_caller(&self, s: &mut Styler<Buf>, caller: &Caller) {
s.element(Element::Caller, |s| {
s.batch(|buf| {
buf.push(b' ');
Expand Down Expand Up @@ -355,7 +375,13 @@ impl RecordFormatter {
}

#[inline]
fn format_message<'a, S: StylingPush<Buf>>(&self, s: &mut S, fs: &mut FormattingState, value: RawValue<'a>) {
#[must_use]
fn format_message<'a, S: StylingPush<Buf>>(
&self,
s: &mut S,
fs: &mut FormattingState,
value: RawValue<'a>,
) -> bool {
match value {
RawValue::String(value) => {
if !value.is_empty() {
Expand All @@ -378,17 +404,19 @@ impl RecordFormatter {
.unwrap();
if result.aborted {
buf.extend(EXPANDED_MESSAGE_HEADER.as_bytes());
fs.format_message_as_field = true;
fs.expand = Some(true);
false
} else {
true
}
})
});
})
} else {
true
}
}
_ => {
fs.format_message_as_field = true;
}
};
_ => false,
}
}

#[inline]
Expand Down Expand Up @@ -473,7 +501,7 @@ impl From<RecordFormatterSettings> for RecordFormatter {
// ---

#[derive(Default)]
struct FormattingState {
struct FormattingState<'a> {
flatten: bool,
expand: Option<bool>,
prefix: Range<usize>,
Expand All @@ -485,10 +513,10 @@ struct FormattingState {
depth: usize,
first_line_used: bool,
some_fields_hidden: bool,
format_message_as_field: bool,
x_fields: heapless::Vec<(&'a str, RawValue<'a>), 5>,
}

impl FormattingState {
impl<'a> FormattingState<'a> {
fn add_element(&mut self, add_space: impl FnOnce()) {
if !self.dirty {
self.dirty = true;
Expand All @@ -497,6 +525,19 @@ impl FormattingState {
}
}

fn transact(&mut self, s: &mut Styler<Buf>, f: impl FnOnce(&mut Self, &mut Styler<Buf>) -> bool) -> bool {
let dirty = self.dirty;
let depth = self.depth;
let first_line_used = self.first_line_used;
let result = s.transact(|s| f(self, s));
if !result {
self.dirty = dirty;
self.depth = depth;
self.first_line_used = first_line_used;
}
result
}

fn checkpoint<S: StylingPush<Buf>>(&self, s: &mut S) -> Checkpoint {
Checkpoint {
dirty: self.dirty,
Expand Down Expand Up @@ -2128,4 +2169,19 @@ mod tests {

assert_eq!(format_no_color(&rec(r#""243""#)), r#"a="243""#);
}

#[test]
fn test_format_unparsable_time() {
let rec = |ts, msg| Record {
ts: Some(Timestamp::new(ts)),
level: Some(Level::Info),
message: Some(EncodedString::raw(msg).into()),
..Default::default()
};

assert_eq!(
format_no_color(&rec("some-unparsable-time", "some-msg")),
"|INF| some-msg ts=some-unparsable-time"
);
}
}
17 changes: 17 additions & 0 deletions src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,23 @@ impl<'a, B: Push<u8>> Styler<'a, B> {
}
}

impl<'a> Styler<'a, Vec<u8>> {
#[inline]
pub fn transact<F: FnOnce(&mut Self) -> bool>(&mut self, f: F) -> bool {
let current = self.current;
let synced = self.synced;
let n = self.buf.len();
if f(self) {
true
} else {
self.buf.truncate(n);
self.current = current;
self.synced = synced;
false
}
}
}

impl<'a, B: Push<u8>> StylingPush<B> for Styler<'a, B> {
#[inline]
fn element<R, F: FnOnce(&mut Self) -> R>(&mut self, element: Element, f: F) -> R {
Expand Down

0 comments on commit 1d162df

Please sign in to comment.