feat: re-arrange columns + ContainerName + ContainerImage, closes #32
Have container name as first column. Wrap name and image using the StringWrapper macro, so that can have a custom fmt::Display, which will only show the firs 29 chars of both the name and image name
This commit is contained in:
@@ -428,6 +428,51 @@ impl Logs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ContainerName and ContainerImage are simple structs, used so can implement custom fmt functions to them
|
||||||
|
macro_rules! string_wrapper {
|
||||||
|
($name:ident) => {
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct $name(String);
|
||||||
|
|
||||||
|
impl From<String> for $name {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl$name {
|
||||||
|
pub fn get(&self) -> String {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, value: String) {
|
||||||
|
self.0 = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for $name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
if self.0.chars().count() >= 30 {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}…",
|
||||||
|
self.0.chars().take(29).collect::<String>()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
self.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
string_wrapper!(ContainerName);
|
||||||
|
string_wrapper!(ContainerImage);
|
||||||
|
|
||||||
/// Info for each container
|
/// Info for each container
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ContainerItem {
|
pub struct ContainerItem {
|
||||||
@@ -435,12 +480,12 @@ pub struct ContainerItem {
|
|||||||
pub cpu_stats: VecDeque<CpuStats>,
|
pub cpu_stats: VecDeque<CpuStats>,
|
||||||
pub docker_controls: StatefulList<DockerControls>,
|
pub docker_controls: StatefulList<DockerControls>,
|
||||||
pub id: ContainerId,
|
pub id: ContainerId,
|
||||||
pub image: String,
|
pub image: ContainerImage,
|
||||||
pub last_updated: u64,
|
pub last_updated: u64,
|
||||||
pub logs: Logs,
|
pub logs: Logs,
|
||||||
pub mem_limit: ByteStats,
|
pub mem_limit: ByteStats,
|
||||||
pub mem_stats: VecDeque<ByteStats>,
|
pub mem_stats: VecDeque<ByteStats>,
|
||||||
pub name: String,
|
pub name: ContainerName,
|
||||||
pub rx: ByteStats,
|
pub rx: ByteStats,
|
||||||
pub state: State,
|
pub state: State,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
@@ -480,13 +525,13 @@ impl ContainerItem {
|
|||||||
cpu_stats: VecDeque::with_capacity(60),
|
cpu_stats: VecDeque::with_capacity(60),
|
||||||
docker_controls,
|
docker_controls,
|
||||||
id,
|
id,
|
||||||
image,
|
image: image.into(),
|
||||||
is_oxker,
|
is_oxker,
|
||||||
last_updated: 0,
|
last_updated: 0,
|
||||||
logs: Logs::default(),
|
logs: Logs::default(),
|
||||||
mem_limit: ByteStats::default(),
|
mem_limit: ByteStats::default(),
|
||||||
mem_stats: VecDeque::with_capacity(60),
|
mem_stats: VecDeque::with_capacity(60),
|
||||||
name,
|
name: name.into(),
|
||||||
rx: ByteStats::default(),
|
rx: ByteStats::default(),
|
||||||
state,
|
state,
|
||||||
status,
|
status,
|
||||||
@@ -550,12 +595,12 @@ impl ContainerItem {
|
|||||||
/// Container information panel headings + widths, for nice pretty formatting
|
/// Container information panel headings + widths, for nice pretty formatting
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Columns {
|
pub struct Columns {
|
||||||
|
pub name: (Header, u8),
|
||||||
pub state: (Header, u8),
|
pub state: (Header, u8),
|
||||||
pub status: (Header, u8),
|
pub status: (Header, u8),
|
||||||
pub cpu: (Header, u8),
|
pub cpu: (Header, u8),
|
||||||
pub mem: (Header, u8, u8),
|
pub mem: (Header, u8, u8),
|
||||||
pub id: (Header, u8),
|
pub id: (Header, u8),
|
||||||
pub name: (Header, u8),
|
|
||||||
pub image: (Header, u8),
|
pub image: (Header, u8),
|
||||||
pub net_rx: (Header, u8),
|
pub net_rx: (Header, u8),
|
||||||
pub net_tx: (Header, u8),
|
pub net_tx: (Header, u8),
|
||||||
@@ -565,12 +610,12 @@ impl Columns {
|
|||||||
/// (Column titles, minimum header string length)
|
/// (Column titles, minimum header string length)
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
name: (Header::Name, 4),
|
||||||
state: (Header::State, 11),
|
state: (Header::State, 11),
|
||||||
status: (Header::Status, 16),
|
status: (Header::Status, 16),
|
||||||
cpu: (Header::Cpu, 7),
|
cpu: (Header::Cpu, 7),
|
||||||
mem: (Header::Memory, 7, 7),
|
mem: (Header::Memory, 7, 7),
|
||||||
id: (Header::Id, 8),
|
id: (Header::Id, 8),
|
||||||
name: (Header::Name, 4),
|
|
||||||
image: (Header::Image, 5),
|
image: (Header::Image, 5),
|
||||||
net_rx: (Header::Rx, 7),
|
net_rx: (Header::Rx, 7),
|
||||||
net_tx: (Header::Tx, 7),
|
net_tx: (Header::Tx, 7),
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ impl DockerData {
|
|||||||
self.init_all_logs(&all_ids);
|
self.init_all_logs(&all_ids);
|
||||||
|
|
||||||
while let Some(x) = self.init.as_ref() {
|
while let Some(x) = self.init.as_ref() {
|
||||||
|
self.app_data.lock().sort_containers();
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||||
if x.load(std::sync::atomic::Ordering::SeqCst) == all_ids.len() {
|
if x.load(std::sync::atomic::Ordering::SeqCst) == all_ids.len() {
|
||||||
self.init = None;
|
self.init = None;
|
||||||
|
|||||||
@@ -393,12 +393,12 @@ impl InputHandler {
|
|||||||
} else {
|
} else {
|
||||||
match key_code {
|
match key_code {
|
||||||
KeyCode::Char('0') => self.app_data.lock().reset_sorted(),
|
KeyCode::Char('0') => self.app_data.lock().reset_sorted(),
|
||||||
KeyCode::Char('1') => self.sort(Header::State),
|
KeyCode::Char('1') => self.sort(Header::Name),
|
||||||
KeyCode::Char('2') => self.sort(Header::Status),
|
KeyCode::Char('2') => self.sort(Header::State),
|
||||||
KeyCode::Char('3') => self.sort(Header::Cpu),
|
KeyCode::Char('3') => self.sort(Header::Status),
|
||||||
KeyCode::Char('4') => self.sort(Header::Memory),
|
KeyCode::Char('4') => self.sort(Header::Cpu),
|
||||||
KeyCode::Char('5') => self.sort(Header::Id),
|
KeyCode::Char('5') => self.sort(Header::Memory),
|
||||||
KeyCode::Char('6') => self.sort(Header::Name),
|
KeyCode::Char('6') => self.sort(Header::Id),
|
||||||
KeyCode::Char('7') => self.sort(Header::Image),
|
KeyCode::Char('7') => self.sort(Header::Image),
|
||||||
KeyCode::Char('8') => self.sort(Header::Rx),
|
KeyCode::Char('8') => self.sort(Header::Rx),
|
||||||
KeyCode::Char('9') => self.sort(Header::Tx),
|
KeyCode::Char('9') => self.sort(Header::Tx),
|
||||||
|
|||||||
+26
-10
@@ -13,7 +13,7 @@ use ratatui::{
|
|||||||
use std::{default::Default, time::Instant};
|
use std::{default::Default, time::Instant};
|
||||||
use std::{fmt::Display, sync::Arc};
|
use std::{fmt::Display, sync::Arc};
|
||||||
|
|
||||||
use crate::app_data::{ContainerItem, Header, SortedOrder};
|
use crate::app_data::{ContainerItem, Header, SortedOrder, ContainerName};
|
||||||
use crate::{
|
use crate::{
|
||||||
app_data::{AppData, ByteStats, Columns, CpuStats, State, Stats},
|
app_data::{AppData, ByteStats, Columns, CpuStats, State, Stats},
|
||||||
app_error::AppError,
|
app_error::AppError,
|
||||||
@@ -127,10 +127,19 @@ fn format_containers<'a>(i: &ContainerItem, widths: &Columns) -> Line<'a> {
|
|||||||
let state_style = Style::default().fg(i.state.get_color());
|
let state_style = Style::default().fg(i.state.get_color());
|
||||||
let blue = Style::default().fg(Color::Blue);
|
let blue = Style::default().fg(Color::Blue);
|
||||||
|
|
||||||
|
// Truncate?
|
||||||
Line::from(vec![
|
Line::from(vec![
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!(
|
format!(
|
||||||
"{:<width$}",
|
"{:>width$}",
|
||||||
|
i.name.to_string(),
|
||||||
|
width = widths.name.1.into()
|
||||||
|
),
|
||||||
|
blue,
|
||||||
|
),
|
||||||
|
Span::styled(
|
||||||
|
format!(
|
||||||
|
"{MARGIN}{:<width$}",
|
||||||
i.state.to_string(),
|
i.state.to_string(),
|
||||||
width = widths.state.1.into()
|
width = widths.state.1.into()
|
||||||
),
|
),
|
||||||
@@ -173,11 +182,11 @@ fn format_containers<'a>(i: &ContainerItem, widths: &Columns) -> Line<'a> {
|
|||||||
blue,
|
blue,
|
||||||
),
|
),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!("{MARGIN}{:>width$}", i.name, width = widths.name.1.into()),
|
format!(
|
||||||
blue,
|
"{MARGIN}{:>width$}",
|
||||||
|
i.image.to_string(),
|
||||||
|
width = widths.image.1.into()
|
||||||
),
|
),
|
||||||
Span::styled(
|
|
||||||
format!("{MARGIN}{:>width$}", i.image, width = widths.image.1.into()),
|
|
||||||
blue,
|
blue,
|
||||||
),
|
),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
@@ -378,9 +387,16 @@ pub fn heading_bar(
|
|||||||
// width is dependant on it that column is selected to sort - or not
|
// width is dependant on it that column is selected to sort - or not
|
||||||
let gen_header = |header: &Header, width: usize| {
|
let gen_header = |header: &Header, width: usize| {
|
||||||
let block = header_block(header);
|
let block = header_block(header);
|
||||||
|
// Yes this is a mess, needs documenting correctly
|
||||||
let text = match header {
|
let text = match header {
|
||||||
Header::State => format!(
|
Header::State => format!(
|
||||||
"{:>width$}{ic}",
|
" {:>width$}{ic}",
|
||||||
|
header,
|
||||||
|
ic = block.1,
|
||||||
|
width = width - block.2,
|
||||||
|
),
|
||||||
|
Header::Name => format!(
|
||||||
|
" {:>width$}{ic}",
|
||||||
header,
|
header,
|
||||||
ic = block.1,
|
ic = block.1,
|
||||||
width = width - block.2,
|
width = width - block.2,
|
||||||
@@ -409,12 +425,12 @@ pub fn heading_bar(
|
|||||||
|
|
||||||
// Meta data to iterate over to create blocks with correct widths
|
// Meta data to iterate over to create blocks with correct widths
|
||||||
let header_meta = [
|
let header_meta = [
|
||||||
|
(Header::Name, data.columns.name.1),
|
||||||
(Header::State, data.columns.state.1),
|
(Header::State, data.columns.state.1),
|
||||||
(Header::Status, data.columns.status.1),
|
(Header::Status, data.columns.status.1),
|
||||||
(Header::Cpu, data.columns.cpu.1),
|
(Header::Cpu, data.columns.cpu.1),
|
||||||
(Header::Memory, data.columns.mem.1 + data.columns.mem.2 + 3),
|
(Header::Memory, data.columns.mem.1 + data.columns.mem.2 + 3),
|
||||||
(Header::Id, data.columns.id.1),
|
(Header::Id, data.columns.id.1),
|
||||||
(Header::Name, data.columns.name.1),
|
|
||||||
(Header::Image, data.columns.image.1),
|
(Header::Image, data.columns.image.1),
|
||||||
(Header::Rx, data.columns.net_rx.1),
|
(Header::Rx, data.columns.net_rx.1),
|
||||||
(Header::Tx, data.columns.net_tx.1),
|
(Header::Tx, data.columns.net_tx.1),
|
||||||
@@ -735,7 +751,7 @@ pub fn help_box(f: &mut Frame) {
|
|||||||
|
|
||||||
/// Draw the delete confirm box in the centre of the screen
|
/// Draw the delete confirm box in the centre of the screen
|
||||||
/// take in container id and container name here?
|
/// take in container id and container name here?
|
||||||
pub fn delete_confirm(f: &mut Frame, gui_state: &Arc<Mutex<GuiState>>, name: &str) {
|
pub fn delete_confirm(f: &mut Frame, gui_state: &Arc<Mutex<GuiState>>, name: &ContainerName) {
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title(" Confirm Delete ")
|
.title(" Confirm Delete ")
|
||||||
.border_type(BorderType::Rounded)
|
.border_type(BorderType::Rounded)
|
||||||
@@ -746,7 +762,7 @@ pub fn delete_confirm(f: &mut Frame, gui_state: &Arc<Mutex<GuiState>>, name: &st
|
|||||||
let confirm = Line::from(vec![
|
let confirm = Line::from(vec![
|
||||||
Span::from("Are you sure you want to delete container: "),
|
Span::from("Are you sure you want to delete container: "),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
name,
|
name.to_string(),
|
||||||
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
|
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user