refactor: major refactor of internal data handling
What started off as an inquisitive look at how the gui drawing blocks get the data they require in order to draw to the screen, ended up as a realisation that it could be achieved in a better manner. Basically just use x.get(y), instead of using x[y] all over the place
This commit is contained in:
+153
-101
@@ -1,7 +1,9 @@
|
||||
use bollard::models::ContainerSummary;
|
||||
use core::fmt;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tui::widgets::ListItem;
|
||||
use tui::{
|
||||
widgets::{ListItem, ListState},
|
||||
};
|
||||
|
||||
mod container_state;
|
||||
|
||||
@@ -11,10 +13,10 @@ pub use container_state::*;
|
||||
/// Global app_state, stored in an Arc<Mutex>
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppData {
|
||||
containers: StatefulList<ContainerItem>,
|
||||
error: Option<AppError>,
|
||||
sorted_by: Option<(Header, SortedOrder)>,
|
||||
pub args: CliArgs,
|
||||
pub containers: StatefulList<ContainerItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
@@ -69,6 +71,34 @@ impl AppData {
|
||||
self.sorted_by
|
||||
}
|
||||
|
||||
pub fn has_containers(&self) -> bool {
|
||||
self.containers.items.is_empty()
|
||||
}
|
||||
|
||||
pub fn container_title(&self) -> String {
|
||||
self.containers.get_state_title()
|
||||
}
|
||||
|
||||
/// Select the first container
|
||||
pub fn containers_start(&mut self) {
|
||||
self.containers.start();
|
||||
}
|
||||
|
||||
// select the last container
|
||||
pub fn containers_end(&mut self) {
|
||||
self.containers.end();
|
||||
}
|
||||
|
||||
/// Select the next container
|
||||
pub fn containers_next(&mut self) {
|
||||
self.containers.next();
|
||||
}
|
||||
|
||||
// select the previous container
|
||||
pub fn containers_previous(&mut self) {
|
||||
self.containers.previous();
|
||||
}
|
||||
|
||||
/// Remove the sorted header & order, and sort by default - created datetime
|
||||
pub fn reset_sorted(&mut self) {
|
||||
self.set_sorted(None);
|
||||
@@ -109,57 +139,124 @@ impl AppData {
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
/// Get the current select docker command
|
||||
/// Get the current selected docker command
|
||||
/// So know which command to execute
|
||||
pub fn get_docker_command(&self) -> Option<DockerControls> {
|
||||
let mut output = None;
|
||||
if let Some(index) = self.containers.state.selected() {
|
||||
if let Some(control_index) = self.containers.items[index]
|
||||
.docker_controls
|
||||
pub fn selected_docker_command(&self) -> Option<DockerControls> {
|
||||
self.get_selected_container().and_then(|i| {
|
||||
i.docker_controls.state.selected().and_then(|x| {
|
||||
i.docker_controls
|
||||
.items
|
||||
.get(x)
|
||||
.map(std::borrow::ToOwned::to_owned)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get Option of the current selected container
|
||||
pub fn get_selected_container(&self) -> Option<&ContainerItem> {
|
||||
self.containers
|
||||
.state
|
||||
.selected()
|
||||
{
|
||||
output = Some(self.containers.items[index].docker_controls.items[control_index]);
|
||||
.and_then(|i| self.containers.items.get(i))
|
||||
}
|
||||
|
||||
/// Get Option of the current selected container
|
||||
pub const fn get_container_items(&self) -> &Vec<ContainerItem> {
|
||||
&self.containers.items
|
||||
}
|
||||
output
|
||||
|
||||
pub fn get_container_state(&mut self) -> &mut ListState {
|
||||
&mut self.containers.state
|
||||
}
|
||||
|
||||
/// Get mutable Option of the current selected container
|
||||
fn get_mut_selected_container(&mut self) -> Option<&mut ContainerItem> {
|
||||
self.containers
|
||||
.state
|
||||
.selected()
|
||||
.and_then(|i| self.containers.items.get_mut(i))
|
||||
}
|
||||
|
||||
/// Get mutable Option of the currently selected container chart data
|
||||
pub fn get_chart_data(&mut self) -> Option<(CpuTuple, MemTuple)> {
|
||||
self.containers
|
||||
.state
|
||||
.selected()
|
||||
.and_then(|i| self.containers.items.get_mut(i))
|
||||
.map(|i| i.get_chart_data())
|
||||
}
|
||||
|
||||
/// Get mutable Vec of current containers logs
|
||||
pub fn get_logs(&mut self) -> Vec<ListItem<'static>> {
|
||||
self.containers
|
||||
.state
|
||||
.selected()
|
||||
.and_then(|i| self.containers.items.get_mut(i))
|
||||
.map_or(vec![], |i| i.logs.to_vec())
|
||||
}
|
||||
|
||||
/// Get mutable Option of the currently selected container Logs state
|
||||
pub fn get_log_state(&mut self) -> Option<&mut ListState> {
|
||||
self.containers
|
||||
.state
|
||||
.selected()
|
||||
.and_then(|i| self.containers.items.get_mut(i))
|
||||
.map(|i| i.logs.state())
|
||||
}
|
||||
|
||||
/// Get mutable Option of the currently selected container DockerControls state
|
||||
pub fn get_control_state(&mut self) -> Option<&mut ListState> {
|
||||
self.get_mut_selected_container()
|
||||
.map(|i| &mut i.docker_controls.state)
|
||||
}
|
||||
|
||||
/// Get mutable Option of the currently selected container DockerControls items
|
||||
pub fn get_control_items(&mut self) -> Option<&mut Vec<DockerControls>> {
|
||||
self.get_mut_selected_container()
|
||||
.map(|i| &mut i.docker_controls.items)
|
||||
}
|
||||
|
||||
/// Get all containers ids
|
||||
pub fn get_all_ids(&self) -> Vec<ContainerId> {
|
||||
self.containers
|
||||
.items
|
||||
.iter()
|
||||
.map(|i| i.id.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// return a mutable container by given id
|
||||
fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> {
|
||||
self.containers.items.iter_mut().find(|i| &i.id == id)
|
||||
}
|
||||
|
||||
/// Change selected choice of docker commands of selected container
|
||||
pub fn docker_command_next(&mut self) {
|
||||
if let Some(index) = self.containers.state.selected() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.docker_controls.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Change selected choice of docker commands of selected container
|
||||
pub fn docker_command_previous(&mut self) {
|
||||
if let Some(index) = self.containers.state.selected() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.docker_controls.previous();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Change selected choice of docker commands of selected container
|
||||
pub fn docker_command_start(&mut self) {
|
||||
if let Some(index) = self.containers.state.selected() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.docker_controls.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Change selected choice of docker commands of selected container
|
||||
pub fn docker_command_end(&mut self) {
|
||||
if let Some(index) = self.containers.state.selected() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.docker_controls.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// return single app_state error
|
||||
pub const fn get_error(&self) -> Option<AppError> {
|
||||
@@ -180,25 +277,14 @@ impl AppData {
|
||||
/// If any containers on system, will always return a ContainerId
|
||||
/// Only returns None when no containers found.
|
||||
pub fn get_selected_container_id(&self) -> Option<ContainerId> {
|
||||
let mut output = None;
|
||||
if let Some(index) = self.containers.state.selected() {
|
||||
if let Some(x) = self.containers.items.get(index) {
|
||||
output = Some(x.id.clone());
|
||||
}
|
||||
}
|
||||
output
|
||||
self.get_selected_container().map(|i| i.id.clone())
|
||||
}
|
||||
|
||||
/// Check if the selected container is a dockerised version of oxker
|
||||
/// So that can disallow commands to be send
|
||||
/// Is a shabby way of implementing this
|
||||
pub fn selected_container_is_oxker(&self) -> bool {
|
||||
if let Some(index) = self.containers.state.selected() {
|
||||
if let Some(x) = self.containers.items.get(index) {
|
||||
return x.is_oxker;
|
||||
}
|
||||
}
|
||||
false
|
||||
self.get_selected_container().map_or(false, |i| i.is_oxker)
|
||||
}
|
||||
|
||||
/// Sort the containers vec, based on a heading, either ascending or descending,
|
||||
@@ -276,26 +362,14 @@ impl AppData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the index of the currently selected single log line
|
||||
pub fn get_selected_log_index(&self) -> Option<usize> {
|
||||
let mut output = None;
|
||||
if let Some(id) = self.get_selected_container_id() {
|
||||
if let Some(index) = self.containers.items.iter().position(|i| i.id == id) {
|
||||
output = Some(index);
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
/// Get the title for log panel for selected container
|
||||
/// will be either
|
||||
/// Get the title for log panel for selected container, will be either
|
||||
/// 1) "logs x/x - container_name" where container_name is 32 chars max
|
||||
/// 2) "logs - container_name" when no logs found, again 32 chars max
|
||||
/// 3) "" no container currently selected - aka no containers on system
|
||||
pub fn get_log_title(&self) -> String {
|
||||
self.get_selected_log_index()
|
||||
.map_or(String::new(), |index| {
|
||||
let logs_len = self.containers.items[index].logs.get_state_title();
|
||||
let mut name = self.containers.items[index].name.clone();
|
||||
self.get_selected_container().map_or_else(String::new, |y| {
|
||||
let logs_len = y.logs.get_state_title();
|
||||
let mut name = y.name.clone();
|
||||
name.truncate(32);
|
||||
if logs_len.is_empty() {
|
||||
format!("- {name} ")
|
||||
@@ -307,39 +381,31 @@ impl AppData {
|
||||
|
||||
/// select next selected log line
|
||||
pub fn log_next(&mut self) {
|
||||
if let Some(index) = self.get_selected_log_index() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.logs.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// select previous selected log line
|
||||
pub fn log_previous(&mut self) {
|
||||
if let Some(index) = self.get_selected_log_index() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.logs.previous();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// select last selected log line
|
||||
pub fn log_end(&mut self) {
|
||||
if let Some(index) = self.get_selected_log_index() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.logs.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// select first selected log line
|
||||
pub fn log_start(&mut self) {
|
||||
if let Some(index) = self.get_selected_log_index() {
|
||||
if let Some(i) = self.containers.items.get_mut(index) {
|
||||
if let Some(i) = self.get_mut_selected_container() {
|
||||
i.logs.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the initial parsing has been completed, by making sure that all ids given (which are running) have a non empty cpu_stats vecdec
|
||||
pub fn initialised(&mut self, all_ids: &[(bool, ContainerId)]) -> bool {
|
||||
@@ -361,7 +427,7 @@ impl AppData {
|
||||
/// Find the widths for the strings in the containers panel.
|
||||
/// So can display nicely and evenly
|
||||
pub fn get_width(&self) -> Columns {
|
||||
let mut output = Columns::new();
|
||||
let mut columns = Columns::new();
|
||||
let count = |x: &String| u8::try_from(x.chars().count()).unwrap_or(12);
|
||||
|
||||
// Should probably find a refactor here somewhere
|
||||
@@ -389,49 +455,35 @@ impl AppData {
|
||||
);
|
||||
let mem_limit_count = count(&container.mem_limit.to_string());
|
||||
|
||||
if cpu_count > output.cpu.1 {
|
||||
output.cpu.1 = cpu_count;
|
||||
if cpu_count > columns.cpu.1 {
|
||||
columns.cpu.1 = cpu_count;
|
||||
};
|
||||
if image_count > output.image.1 {
|
||||
output.image.1 = image_count;
|
||||
if image_count > columns.image.1 {
|
||||
columns.image.1 = image_count;
|
||||
};
|
||||
if mem_current_count > output.mem.1 {
|
||||
output.mem.1 = mem_current_count;
|
||||
if mem_current_count > columns.mem.1 {
|
||||
columns.mem.1 = mem_current_count;
|
||||
};
|
||||
if mem_limit_count > output.mem.2 {
|
||||
output.mem.2 = mem_limit_count;
|
||||
if mem_limit_count > columns.mem.2 {
|
||||
columns.mem.2 = mem_limit_count;
|
||||
};
|
||||
if name_count > output.name.1 {
|
||||
output.name.1 = name_count;
|
||||
if name_count > columns.name.1 {
|
||||
columns.name.1 = name_count;
|
||||
};
|
||||
if state_count > output.state.1 {
|
||||
output.state.1 = state_count;
|
||||
if state_count > columns.state.1 {
|
||||
columns.state.1 = state_count;
|
||||
};
|
||||
if status_count > output.status.1 {
|
||||
output.status.1 = status_count;
|
||||
if status_count > columns.status.1 {
|
||||
columns.status.1 = status_count;
|
||||
};
|
||||
if rx_count > output.net_rx.1 {
|
||||
output.net_rx.1 = rx_count;
|
||||
if rx_count > columns.net_rx.1 {
|
||||
columns.net_rx.1 = rx_count;
|
||||
};
|
||||
if tx_count > output.net_tx.1 {
|
||||
output.net_tx.1 = tx_count;
|
||||
if tx_count > columns.net_tx.1 {
|
||||
columns.net_tx.1 = tx_count;
|
||||
};
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
/// Get all containers ids
|
||||
pub fn get_all_ids(&self) -> Vec<ContainerId> {
|
||||
self.containers
|
||||
.items
|
||||
.iter()
|
||||
.map(|i| i.id.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// return a mutable container by given id
|
||||
fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> {
|
||||
self.containers.items.iter_mut().find(|i| &i.id == id)
|
||||
columns
|
||||
}
|
||||
|
||||
/// Update container mem, cpu, & network stats, in single function so only need to call .lock() once
|
||||
@@ -563,7 +615,7 @@ impl AppData {
|
||||
}
|
||||
|
||||
/// update logs of a given container, based on id
|
||||
pub fn update_log_by_id(&mut self, output: Vec<String>, id: &ContainerId) {
|
||||
pub fn update_log_by_id(&mut self, logs: Vec<String>, id: &ContainerId) {
|
||||
let color = self.args.color;
|
||||
let raw = self.args.raw;
|
||||
|
||||
@@ -573,7 +625,7 @@ impl AppData {
|
||||
container.last_updated = Self::get_systemtime();
|
||||
let current_len = container.logs.len();
|
||||
|
||||
for mut i in output {
|
||||
for mut i in logs {
|
||||
let tz = LogsTz::from(&i);
|
||||
// Strip the timestamp if `-t` flag set
|
||||
if !timestamp {
|
||||
|
||||
+5
-12
@@ -267,25 +267,18 @@ impl DockerData {
|
||||
/// Update all cpu_mem, and selected container log (if a log update join_handle isn't currently being executed)
|
||||
async fn update_everything(&mut self) {
|
||||
let all_ids = self.update_all_containers().await;
|
||||
let optional_index = self.app_data.lock().get_selected_log_index();
|
||||
if let Some(index) = optional_index {
|
||||
if let Some(container) = self.app_data.lock().containers.items.get(index) {
|
||||
if let Some(container) = self.app_data.lock().get_selected_container() {
|
||||
let id = container.id.clone();
|
||||
let last_updated = container.last_updated;
|
||||
self.spawns
|
||||
.lock()
|
||||
.entry(SpawnId::Log(container.id.clone()))
|
||||
.entry(SpawnId::Log(id.clone()))
|
||||
.or_insert_with(|| {
|
||||
let docker = Arc::clone(&self.docker);
|
||||
let app_data = Arc::clone(&self.app_data);
|
||||
let spawns = Arc::clone(&self.spawns);
|
||||
tokio::spawn(Self::update_log(
|
||||
app_data,
|
||||
docker,
|
||||
container.id.clone(),
|
||||
container.last_updated,
|
||||
spawns,
|
||||
))
|
||||
tokio::spawn(Self::update_log(app_data, docker, id, last_updated, spawns))
|
||||
});
|
||||
}
|
||||
};
|
||||
self.update_all_container_stats(&all_ids);
|
||||
self.app_data.lock().sort_containers();
|
||||
|
||||
@@ -207,7 +207,7 @@ impl InputHandler {
|
||||
KeyCode::Home => {
|
||||
let mut locked_data = self.app_data.lock();
|
||||
match self.gui_state.lock().selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers.start(),
|
||||
SelectablePanel::Containers => locked_data.containers_start(),
|
||||
SelectablePanel::Logs => locked_data.log_start(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_start(),
|
||||
}
|
||||
@@ -215,7 +215,7 @@ impl InputHandler {
|
||||
KeyCode::End => {
|
||||
let mut locked_data = self.app_data.lock();
|
||||
match self.gui_state.lock().selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers.end(),
|
||||
SelectablePanel::Containers => locked_data.containers_end(),
|
||||
SelectablePanel::Logs => locked_data.log_end(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_end(),
|
||||
}
|
||||
@@ -236,7 +236,7 @@ impl InputHandler {
|
||||
// This isn't great, just means you can't send docker commands before full initialization of the program
|
||||
let panel = self.gui_state.lock().selected_panel;
|
||||
if panel == SelectablePanel::Commands {
|
||||
let option_command = self.app_data.lock().get_docker_command();
|
||||
let option_command = self.app_data.lock().selected_docker_command();
|
||||
|
||||
if let Some(command) = option_command {
|
||||
let option_id = self.app_data.lock().get_selected_container_id();
|
||||
@@ -287,14 +287,12 @@ impl InputHandler {
|
||||
MouseEventKind::ScrollUp => self.previous(),
|
||||
MouseEventKind::ScrollDown => self.next(),
|
||||
MouseEventKind::Down(MouseButton::Left) => {
|
||||
let header_intersects = self.gui_state.lock().header_intersect(Rect::new(
|
||||
if let Some(header) = self.gui_state.lock().header_intersect(Rect::new(
|
||||
mouse_event.column,
|
||||
mouse_event.row,
|
||||
1,
|
||||
1,
|
||||
));
|
||||
|
||||
if let Some(header) = header_intersects {
|
||||
)) {
|
||||
self.sort(header);
|
||||
}
|
||||
|
||||
@@ -313,7 +311,7 @@ impl InputHandler {
|
||||
fn next(&mut self) {
|
||||
let mut locked_data = self.app_data.lock();
|
||||
match self.gui_state.lock().selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers.next(),
|
||||
SelectablePanel::Containers => locked_data.containers_next(),
|
||||
SelectablePanel::Logs => locked_data.log_next(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_next(),
|
||||
};
|
||||
@@ -323,7 +321,7 @@ impl InputHandler {
|
||||
fn previous(&mut self) {
|
||||
let mut locked_data = self.app_data.lock();
|
||||
match self.gui_state.lock().selected_panel {
|
||||
SelectablePanel::Containers => locked_data.containers.previous(),
|
||||
SelectablePanel::Containers => locked_data.containers_previous(),
|
||||
SelectablePanel::Logs => locked_data.log_previous(),
|
||||
SelectablePanel::Commands => locked_data.docker_command_previous(),
|
||||
}
|
||||
|
||||
+32
-53
@@ -57,11 +57,7 @@ fn generate_block<'a>(
|
||||
let current_selected_panel = gui_state.lock().selected_panel;
|
||||
let mut title = match panel {
|
||||
SelectablePanel::Containers => {
|
||||
format!(
|
||||
"{} {}",
|
||||
panel.title(),
|
||||
app_data.lock().containers.get_state_title()
|
||||
)
|
||||
format!("{} {}", panel.title(), app_data.lock().container_title())
|
||||
}
|
||||
SelectablePanel::Logs => {
|
||||
format!("{} {}", panel.title(), app_data.lock().get_log_title())
|
||||
@@ -87,35 +83,31 @@ pub fn commands<B: Backend>(
|
||||
area: Rect,
|
||||
f: &mut Frame<'_, B>,
|
||||
gui_state: &Arc<Mutex<GuiState>>,
|
||||
index: Option<usize>,
|
||||
) {
|
||||
let block = generate_block(app_data, area, gui_state, SelectablePanel::Commands);
|
||||
if let Some(i) = index {
|
||||
let items = app_data.lock().containers.items[i]
|
||||
.docker_controls
|
||||
.items
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let block = || generate_block(app_data, area, gui_state, SelectablePanel::Commands);
|
||||
let items = app_data.lock().get_control_items().map_or(vec![], |i| {
|
||||
i.iter()
|
||||
.map(|c| {
|
||||
let lines = Spans::from(vec![Span::styled(
|
||||
i.to_string(),
|
||||
Style::default().fg(i.get_color()),
|
||||
c.to_string(),
|
||||
Style::default().fg(c.get_color()),
|
||||
)]);
|
||||
ListItem::new(lines)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
let items = List::new(items)
|
||||
.block(block)
|
||||
.block(block())
|
||||
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
|
||||
.highlight_symbol(ARROW);
|
||||
|
||||
f.render_stateful_widget(
|
||||
items,
|
||||
area,
|
||||
&mut app_data.lock().containers.items[i].docker_controls.state,
|
||||
);
|
||||
if let Some(i) = app_data.lock().get_control_state() {
|
||||
f.render_stateful_widget(items, area, i);
|
||||
} else {
|
||||
let paragraph = Paragraph::new("").block(block).alignment(Alignment::Center);
|
||||
let paragraph = Paragraph::new("")
|
||||
.block(block())
|
||||
.alignment(Alignment::Center);
|
||||
f.render_widget(paragraph, area);
|
||||
}
|
||||
}
|
||||
@@ -132,8 +124,7 @@ pub fn containers<B: Backend>(
|
||||
|
||||
let items = app_data
|
||||
.lock()
|
||||
.containers
|
||||
.items
|
||||
.get_container_items()
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let state_style = Style::default().fg(i.state.get_color());
|
||||
@@ -204,6 +195,7 @@ pub fn containers<B: Backend>(
|
||||
ListItem::new(lines)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if items.is_empty() {
|
||||
let paragraph = Paragraph::new("no containers running")
|
||||
.block(block)
|
||||
@@ -215,7 +207,7 @@ pub fn containers<B: Backend>(
|
||||
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
|
||||
.highlight_symbol(CIRCLE);
|
||||
|
||||
f.render_stateful_widget(items, area, &mut app_data.lock().containers.state);
|
||||
f.render_stateful_widget(items, area, app_data.lock().get_container_state());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,51 +217,41 @@ pub fn logs<B: Backend>(
|
||||
area: Rect,
|
||||
f: &mut Frame<'_, B>,
|
||||
gui_state: &Arc<Mutex<GuiState>>,
|
||||
index: Option<usize>,
|
||||
loading_icon: &str,
|
||||
) {
|
||||
let block = generate_block(app_data, area, gui_state, SelectablePanel::Logs);
|
||||
let contains_init = gui_state.lock().status_contains(&[Status::Init]);
|
||||
if contains_init {
|
||||
let block = || generate_block(app_data, area, gui_state, SelectablePanel::Logs);
|
||||
if gui_state.lock().status_contains(&[Status::Init]) {
|
||||
let paragraph = Paragraph::new(format!("parsing logs {loading_icon}"))
|
||||
.style(Style::default())
|
||||
.block(block)
|
||||
.block(block())
|
||||
.alignment(Alignment::Center);
|
||||
f.render_widget(paragraph, area);
|
||||
} else if let Some(index) = index {
|
||||
let items = List::new(app_data.lock().containers.items[index].logs.to_vec())
|
||||
.block(block)
|
||||
} else {
|
||||
let items = app_data.lock().get_logs();
|
||||
let items = List::new(items)
|
||||
.block(block())
|
||||
.highlight_symbol(ARROW)
|
||||
.highlight_style(Style::default().add_modifier(Modifier::BOLD));
|
||||
f.render_stateful_widget(
|
||||
items,
|
||||
area,
|
||||
app_data.lock().containers.items[index].logs.state(),
|
||||
);
|
||||
|
||||
if let Some(i) = app_data.lock().get_log_state() {
|
||||
f.render_stateful_widget(items, area, i);
|
||||
} else {
|
||||
let paragraph = Paragraph::new("no logs found")
|
||||
.block(block)
|
||||
.block(block())
|
||||
.alignment(Alignment::Center);
|
||||
f.render_widget(paragraph, area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the cpu + mem charts
|
||||
pub fn chart<B: Backend>(
|
||||
f: &mut Frame<'_, B>,
|
||||
area: Rect,
|
||||
app_data: &Arc<Mutex<AppData>>,
|
||||
index: Option<usize>,
|
||||
) {
|
||||
if let Some(index) = index {
|
||||
pub fn chart<B: Backend>(f: &mut Frame<'_, B>, area: Rect, app_data: &Arc<Mutex<AppData>>) {
|
||||
if let Some((cpu, mem)) = app_data.lock().get_chart_data() {
|
||||
let area = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.split(area);
|
||||
|
||||
// Check is some, else can cause out of bounds error, if containers get removed before a docker update
|
||||
if let Some(data) = app_data.lock().containers.items.get(index) {
|
||||
let (cpu, mem) = data.get_chart_data();
|
||||
let cpu_dataset = vec![Dataset::default()
|
||||
.marker(symbols::Marker::Dot)
|
||||
.style(Style::default().fg(Color::Magenta))
|
||||
@@ -288,7 +270,6 @@ pub fn chart<B: Backend>(
|
||||
f.render_widget(cpu_chart, area[0]);
|
||||
f.render_widget(mem_chart, area[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create charts
|
||||
@@ -490,8 +471,6 @@ pub fn heading_bar<B: Backend>(
|
||||
|
||||
// If no containers, don't display the headers, could maybe do this first?
|
||||
let help_index = if has_containers { 2 } else { 0 };
|
||||
// render help info
|
||||
|
||||
f.render_widget(help_paragraph, split_bar[help_index]);
|
||||
}
|
||||
|
||||
|
||||
@@ -242,6 +242,7 @@ impl GuiState {
|
||||
}
|
||||
|
||||
/// Check if the current gui_status contains any of the given status'
|
||||
/// Don't really like this methodology for gui state, needs a re-think
|
||||
pub fn status_contains(&self, status: &[Status]) -> bool {
|
||||
status.iter().any(|i| self.status.contains(i))
|
||||
}
|
||||
|
||||
+4
-12
@@ -141,9 +141,8 @@ fn ui<B: Backend>(
|
||||
let height = if height < 12 { height + 4 } else { 12 };
|
||||
|
||||
let column_widths = app_data.lock().get_width();
|
||||
let has_containers = !app_data.lock().containers.items.is_empty();
|
||||
let has_containers = !app_data.lock().has_containers();
|
||||
let has_error = app_data.lock().get_error();
|
||||
let log_index = app_data.lock().get_selected_log_index();
|
||||
let sorted_by = app_data.lock().get_sorted();
|
||||
|
||||
let show_help = gui_state.lock().status_contains(&[Status::Help]);
|
||||
@@ -193,17 +192,10 @@ fn ui<B: Backend>(
|
||||
draw_blocks::containers(app_data, top_panel[0], f, gui_state, &column_widths);
|
||||
|
||||
if has_containers {
|
||||
draw_blocks::commands(app_data, top_panel[1], f, gui_state, log_index);
|
||||
draw_blocks::commands(app_data, top_panel[1], f, gui_state);
|
||||
}
|
||||
|
||||
draw_blocks::logs(
|
||||
app_data,
|
||||
lower_main[0],
|
||||
f,
|
||||
gui_state,
|
||||
log_index,
|
||||
&loading_icon,
|
||||
);
|
||||
draw_blocks::logs(app_data, lower_main[0], f, gui_state, &loading_icon);
|
||||
|
||||
draw_blocks::heading_bar(
|
||||
whole_layout[0],
|
||||
@@ -217,7 +209,7 @@ fn ui<B: Backend>(
|
||||
|
||||
// only draw charts if there are containers
|
||||
if has_containers {
|
||||
draw_blocks::chart(f, lower_main[1], app_data, log_index);
|
||||
draw_blocks::chart(f, lower_main[1], app_data);
|
||||
}
|
||||
|
||||
if let Some(info) = info_text {
|
||||
|
||||
Reference in New Issue
Block a user