feat: set log timezone, closes #56

Implement a CLI arg, and config file setting, for changing the timezone of the Docker logs timestamp
This commit is contained in:
Jack Wills
2025-02-21 11:03:19 +00:00
parent 8305e6fda6
commit 17a5e7a258
27 changed files with 1122 additions and 163 deletions
+171 -41
View File
@@ -1,4 +1,5 @@
use crossterm::event::KeyCode;
use jiff::tz::TimeZone;
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout},
style::{Color, Modifier, Style},
@@ -84,13 +85,13 @@ impl HelpInfo {
}
/// Generate the button information span + metadata
fn gen_keymap_info(colors: AppColors) -> Self {
fn gen_keymap_info(colors: AppColors, zone: Option<&TimeZone>, show_timestamp: bool) -> Self {
let button_item = |x: &str| Self::highlighted_text_span(&format!(" ( {x} ) "), colors);
let button_desc = |x: &str| Self::text_span(x, colors);
let or = || button_desc("or");
let space = || button_desc(" ");
let lines = [
let descriptions = [
Line::from(vec![
space(),
button_item("tab"),
@@ -163,10 +164,23 @@ impl HelpInfo {
]),
];
let mut lines = if show_timestamp {
Vec::from([
Self::custom_text(colors, &Keymap::new(), zone),
Self::empty_span(),
])
} else {
vec![]
};
lines.extend_from_slice(&descriptions);
let width = Self::calc_width(&lines);
let height = lines.len();
Self {
lines: lines.to_vec(),
width: Self::calc_width(&lines),
height: lines.len(),
lines,
width,
height,
}
}
@@ -193,8 +207,31 @@ impl HelpInfo {
}
}
/// Display timezone in timestamps are visible
/// Has ability to display if keymap or colors are customized, but currently not in use
fn custom_text<'a>(colors: AppColors, _keymap: &Keymap, zone: Option<&TimeZone>) -> Line<'a> {
let highlighted = |x: &str| Self::highlighted_text_span(x, colors);
let text = |x: &str| Self::text_span(x, colors);
// if keymap != &Keymap::new() {
// op.push(highlighted("customised keymap, "));
// }
// if colors != AppColors::new() {
// op.push(highlighted("customised app colors, "));
// };
let zone = zone.and_then(|i| i.iana_name()).unwrap_or("Etc/UTC");
Line::from(Vec::from([text("logs timezone: "), highlighted(zone)])).centered()
}
/// Generate the display information when a custom keymap is being used
fn gen_custom_keymap_info(colors: AppColors, km: &Keymap) -> Self {
fn gen_custom_keymap_info(
colors: AppColors,
km: &Keymap,
zone: Option<&TimeZone>,
show_timestamp: bool,
) -> Self {
let button_item = |x: &str| Self::highlighted_text_span(&format!(" ( {x} ) "), colors);
let button_desc = |x: &str| Self::text_span(x, colors);
let or = || button_desc("or");
@@ -220,11 +257,7 @@ impl HelpInfo {
},
)
};
let lines = [
Line::from(vec![Span::from("Custom keymap config in use\n")])
.alignment(Alignment::Center)
.style(Style::default().fg(colors.popup_help.text_highlight)),
let descriptions = [
or_secondary(km.select_next_panel, "select next panel"),
or_secondary(km.select_previous_panel, "select previous panel"),
or_secondary(km.scroll_down_one, "scroll list down by one"),
@@ -266,16 +299,32 @@ impl HelpInfo {
or_secondary(km.quit, "quit at any time"),
];
let mut lines = if show_timestamp {
Vec::from([Self::custom_text(colors, km, zone), Self::empty_span()])
} else {
vec![]
};
lines.extend_from_slice(&descriptions);
let width = Self::calc_width(&lines);
let height = lines.len();
Self {
lines: lines.to_vec(),
width: Self::calc_width(&lines),
height: lines.len(),
lines,
width,
height,
}
}
}
/// Draw the help box in the centre of the screen
pub fn draw(f: &mut Frame, colors: AppColors, keymap: &Keymap) {
pub fn draw(
colors: AppColors,
f: &mut Frame,
keymap: &Keymap,
show_timestamp: bool,
zone: Option<&TimeZone>,
) {
let title = format!(" {VERSION} ");
let name_info = HelpInfo::gen_name(colors);
@@ -283,9 +332,9 @@ pub fn draw(f: &mut Frame, colors: AppColors, keymap: &Keymap) {
let final_info = HelpInfo::gen_final(colors);
let button_info = if keymap == &Keymap::new() {
HelpInfo::gen_keymap_info(colors)
HelpInfo::gen_keymap_info(colors, zone, show_timestamp)
} else {
HelpInfo::gen_custom_keymap_info(colors, keymap)
HelpInfo::gen_custom_keymap_info(colors, keymap, zone, show_timestamp)
};
let max_line_width = [
@@ -364,13 +413,14 @@ pub fn draw(f: &mut Frame, colors: AppColors, keymap: &Keymap) {
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::unwrap_used, clippy::too_many_lines)]
mod tests {
use crate::{
config::{AppColors, Keymap},
ui::draw_blocks::VERSION,
};
use crossterm::event::KeyCode;
use jiff::tz::TimeZone;
use ratatui::style::{Color, Modifier};
use crate::ui::draw_blocks::tests::{expected_to_vec, get_result, test_setup};
@@ -380,12 +430,18 @@ mod tests {
fn test_draw_blocks_help() {
let (w, h) = (87, 33);
let mut setup = test_setup(w, h, true, true);
let colors = setup.app_data.lock().config.app_colors;
let tz = setup.app_data.lock().config.timezone.clone();
setup
.terminal
.draw(|f| {
super::draw(f, colors, &setup.app_data.lock().config.keymap);
super::draw(
AppColors::new(),
f,
&setup.app_data.lock().config.keymap,
false,
tz.as_ref(),
);
})
.unwrap();
@@ -476,6 +532,7 @@ mod tests {
let (w, h) = (87, 33);
let mut setup = test_setup(w, h, true, true);
let mut colors = AppColors::new();
let tz = setup.app_data.lock().config.timezone.clone();
colors.popup_help.background = Color::Black;
colors.popup_help.text = Color::Red;
@@ -484,7 +541,13 @@ mod tests {
setup
.terminal
.draw(|f| {
super::draw(f, colors, &setup.app_data.lock().config.keymap);
super::draw(
colors,
f,
&setup.app_data.lock().config.keymap,
false,
tz.as_ref(),
);
})
.unwrap();
@@ -571,10 +634,9 @@ mod tests {
#[test]
/// Help panel will show custom keymap if in use, with one definition for each entry
fn test_draw_blocks_custom_keymap_one_definition() {
let (w, h) = (98, 48);
fn test_draw_blocks_help_custom_keymap_one_definition() {
let (w, h) = (98, 47);
let mut setup = test_setup(w, h, true, true);
let colors = setup.app_data.lock().config.app_colors;
let input = Keymap {
clear: (KeyCode::Char('a'), None),
@@ -609,7 +671,7 @@ mod tests {
setup
.terminal
.draw(|f| {
super::draw(f, colors, &input);
super::draw(AppColors::new(), f, &input, false, None);
})
.unwrap();
@@ -629,7 +691,6 @@ mod tests {
" │ │ ",
" │ A simple tui to view & control docker containers │ ",
" │ │ ",
" │ Custom keymap config in use │ ",
" │ ( 0 ) select next panel │ ",
" │ ( 2 ) select previous panel │ ",
" │ ( q ) scroll list down by one │ ",
@@ -669,21 +730,17 @@ mod tests {
let expected_row = expected_to_vec(&expected, row_index);
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
if row_index == 14 && (36..=62).contains(&result_cell_index) {
assert_eq!(result_cell.fg, Color::White);
}
}
}
}
#[test]
/// Help panel will show custom keymap if in use, with two definition for each entry
fn test_draw_blocks_custom_keymap_two_definitions() {
let (w, h) = (110, 48);
fn test_draw_blocks_help_custom_keymap_two_definitions() {
let (w, h) = (110, 47);
let mut setup = test_setup(w, h, true, true);
let colors = setup.app_data.lock().config.app_colors;
let input = Keymap {
let keymap = Keymap {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))),
delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
@@ -716,7 +773,7 @@ mod tests {
setup
.terminal
.draw(|f| {
super::draw(f, colors, &input);
super::draw(AppColors::new(), f, &keymap, false, None);
})
.unwrap();
@@ -736,7 +793,6 @@ mod tests {
" │ │ ",
" │ A simple tui to view & control docker containers │ ",
" │ │ ",
" │ Custom keymap config in use │ ",
" │ ( 0 ) or ( 1 ) select next panel │ ",
" │ ( 2 ) or ( 3 ) select previous panel │ ",
" │ ( q ) or ( r ) scroll list down by one │ ",
@@ -782,12 +838,11 @@ mod tests {
#[test]
/// Help panel will show custom keymap if in use, with either one or two definition for each entry
fn test_draw_blocks_custom_keymap_one_and_two_definitions() {
let (w, h) = (110, 48);
fn test_draw_blocks_help_one_and_two_definitions() {
let (w, h) = (110, 47);
let mut setup = test_setup(w, h, true, true);
let colors = setup.app_data.lock().config.app_colors;
let input = Keymap {
let keymap = Keymap {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))),
delete_deny: (KeyCode::Char('c'), None),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
@@ -817,10 +872,12 @@ mod tests {
toggle_mouse_capture: (KeyCode::PageDown, Some(KeyCode::PageUp)),
};
let tz = setup.app_data.lock().config.timezone.clone();
setup
.terminal
.draw(|f| {
super::draw(f, colors, &input);
super::draw(AppColors::new(), f, &keymap, false, tz.as_ref());
})
.unwrap();
@@ -840,7 +897,6 @@ mod tests {
" │ │ ",
" │ A simple tui to view & control docker containers │ ",
" │ │ ",
" │ Custom keymap config in use │ ",
" │ ( 0 ) select next panel │ ",
" │ ( 2 ) or ( 3 ) select previous panel │ ",
" │ ( q ) or ( r ) scroll list down by one │ ",
@@ -883,4 +939,78 @@ mod tests {
}
}
}
#[test]
fn test_draw_blocks_help_show_timezone() {
let (w, h) = (87, 35);
let mut setup = test_setup(w, h, true, true);
setup
.terminal
.draw(|f| {
super::draw(
AppColors::new(),
f,
&Keymap::new(),
true,
Some(&TimeZone::get("asia/tokyo").unwrap()),
);
})
.unwrap();
let version_row = format!("{VERSION} ────────────────────────────────────────────────────────────────────────────╮ ");
let expected = [
" ",
version_row.as_str(),
" │ │ ",
" │ 88 │ ",
" │ 88 │ ",
" │ 88 │ ",
" │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ",
r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#,
r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#,
r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#,
r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#,
" │ │ ",
" │ A simple tui to view & control docker containers │ ",
" │ │ ",
" │ logs timezone: Asia/Tokyo │ ",
" │ │ ",
" │ ( tab ) or ( shift+tab ) change panels │ ",
" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ ",
" │ ( enter ) send docker container command │ ",
" │ ( e ) exec into a container │ ",
" │ ( h ) toggle this help information - or click heading │ ",
" │ ( s ) save logs to file │ ",
" │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ",
" │ ( F1 ) or ( / ) enter filter mode │ ",
" │ ( 0 ) stop sort │ ",
" │ ( 1 - 9 ) sort by header - or click header │ ",
" │ ( esc ) close dialog │ ",
" │ ( q ) quit at any time │ ",
" │ │ ",
" │ currently an early work in progress, all and any input appreciated │ ",
" │ https://github.com/mrjackwills/oxker │ ",
" │ │ ",
" │ │ ",
" ╰───────────────────────────────────────────────────────────────────────────────────╯ ",
" "
];
for (row_index, result_row) in get_result(&setup, w) {
let expected_row = expected_to_vec(&expected, row_index);
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) {
(14, 31..=45) => {
assert_eq!(result_cell.fg, AppColors::new().popup_help.text);
}
(14, 46..=55) => {
assert_eq!(result_cell.fg, AppColors::new().popup_help.text_highlight);
}
_ => (),
}
assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
}
}
}
}