chore: merge release-v0.2.1 into main
This commit is contained in:
+14
-12
@@ -1,20 +1,22 @@
|
||||
### 2023-01-21
|
||||
### 2023-01-29
|
||||
|
||||
### Chores
|
||||
+ dependencies updated, [8cd199db49186fad6ce432bb277e3a10f0a08d34], [d880b829c123dbe57deccadef97810e45c083737], [66d57c99558ca14d9593d6dbfd5b0e8e5d59055d], [33f9374908942f4a3b90be227fad94ca353cf351], [007d5d83d7f1b93e1e78777a4417b2740db706bd]
|
||||
+ create_release.sh typos, [9a27d46a044452080144ee1367dc95886b10abf8]
|
||||
+ dev container post create install cross, [2d253f034182741d434e4bac12317f24221d0d4a]
|
||||
+ dependencies updated, [c129f474fe2976454b1868d00e8d7d99b87ec23b], [9788b8afd98e59b1d4412a8adc54b34d2c5671fd], [2ab88eb26e9bbbc4dad4651256d8d9b044ea3272]
|
||||
|
||||
### Features
|
||||
**all potentially considered breaking changes**
|
||||
+ store Logs in own struct, use a hashset to track timestamps, hopefully closes #11, [657ea2d751a71f05b17547b47c492d5676817336]
|
||||
+ Spawn docker commands into own thread, can now execute multiple docker commands at the same time, [9ec43e124a62a80f4e78acba85fc3af5980ce260]
|
||||
+ align memory columns correctly, minimum byte display value now `0.00 kB`, rather than `0 B`, closes #20, [bd7dfcd2c512a527d66a1388f90006988a487186], [51c580010a24de2427373795803936d498dc8cee]
|
||||
### Docs
|
||||
+ comment typo, [1025579138f11e4987263c7bfe936c4c8542f8b3]
|
||||
|
||||
### Fixes
|
||||
+ deadlock on draw logs when no containers found, [68e444bfc393eb46bac2b99eb57697bb9b0451af]
|
||||
+ github workflow release on main only (with semver tag), [e4ca41dfd8ec3acae202a2d2464b8e18f5c5bdd5], [749ec712f07cff2c941aed6726c56bdbd5cb8d2c]
|
||||
|
||||
### Refactors
|
||||
+ main.rs tidy up, [97b89349dc2de275ca514a1e6420255a63d775e8]
|
||||
+ derive Default for GuiState, [9dcd0509efeb464f58fb53d813bd78de2447949d]
|
||||
+ param reduction, AtomicBool to Relaxed, [0350293de3c00c6e5e5d787b7596bb3413d1cda1]
|
||||
+ major refactor of internal data handling, [b4488e4bdb0252f5c5680cee6a46427f22a282ab]
|
||||
+ needless (double) referencing removed, [a174dafe1b05908735680a874dc551a86da24777]
|
||||
+ app_data methods re-ordered & renamed, [c0bb5355d6a5d352260655110ce3d5ab695acda9]
|
||||
|
||||
### Reverts
|
||||
+ is_running AtomicBool back to SeqCst, [c4d80061dab94afd08d4d793dc147f878c965ad6]
|
||||
|
||||
|
||||
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
name: Release CI
|
||||
on:
|
||||
push:
|
||||
branch:
|
||||
- 'main'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.2.1'>v0.2.1</a>
|
||||
### 2023-01-29
|
||||
|
||||
### Chores
|
||||
+ dependencies updated, [c129f474](https://github.com/mrjackwills/oxker/commit/c129f474fe2976454b1868d00e8d7d99b87ec23b), [9788b8af](https://github.com/mrjackwills/oxker/commit/9788b8afd98e59b1d4412a8adc54b34d2c5671fd), [2ab88eb2](https://github.com/mrjackwills/oxker/commit/2ab88eb26e9bbbc4dad4651256d8d9b044ea3272)
|
||||
|
||||
### Docs
|
||||
+ comment typo, [10255791](https://github.com/mrjackwills/oxker/commit/1025579138f11e4987263c7bfe936c4c8542f8b3)
|
||||
|
||||
### Fixes
|
||||
+ deadlock on draw logs when no containers found, [68e444bf](https://github.com/mrjackwills/oxker/commit/68e444bfc393eb46bac2b99eb57697bb9b0451af)
|
||||
+ github workflow release on main only (with semver tag), [e4ca41df](https://github.com/mrjackwills/oxker/commit/e4ca41dfd8ec3acae202a2d2464b8e18f5c5bdd5), [749ec712](https://github.com/mrjackwills/oxker/commit/749ec712f07cff2c941aed6726c56bdbd5cb8d2c)
|
||||
|
||||
### Refactors
|
||||
+ major refactor of internal data handling, [b4488e4b](https://github.com/mrjackwills/oxker/commit/b4488e4bdb0252f5c5680cee6a46427f22a282ab)
|
||||
+ needless (double) referencing removed, [a174dafe](https://github.com/mrjackwills/oxker/commit/a174dafe1b05908735680a874dc551a86da24777)
|
||||
+ app_data methods re-ordered & renamed, [c0bb5355](https://github.com/mrjackwills/oxker/commit/c0bb5355d6a5d352260655110ce3d5ab695acda9)
|
||||
|
||||
### Reverts
|
||||
+ is_running AtomicBool back to SeqCst, [c4d80061](https://github.com/mrjackwills/oxker/commit/c4d80061dab94afd08d4d793dc147f878c965ad6)
|
||||
|
||||
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.2.0'>v0.2.0</a>
|
||||
### 2023-01-21
|
||||
|
||||
|
||||
Generated
+31
-15
@@ -106,9 +106,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -131,9 +131,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.1"
|
||||
version = "4.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2"
|
||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
@@ -200,6 +200,22 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77f67c7faacd4db07a939f55d66a983a5355358a1f17d32cc9a8d01d1266b9ce"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.0"
|
||||
@@ -211,9 +227,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e"
|
||||
checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -223,9 +239,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200"
|
||||
checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -238,15 +254,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea"
|
||||
checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e"
|
||||
checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -674,13 +690,13 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "oxker"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
"cansi",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"crossterm 0.26.0",
|
||||
"futures-util",
|
||||
"parking_lot",
|
||||
"tokio",
|
||||
@@ -1223,7 +1239,7 @@ checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"crossterm",
|
||||
"crossterm 0.25.0",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "oxker"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
authors = ["Jack Wills <email@mrjackwills.com>"]
|
||||
description = "A simple tui to view & control docker containers"
|
||||
@@ -16,7 +16,7 @@ anyhow = "1.0"
|
||||
bollard = "0.14"
|
||||
cansi = "2.2"
|
||||
clap={version="4.1", features = ["derive", "unicode", "color"] }
|
||||
crossterm = "0.25"
|
||||
crossterm = "0.26"
|
||||
futures-util = "0.3"
|
||||
parking_lot = {version= "0.12"}
|
||||
tokio = {version = "1.24", features=["full"]}
|
||||
|
||||
+239
-180
@@ -1,7 +1,7 @@
|
||||
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 +11,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)]
|
||||
@@ -55,39 +55,6 @@ impl fmt::Display for Header {
|
||||
}
|
||||
|
||||
impl AppData {
|
||||
/// Generate a default app_state
|
||||
pub fn default(args: CliArgs) -> Self {
|
||||
Self {
|
||||
args,
|
||||
containers: StatefulList::new(vec![]),
|
||||
error: None,
|
||||
sorted_by: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn get_sorted(&self) -> Option<(Header, SortedOrder)> {
|
||||
self.sorted_by
|
||||
}
|
||||
|
||||
/// Remove the sorted header & order, and sort by default - created datetime
|
||||
pub fn reset_sorted(&mut self) {
|
||||
self.set_sorted(None);
|
||||
}
|
||||
|
||||
/// Sort containers based on a given header, if headings match, and already ascending, remove sorting
|
||||
pub fn set_sort_by_header(&mut self, selected_header: Header) {
|
||||
let mut output = Some((selected_header, SortedOrder::Asc));
|
||||
if let Some((current_header, order)) = self.get_sorted() {
|
||||
if current_header == selected_header {
|
||||
match order {
|
||||
SortedOrder::Desc => output = None,
|
||||
SortedOrder::Asc => output = Some((selected_header, SortedOrder::Desc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
self.set_sorted(output);
|
||||
}
|
||||
|
||||
/// Change the sorted order, also set the selected container state to match new order
|
||||
fn set_sorted(&mut self, x: Option<(Header, SortedOrder)>) {
|
||||
self.sorted_by = x;
|
||||
@@ -109,96 +76,39 @@ impl AppData {
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
/// Get the current select 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
|
||||
.state
|
||||
.selected()
|
||||
{
|
||||
output = Some(self.containers.items[index].docker_controls.items[control_index]);
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
i.docker_controls.next();
|
||||
}
|
||||
/// Generate a default app_state
|
||||
pub fn default(args: CliArgs) -> Self {
|
||||
Self {
|
||||
args,
|
||||
containers: StatefulList::new(vec![]),
|
||||
error: None,
|
||||
sorted_by: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
i.docker_controls.previous();
|
||||
}
|
||||
}
|
||||
/// Container sort related methods
|
||||
|
||||
/// Remove the sorted header & order, and sort by default - created datetime
|
||||
pub fn reset_sorted(&mut self) {
|
||||
self.set_sorted(None);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
i.docker_controls.start();
|
||||
/// Sort containers based on a given header, if headings match, and already ascending, remove sorting
|
||||
pub fn set_sort_by_header(&mut self, selected_header: Header) {
|
||||
let mut output = Some((selected_header, SortedOrder::Asc));
|
||||
if let Some((current_header, order)) = self.get_sorted() {
|
||||
if current_header == selected_header {
|
||||
match order {
|
||||
SortedOrder::Desc => output = None,
|
||||
SortedOrder::Asc => output = Some((selected_header, SortedOrder::Desc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
self.set_sorted(output);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
i.docker_controls.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// return single app_state error
|
||||
pub const fn get_error(&self) -> Option<AppError> {
|
||||
self.error
|
||||
}
|
||||
|
||||
/// remove single app_state error
|
||||
pub fn remove_error(&mut self) {
|
||||
self.error = None;
|
||||
}
|
||||
|
||||
/// insert single app_state error
|
||||
pub fn set_error(&mut self, error: AppError) {
|
||||
self.error = Some(error);
|
||||
}
|
||||
|
||||
/// Find the id of the currently selected container.
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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
|
||||
pub const fn get_sorted(&self) -> Option<(Header, SortedOrder)> {
|
||||
self.sorted_by
|
||||
}
|
||||
|
||||
/// Sort the containers vec, based on a heading, either ascending or descending,
|
||||
@@ -276,26 +186,128 @@ 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
|
||||
/// Container state methods
|
||||
|
||||
/// Just get the total number of containers
|
||||
pub fn get_container_len(&self) -> usize {
|
||||
self.containers.items.len()
|
||||
}
|
||||
|
||||
/// Get the title for log panel for selected container
|
||||
/// will be either
|
||||
/// Get title for containers section
|
||||
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();
|
||||
}
|
||||
|
||||
/// Get Container items
|
||||
pub const fn get_container_items(&self) -> &Vec<ContainerItem> {
|
||||
&self.containers.items
|
||||
}
|
||||
|
||||
/// Get Option of the current selected container
|
||||
pub fn get_selected_container(&self) -> Option<&ContainerItem> {
|
||||
self.containers
|
||||
.state
|
||||
.selected()
|
||||
.and_then(|i| self.containers.items.get(i))
|
||||
}
|
||||
|
||||
/// 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 ListState of containers
|
||||
pub fn get_container_state(&mut self) -> &mut ListState {
|
||||
&mut self.containers.state
|
||||
}
|
||||
|
||||
/// Selected DockerCommand methods
|
||||
|
||||
/// Get the current selected docker command
|
||||
/// So know which command to execute
|
||||
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 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)
|
||||
}
|
||||
|
||||
/// Change selected choice of docker commands of selected container
|
||||
pub fn docker_command_next(&mut self) {
|
||||
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(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(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(i) = self.get_mut_selected_container() {
|
||||
i.docker_controls.end();
|
||||
}
|
||||
}
|
||||
|
||||
/// Logs related methods
|
||||
|
||||
/// 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,38 +319,85 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
/// Chart data related methods
|
||||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// Logs related methods
|
||||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// Error realted methods
|
||||
|
||||
/// return single app_state error
|
||||
pub const fn get_error(&self) -> Option<AppError> {
|
||||
self.error
|
||||
}
|
||||
|
||||
/// remove single app_state error
|
||||
pub fn remove_error(&mut self) {
|
||||
self.error = None;
|
||||
}
|
||||
|
||||
/// insert single app_state error
|
||||
pub fn set_error(&mut self, error: AppError) {
|
||||
self.error = Some(error);
|
||||
}
|
||||
|
||||
/// 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 is_oxker(&self) -> bool {
|
||||
self.get_selected_container().map_or(false, |i| i.is_oxker)
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -353,15 +412,10 @@ impl AppData {
|
||||
count_is_running == number_with_cpu_status
|
||||
}
|
||||
|
||||
/// Just get the total number of containers
|
||||
pub fn get_container_len(&self) -> usize {
|
||||
self.containers.items.len()
|
||||
}
|
||||
|
||||
/// 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,51 +443,51 @@ 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
|
||||
columns
|
||||
}
|
||||
|
||||
/// Get all containers ids
|
||||
pub fn get_all_ids(&self) -> Vec<ContainerId> {
|
||||
self.containers
|
||||
.items
|
||||
.iter()
|
||||
.map(|i| i.id.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
/// Update related methods
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Find the id of the currently selected container.
|
||||
/// 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> {
|
||||
self.get_selected_container().map(|i| i.id.clone())
|
||||
}
|
||||
|
||||
/// Update container mem, cpu, & network stats, in single function so only need to call .lock() once
|
||||
/// Will also, if a sort is set, sort the containers
|
||||
pub fn update_stats(
|
||||
@@ -465,14 +519,19 @@ impl AppData {
|
||||
container.mem_limit.update(mem_limit);
|
||||
}
|
||||
// need to benchmark this?
|
||||
if self.get_sorted().is_some() {
|
||||
// if self.get_sorted().is_some() {
|
||||
self.sort_containers();
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/// Update, or insert, containers
|
||||
pub fn update_containers(&mut self, all_containers: &mut [ContainerSummary]) {
|
||||
let all_ids = self.get_all_ids();
|
||||
let all_ids = self
|
||||
.containers
|
||||
.items
|
||||
.iter()
|
||||
.map(|i| i.id.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Only sort it no containers currently set, as afterwards the order is fixed
|
||||
if self.containers.items.is_empty() {
|
||||
@@ -563,7 +622,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 +632,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 {
|
||||
|
||||
+7
-14
@@ -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();
|
||||
@@ -293,7 +286,7 @@ impl DockerData {
|
||||
|
||||
/// Animate the loading icon
|
||||
async fn loading_spin(loading_uuid: Uuid, gui_state: &Arc<Mutex<GuiState>>) -> JoinHandle<()> {
|
||||
let gui_state = Arc::clone(&gui_state);
|
||||
let gui_state = Arc::clone(gui_state);
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
@@ -412,7 +405,7 @@ impl DockerData {
|
||||
.values()
|
||||
.into_iter()
|
||||
.for_each(tokio::task::JoinHandle::abort);
|
||||
self.is_running.store(false, Ordering::Relaxed);
|
||||
self.is_running.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-12
@@ -73,7 +73,7 @@ impl InputHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.is_running.load(Ordering::Relaxed) {
|
||||
if !self.is_running.load(Ordering::SeqCst) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ impl InputHandler {
|
||||
.lock()
|
||||
.status_contains(&[Status::Error, Status::Init]);
|
||||
if error_init || self.docker_sender.send(DockerMessage::Quit).await.is_err() {
|
||||
self.is_running.store(false, Ordering::Relaxed);
|
||||
self.is_running.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,12 +236,12 @@ 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();
|
||||
// Poor way of disallowing commands to be sent to a containerised okxer
|
||||
if self.app_data.lock().selected_container_is_oxker() {
|
||||
if self.app_data.lock().is_oxker() {
|
||||
return;
|
||||
};
|
||||
if let Some(id) = option_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(),
|
||||
}
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(clippy::unused_async, clippy::unwrap_used, clippy::expect_used)]
|
||||
// Wanring - These are indeed pedantic
|
||||
// Warning - These are indeed pedantic
|
||||
#![warn(clippy::pedantic)]
|
||||
#![warn(clippy::nursery)]
|
||||
#![allow(
|
||||
@@ -49,9 +49,9 @@ async fn docker_init(
|
||||
) {
|
||||
if let Ok(docker) = Docker::connect_with_socket_defaults() {
|
||||
if docker.ping().await.is_ok() {
|
||||
let app_data = Arc::clone(&app_data);
|
||||
let gui_state = Arc::clone(&gui_state);
|
||||
let is_running = Arc::clone(&is_running);
|
||||
let app_data = Arc::clone(app_data);
|
||||
let gui_state = Arc::clone(gui_state);
|
||||
let is_running = Arc::clone(is_running);
|
||||
tokio::spawn(DockerData::init(
|
||||
app_data, docker, docker_rx, gui_state, is_running,
|
||||
));
|
||||
@@ -66,16 +66,16 @@ async fn docker_init(
|
||||
}
|
||||
|
||||
/// Create data for, and then spawn a tokio thread, for the input handler
|
||||
async fn handler_init(
|
||||
fn handler_init(
|
||||
app_data: &Arc<Mutex<AppData>>,
|
||||
docker_sx: &Sender<DockerMessage>,
|
||||
gui_state: &Arc<Mutex<GuiState>>,
|
||||
input_rx: Receiver<InputMessages>,
|
||||
is_running: &Arc<AtomicBool>,
|
||||
) {
|
||||
let input_app_data = Arc::clone(&app_data);
|
||||
let input_gui_state = Arc::clone(&gui_state);
|
||||
let input_is_running = Arc::clone(&is_running);
|
||||
let input_app_data = Arc::clone(app_data);
|
||||
let input_gui_state = Arc::clone(gui_state);
|
||||
let input_is_running = Arc::clone(is_running);
|
||||
tokio::spawn(input_handler::InputHandler::init(
|
||||
input_app_data,
|
||||
input_rx,
|
||||
@@ -97,7 +97,7 @@ async fn main() {
|
||||
|
||||
docker_init(&app_data, docker_rx, &gui_state, &is_running).await;
|
||||
|
||||
handler_init(&app_data, &docker_sx, &gui_state, input_rx, &is_running).await;
|
||||
handler_init(&app_data, &docker_sx, &gui_state, input_rx, &is_running);
|
||||
|
||||
if args.gui {
|
||||
create_ui(app_data, docker_sx, gui_state, is_running, input_sx)
|
||||
|
||||
+41
-57
@@ -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,45 @@ 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 logs = app_data.lock().get_logs();
|
||||
|
||||
if logs.is_empty() {
|
||||
let paragraph = Paragraph::new("no logs found")
|
||||
.block(block())
|
||||
.alignment(Alignment::Center);
|
||||
f.render_widget(paragraph, area);
|
||||
} else {
|
||||
let items = List::new(logs)
|
||||
.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(),
|
||||
);
|
||||
} else {
|
||||
let paragraph = Paragraph::new("no logs found")
|
||||
.block(block)
|
||||
.alignment(Alignment::Center);
|
||||
f.render_widget(paragraph, area);
|
||||
|
||||
// This should always return Some, as logs is not empty
|
||||
if let Some(i) = app_data.lock().get_log_state() {
|
||||
f.render_stateful_widget(items, area, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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))
|
||||
@@ -280,6 +266,7 @@ pub fn chart<B: Backend>(
|
||||
.style(Style::default().fg(Color::Cyan))
|
||||
.graph_type(GraphType::Line)
|
||||
.data(&mem.0)];
|
||||
|
||||
let cpu_stats = CpuStats::new(cpu.0.last().map_or(0.00, |f| f.1));
|
||||
let mem_stats = ByteStats::new(mem.0.last().map_or(0, |f| f.1 as u64));
|
||||
let cpu_chart = make_chart(cpu.2, "cpu", cpu_dataset, &cpu_stats, &cpu.1);
|
||||
@@ -289,7 +276,6 @@ pub fn chart<B: Backend>(
|
||||
f.render_widget(mem_chart, area[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create charts
|
||||
fn make_chart<'a, T: Stats + Display>(
|
||||
@@ -490,8 +476,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))
|
||||
}
|
||||
|
||||
+6
-14
@@ -60,7 +60,7 @@ pub async fn create_ui(
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{err}");
|
||||
println!("error: {err}");
|
||||
}
|
||||
std::io::stdout().flush().unwrap_or(());
|
||||
Ok(())
|
||||
@@ -98,7 +98,7 @@ async fn run_app<B: Backend + Send>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while is_running.load(Ordering::Relaxed) {
|
||||
while is_running.load(Ordering::SeqCst) {
|
||||
if crossterm::event::poll(input_poll_rate).unwrap_or(false) {
|
||||
if let Ok(event) = event::read() {
|
||||
if let Event::Key(key) = event {
|
||||
@@ -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().get_container_len() > 0;
|
||||
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