chore: merge release-v0.0.6 into main
This commit is contained in:
@@ -13,14 +13,11 @@
|
|||||||
"runArgs": [
|
"runArgs": [
|
||||||
"--cap-add=SYS_PTRACE",
|
"--cap-add=SYS_PTRACE",
|
||||||
"--security-opt",
|
"--security-opt",
|
||||||
"seccomp=unconfined",
|
"seccomp=unconfined"
|
||||||
],
|
],
|
||||||
|
|
||||||
"mounts": [
|
"mounts": [
|
||||||
// //"source=/etc/timezone,target=/etc/timezone,type=bind,readonly",
|
"source=/etc/timezone,target=/etc/timezone,type=bind,readonly"
|
||||||
"source=/dev/shm,target=/ramdrive,type=bind",
|
|
||||||
"source=${localEnv:HOME}/.cargo/bin/cargo-watch,target=/usr/local/cargo/bin/cargo-watch,type=bind,readonly",
|
|
||||||
"source=${localEnv:HOME}/.cargo/bin/cross,target=/usr/local/cargo/bin/cross,type=bind,readonly",
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// Set *default* container specific settings.json values on container create.
|
// Set *default* container specific settings.json values on container create.
|
||||||
|
|||||||
+12
-6
@@ -1,13 +1,19 @@
|
|||||||
### 2022-05-30
|
### 2022-07-06
|
||||||
|
|
||||||
### Docs
|
### Docs
|
||||||
+ Readme one-liner to download & install latest version, [11d5ba361ee4c11d080f1c3c14d8bb677cbfd1fc]
|
+ readme update, [f29e29ad151ddf424ba630e6d33edf19acfd7636]
|
||||||
+ Example docker-compose.yml bump alpine version to 3.16, [98c83f2f68f59e78f0c78270c59886630d98913c]
|
+ comments improved, [1674db8a20aafa447732deb2e44ac8b97cf0471b]
|
||||||
|
+ readme logo size, [a733efa65865e04d9ec86c7ca8785dfbae635695]
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
+ use Some() checks to make sure that container item indexes are still valid, else can create out-of-bounds errors, closes [#8], [4cf02e3f04426ef44ec5a7421687f2104ac5102f]
|
+ Remove unwraps(), [61db81ecfe5684ddb8a360715f43357a042162c0]
|
||||||
+ Remove + replace as many unwrap()'s as possible, [d8e22d7444965f1874d7367259310440a889432b]
|
+ Help menu alt+tab > shift+tab typo, thanks [siph](https://github.com/siph), [04466803481b75feb7d7f275248279fdb8729862]
|
||||||
+ Help panel typo, [e497f3f2d9e1dca99469860c2e728c99e29353ad]
|
|
||||||
|
### Refactors
|
||||||
|
+ tokio spawns, [1fd230f2f3cf4e376058359515e76f4fa6e425c2]
|
||||||
|
+ max_line_width(), [a5d7dabbd68dc15a081df33352ce3b55d9a9891c]
|
||||||
|
+ create_release dead code removed, [297979c197c2defd409053d8da724f922b0bba1b]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
|
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.0.6'>v0.0.6</a>
|
||||||
|
### 2022-07-06
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
+ readme update, [f29e29ad](https://github.com/mrjackwills/oxker/commit/f29e29ad151ddf424ba630e6d33edf19acfd7636),
|
||||||
|
+ comments improved, [1674db8a](https://github.com/mrjackwills/oxker/commit/1674db8a20aafa447732deb2e44ac8b97cf0471b),
|
||||||
|
+ readme logo size, [a733efa6](https://github.com/mrjackwills/oxker/commit/a733efa65865e04d9ec86c7ca8785dfbae635695),
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
+ Remove unwraps(), [61db81ec](https://github.com/mrjackwills/oxker/commit/61db81ecfe5684ddb8a360715f43357a042162c0),
|
||||||
|
+ Help menu alt+tab > shift+tab typo, thanks [siph](https://github.com/siph), [04466803](https://github.com/mrjackwills/oxker/commit/04466803481b75feb7d7f275248279fdb8729862),
|
||||||
|
|
||||||
|
### Refactors
|
||||||
|
+ tokio spawns, [1fd230f2](https://github.com/mrjackwills/oxker/commit/1fd230f2f3cf4e376058359515e76f4fa6e425c2),
|
||||||
|
+ max_line_width(), [a5d7dabb](https://github.com/mrjackwills/oxker/commit/a5d7dabbd68dc15a081df33352ce3b55d9a9891c),
|
||||||
|
+ create_release dead code removed, [297979c1](https://github.com/mrjackwills/oxker/commit/297979c197c2defd409053d8da724f922b0bba1b),
|
||||||
|
|
||||||
|
|
||||||
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.0.5'>v0.0.5</a>
|
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.0.5'>v0.0.5</a>
|
||||||
### 2022-05-30
|
### 2022-05-30
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "oxker"
|
name = "oxker"
|
||||||
version = "0.0.5"
|
version = "0.0.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Jack Wills <email@mrjackwills.com>"]
|
authors = ["Jack Wills <email@mrjackwills.com>"]
|
||||||
description = "a simple tui to view & control docker containers"
|
description = "a simple tui to view & control docker containers"
|
||||||
@@ -11,14 +11,14 @@ readme = "README.md"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
bollard = "0.12.0"
|
bollard = "0.12.0"
|
||||||
cansi = "2.1.1"
|
cansi = "2.1"
|
||||||
clap={version="3.1.0", features = ["derive", "unicode"] }
|
clap={version="3.1", features = ["derive", "unicode"] }
|
||||||
crossterm = "0.23.2"
|
crossterm = "0.23"
|
||||||
futures-util = "0.3.21"
|
futures-util = "0.3"
|
||||||
parking_lot = {version= "0.12.0"}
|
parking_lot = {version= "0.12"}
|
||||||
tokio = {version = "1.17.0", features=["full"]}
|
tokio = {version = "1.19", features=["full"]}
|
||||||
tracing = "0.1.32"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3.9"
|
tracing-subscriber = "0.3"
|
||||||
tui = "0.18"
|
tui = "0.18"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022 Jack Wills
|
Copyright (c) 2022- Jack Wills
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src='./.github/logo.svg' width='200px'/>
|
<img src='./.github/logo.svg' width='125px'/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -92,7 +92,7 @@ or individually
|
|||||||
|
|
||||||
```docker run --name redis -d redis:alpine3.16```
|
```docker run --name redis -d redis:alpine3.16```
|
||||||
|
|
||||||
```docker run --name postgres -e POSTGRES_PASSWORD=never_use_this_password_in_production -d postgres:alpine```
|
```docker run --name postgres -e POSTGRES_PASSWORD=never_use_this_password_in_production -d postgres:alpine3.16```
|
||||||
|
|
||||||
```docker run -d --hostname my-rabbit --name rabbitmq rabbitmq:3```
|
```docker run -d --hostname my-rabbit --name rabbitmq rabbitmq:3```
|
||||||
|
|
||||||
|
|||||||
+5
-9
@@ -205,16 +205,16 @@ release_flow() {
|
|||||||
git push --atomic origin main "$NEW_TAG_WITH_V"
|
git push --atomic origin main "$NEW_TAG_WITH_V"
|
||||||
git checkout dev
|
git checkout dev
|
||||||
git merge --no-ff main -m 'chore: merge main into dev'
|
git merge --no-ff main -m 'chore: merge main into dev'
|
||||||
|
git push origin dev
|
||||||
git branch -d "$RELEASE_BRANCH"
|
git branch -d "$RELEASE_BRANCH"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
cmd=(dialog --backtitle "Choose build option" --radiolist "choose" 14 80 16)
|
cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16)
|
||||||
options=(
|
options=(
|
||||||
1 "build" off
|
1 "test" off
|
||||||
2 "test" off
|
2 "release" off
|
||||||
3 "release" off
|
|
||||||
)
|
)
|
||||||
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
||||||
exitStatus=$?
|
exitStatus=$?
|
||||||
@@ -229,14 +229,10 @@ main() {
|
|||||||
exit
|
exit
|
||||||
break;;
|
break;;
|
||||||
1)
|
1)
|
||||||
cargo_build_all
|
|
||||||
main
|
|
||||||
break;;
|
|
||||||
2)
|
|
||||||
cargo_test
|
cargo_test
|
||||||
main
|
main
|
||||||
break;;
|
break;;
|
||||||
3)
|
2)
|
||||||
release_flow
|
release_flow
|
||||||
break;;
|
break;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -134,8 +134,6 @@ impl fmt::Display for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Items for the container control list
|
/// Items for the container control list
|
||||||
/// Should probably have a vec for each container
|
|
||||||
/// so that can remove Pause if container currently Paused etc
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DockerControls {
|
pub enum DockerControls {
|
||||||
Pause,
|
Pause,
|
||||||
@@ -411,6 +409,7 @@ pub struct Columns {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Columns {
|
impl Columns {
|
||||||
|
//. (Column titles, minimum header string length)
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: (String::from("state"), 11),
|
state: (String::from("state"), 11),
|
||||||
|
|||||||
+5
-6
@@ -99,7 +99,7 @@ impl AppData {
|
|||||||
self.error = Some(error);
|
self.error = Some(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the if of the currently selected container.
|
/// Find the id of the currently selected container.
|
||||||
/// If any containers on system, will always return a string.
|
/// If any containers on system, will always return a string.
|
||||||
/// Only returns None when no containers found.
|
/// Only returns None when no containers found.
|
||||||
pub fn get_selected_container_id(&self) -> Option<String> {
|
pub fn get_selected_container_id(&self) -> Option<String> {
|
||||||
@@ -183,7 +183,7 @@ impl AppData {
|
|||||||
self.containers.items.len()
|
self.containers.items.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the widths for the strings in the containers panel
|
/// Find the widths for the strings in the containers panel.
|
||||||
/// So can display nicely and evenly
|
/// So can display nicely and evenly
|
||||||
pub fn get_width(&self) -> Columns {
|
pub fn get_width(&self) -> Columns {
|
||||||
let mut output = Columns::new();
|
let mut output = Columns::new();
|
||||||
@@ -228,11 +228,9 @@ impl AppData {
|
|||||||
if status_count > output.status.1 {
|
if status_count > output.status.1 {
|
||||||
output.status.1 = status_count;
|
output.status.1 = status_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
if net_rx_count > output.net_rx.1 {
|
if net_rx_count > output.net_rx.1 {
|
||||||
output.net_rx.1 = net_rx_count;
|
output.net_rx.1 = net_rx_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
if net_tx_count > output.net_tx.1 {
|
if net_tx_count > output.net_tx.1 {
|
||||||
output.net_tx.1 = net_tx_count;
|
output.net_tx.1 = net_tx_count;
|
||||||
};
|
};
|
||||||
@@ -254,7 +252,7 @@ impl AppData {
|
|||||||
self.containers.items.iter_mut().find(|i| i.id == id)
|
self.containers.items.iter_mut().find(|i| i.id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update container mem + cpu stats, in single function so only need to call .lock() once
|
/// Update container mem, cpu, & network stats, in single function so only need to call .lock() once
|
||||||
pub fn update_stats(
|
pub fn update_stats(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: String,
|
id: String,
|
||||||
@@ -318,7 +316,7 @@ impl AppData {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&vec!["".to_owned()])
|
.unwrap_or(&vec!["".to_owned()])
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap()
|
.unwrap_or(&String::from(""))
|
||||||
.to_owned();
|
.to_owned();
|
||||||
if let Some(c) = name.chars().next() {
|
if let Some(c) = name.chars().next() {
|
||||||
if c == '/' {
|
if c == '/' {
|
||||||
@@ -391,6 +389,7 @@ impl AppData {
|
|||||||
self.logs_parsed = true;
|
self.logs_parsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update all containers logs, should only be used on first initialisation
|
||||||
pub fn update_all_logs(&mut self, all_logs: Vec<Vec<String>>) {
|
pub fn update_all_logs(&mut self, all_logs: Vec<Vec<String>>) {
|
||||||
for (index, output) in all_logs.into_iter().enumerate() {
|
for (index, output) in all_logs.into_iter().enumerate() {
|
||||||
self.update_log_by_index(output, index);
|
self.update_log_by_index(output, index);
|
||||||
|
|||||||
+23
-22
@@ -31,12 +31,12 @@ impl DockerData {
|
|||||||
let mut cpu_percentage = 0.0;
|
let mut cpu_percentage = 0.0;
|
||||||
let previous_cpu = stats.precpu_stats.cpu_usage.total_usage;
|
let previous_cpu = stats.precpu_stats.cpu_usage.total_usage;
|
||||||
let cpu_delta = stats.cpu_stats.cpu_usage.total_usage as f64 - previous_cpu as f64;
|
let cpu_delta = stats.cpu_stats.cpu_usage.total_usage as f64 - previous_cpu as f64;
|
||||||
if stats.cpu_stats.system_cpu_usage.is_some()
|
|
||||||
&& stats.precpu_stats.system_cpu_usage.is_some()
|
if let (Some(cpu_stats_usage), Some(precpu_stats_usage)) = (
|
||||||
{
|
stats.cpu_stats.system_cpu_usage,
|
||||||
let system_delta = (stats.cpu_stats.system_cpu_usage.unwrap_or(0)
|
stats.precpu_stats.system_cpu_usage,
|
||||||
- stats.precpu_stats.system_cpu_usage.unwrap_or(0))
|
) {
|
||||||
as f64;
|
let system_delta = (cpu_stats_usage - precpu_stats_usage) as f64;
|
||||||
let online_cpus = stats.cpu_stats.online_cpus.unwrap_or_else(|| {
|
let online_cpus = stats.cpu_stats.online_cpus.unwrap_or_else(|| {
|
||||||
stats
|
stats
|
||||||
.cpu_stats
|
.cpu_stats
|
||||||
@@ -54,7 +54,7 @@ impl DockerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a single docker stat in order to update mem and cpu usage
|
/// Get a single docker stat in order to update mem and cpu usage
|
||||||
/// don't take &self, so that can tokio::spawn into it's on thread
|
/// don't take &self, so that can tokio::spawn into it's own thread
|
||||||
async fn update_container_stat(
|
async fn update_container_stat(
|
||||||
docker: Arc<Docker>,
|
docker: Arc<Docker>,
|
||||||
id: String,
|
id: String,
|
||||||
@@ -117,14 +117,15 @@ impl DockerData {
|
|||||||
let app_data = Arc::clone(&self.app_data);
|
let app_data = Arc::clone(&self.app_data);
|
||||||
let is_running = *is_running;
|
let is_running = *is_running;
|
||||||
let id = id.to_owned();
|
let id = id.to_owned();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(Self::update_container_stat(
|
||||||
Self::update_container_stat(docker, id, app_data, is_running).await
|
docker, id, app_data, is_running,
|
||||||
});
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all current containers, handle into ContainerItem in the app_data struct rather than here
|
/// Get all current containers, handle into ContainerItem in the app_data struct rather than here
|
||||||
/// Just make sure that items sent are guaranteed to have an id
|
/// Just make sure that items sent are guaranteed to have an id
|
||||||
|
/// return Vec<(is_running, id)>
|
||||||
pub async fn update_all_containers(&mut self) -> Vec<(bool, String)> {
|
pub async fn update_all_containers(&mut self) -> Vec<(bool, String)> {
|
||||||
let containers = self
|
let containers = self
|
||||||
.docker
|
.docker
|
||||||
@@ -136,11 +137,11 @@ impl DockerData {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
// iter over containers, to only send ones which have an id, as use ID for extensivley!
|
// iter over containers, to only send ones which have an id, as use id for identification throughout!
|
||||||
// alternative is to create my own container struct, and will out with details
|
containers
|
||||||
containers.iter().filter(|i| i.id.is_some()).for_each(|c| {
|
.iter()
|
||||||
output.push(c.to_owned());
|
.filter(|i| i.id.is_some())
|
||||||
});
|
.for_each(|c| output.push(c.to_owned()));
|
||||||
|
|
||||||
self.app_data.lock().update_containers(&output);
|
self.app_data.lock().update_containers(&output);
|
||||||
output
|
output
|
||||||
@@ -157,7 +158,7 @@ impl DockerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update single container logs
|
/// Update single container logs
|
||||||
/// don't take &self, so that can tokio::spawn into it's on thread
|
/// don't take &self, so that can tokio::spawn into it's own thread
|
||||||
async fn update_log(
|
async fn update_log(
|
||||||
docker: Arc<Docker>,
|
docker: Arc<Docker>,
|
||||||
id: String,
|
id: String,
|
||||||
@@ -202,12 +203,12 @@ impl DockerData {
|
|||||||
|
|
||||||
async fn update_everything(&mut self) {
|
async fn update_everything(&mut self) {
|
||||||
let all_ids = self.update_all_containers().await;
|
let all_ids = self.update_all_containers().await;
|
||||||
let op_index = self.app_data.lock().get_selected_log_index();
|
let optional_index = self.app_data.lock().get_selected_log_index();
|
||||||
if let Some(index) = op_index {
|
if let Some(index) = optional_index {
|
||||||
let docker = Arc::clone(&self.docker);
|
|
||||||
let since = self.app_data.lock().containers.items[index].last_updated as i64;
|
|
||||||
let timestamps = self.timestamps;
|
|
||||||
let id = self.app_data.lock().containers.items[index].id.to_owned();
|
let id = self.app_data.lock().containers.items[index].id.to_owned();
|
||||||
|
let since = self.app_data.lock().containers.items[index].last_updated as i64;
|
||||||
|
let docker = Arc::clone(&self.docker);
|
||||||
|
let timestamps = self.timestamps;
|
||||||
let logs = Self::update_log(docker, id, timestamps, since).await;
|
let logs = Self::update_log(docker, id, timestamps, since).await;
|
||||||
self.app_data.lock().update_log_by_index(logs, index);
|
self.app_data.lock().update_log_by_index(logs, index);
|
||||||
};
|
};
|
||||||
@@ -226,7 +227,7 @@ impl DockerData {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop the loading_spin fn, and reset gui loading status
|
/// Stop the loading_spin function, and reset gui loading status
|
||||||
fn stop_loading_spin(&mut self, handle: JoinHandle<()>) {
|
fn stop_loading_spin(&mut self, handle: JoinHandle<()>) {
|
||||||
handle.abort();
|
handle.abort();
|
||||||
self.gui_state.lock().reset_loading();
|
self.gui_state.lock().reset_loading();
|
||||||
|
|||||||
@@ -175,8 +175,6 @@ impl InputHandler {
|
|||||||
}
|
}
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
// This isn't great, just means you can't send docker commands before full initialization of the program
|
// This isn't great, just means you can't send docker commands before full initialization of the program
|
||||||
// could change to to if loading = true, although at the moment don't have a loading bool
|
|
||||||
// Does is matter though?
|
|
||||||
let panel = self.gui_state.lock().selected_panel;
|
let panel = self.gui_state.lock().selected_panel;
|
||||||
if panel == SelectablePanel::Commands {
|
if panel == SelectablePanel::Commands {
|
||||||
let option_command = self.app_data.lock().get_docker_command();
|
let option_command = self.app_data.lock().get_docker_command();
|
||||||
|
|||||||
+15
-21
@@ -32,21 +32,19 @@ async fn main() {
|
|||||||
let docker_gui_state = Arc::clone(&gui_state);
|
let docker_gui_state = Arc::clone(&gui_state);
|
||||||
|
|
||||||
let (docker_sx, docker_rx) = tokio::sync::mpsc::channel(16);
|
let (docker_sx, docker_rx) = tokio::sync::mpsc::channel(16);
|
||||||
|
|
||||||
// Create docker daemon handler, and only spawn up the docker data handler if ping returns non-error
|
// Create docker daemon handler, and only spawn up the docker data handler if ping returns non-error
|
||||||
let docker = Arc::new(Docker::connect_with_socket_defaults().unwrap());
|
let docker = Arc::new(Docker::connect_with_socket_defaults().unwrap());
|
||||||
match docker.ping().await {
|
match docker.ping().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let docker = Arc::clone(&docker);
|
let docker = Arc::clone(&docker);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(DockerData::init(
|
||||||
DockerData::init(
|
docker_args,
|
||||||
docker_args,
|
docker_app_data,
|
||||||
docker_app_data,
|
docker,
|
||||||
docker,
|
docker_gui_state,
|
||||||
docker_gui_state,
|
docker_rx,
|
||||||
docker_rx,
|
));
|
||||||
)
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Err(_) => app_data.lock().set_error(AppError::DockerConnect),
|
Err(_) => app_data.lock().set_error(AppError::DockerConnect),
|
||||||
}
|
}
|
||||||
@@ -55,23 +53,19 @@ async fn main() {
|
|||||||
|
|
||||||
let (input_sx, input_rx) = tokio::sync::mpsc::channel(16);
|
let (input_sx, input_rx) = tokio::sync::mpsc::channel(16);
|
||||||
|
|
||||||
// let input_docker = Arc::clone(&docker);
|
|
||||||
let is_running = Arc::new(AtomicBool::new(true));
|
let is_running = Arc::new(AtomicBool::new(true));
|
||||||
let input_is_running = Arc::clone(&is_running);
|
let input_is_running = Arc::clone(&is_running);
|
||||||
let input_gui_state = Arc::clone(&gui_state);
|
let input_gui_state = Arc::clone(&gui_state);
|
||||||
let input_docker_sender = docker_sx.clone();
|
let input_docker_sender = docker_sx.clone();
|
||||||
|
|
||||||
// Spawn input handling into own tokio thread
|
// Spawn input handling into own tokio thread
|
||||||
tokio::spawn(async {
|
tokio::spawn(input_handler::InputHandler::init(
|
||||||
input_handler::InputHandler::init(
|
input_app_data,
|
||||||
input_app_data,
|
input_rx,
|
||||||
input_rx,
|
input_docker_sender,
|
||||||
input_docker_sender,
|
input_gui_state,
|
||||||
input_gui_state,
|
input_is_running,
|
||||||
input_is_running,
|
));
|
||||||
)
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Debug mode for testing, mostly pointless, doesn't take terminal nor draw gui
|
// Debug mode for testing, mostly pointless, doesn't take terminal nor draw gui
|
||||||
if !args.gui {
|
if !args.gui {
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ impl CliArgs {
|
|||||||
docker_interval: args.docker_interval,
|
docker_interval: args.docker_interval,
|
||||||
gui: !args.gui,
|
gui: !args.gui,
|
||||||
raw: args.raw,
|
raw: args.raw,
|
||||||
// install: args.install,
|
|
||||||
timestamp: !args.timestamp,
|
timestamp: !args.timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-42
@@ -72,7 +72,7 @@ fn generate_block<'a>(
|
|||||||
block
|
block
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the selectable panels
|
/// Draw the command panel
|
||||||
pub fn draw_commands<B: Backend>(
|
pub fn draw_commands<B: Backend>(
|
||||||
app_data: &Arc<Mutex<AppData>>,
|
app_data: &Arc<Mutex<AppData>>,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
@@ -114,7 +114,7 @@ pub fn draw_commands<B: Backend>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the selectable panels
|
/// Draw the containers panel
|
||||||
pub fn draw_containers<B: Backend>(
|
pub fn draw_containers<B: Backend>(
|
||||||
app_data: &Arc<Mutex<AppData>>,
|
app_data: &Arc<Mutex<AppData>>,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
@@ -207,7 +207,7 @@ pub fn draw_containers<B: Backend>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the logs panels
|
/// Draw the logs panel
|
||||||
pub fn draw_logs<B: Backend>(
|
pub fn draw_logs<B: Backend>(
|
||||||
app_data: &Arc<Mutex<AppData>>,
|
app_data: &Arc<Mutex<AppData>>,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
@@ -269,13 +269,11 @@ pub fn draw_chart<B: Backend>(
|
|||||||
// Check is some, else can cause out of bounds error, if containers get removed before a docker update
|
// 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) {
|
if let Some(data) = app_data.lock().containers.items.get(index) {
|
||||||
let (cpu, mem) = data.get_chart_data();
|
let (cpu, mem) = data.get_chart_data();
|
||||||
|
|
||||||
let cpu_dataset = vec![Dataset::default()
|
let cpu_dataset = vec![Dataset::default()
|
||||||
.marker(symbols::Marker::Dot)
|
.marker(symbols::Marker::Dot)
|
||||||
.style(Style::default().fg(Color::Magenta))
|
.style(Style::default().fg(Color::Magenta))
|
||||||
.graph_type(GraphType::Line)
|
.graph_type(GraphType::Line)
|
||||||
.data(&cpu.0)];
|
.data(&cpu.0)];
|
||||||
|
|
||||||
let mem_dataset = vec![Dataset::default()
|
let mem_dataset = vec![Dataset::default()
|
||||||
.marker(symbols::Marker::Dot)
|
.marker(symbols::Marker::Dot)
|
||||||
.style(Style::default().fg(Color::Cyan))
|
.style(Style::default().fg(Color::Cyan))
|
||||||
@@ -347,11 +345,12 @@ fn make_chart<T: Stats + Display>(
|
|||||||
.fg(label_color),
|
.fg(label_color),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
// Add 0.01, so that max point is always visible?
|
||||||
.bounds([0.0, max.get_value() + 0.01]),
|
.bounds([0.0, max.get_value() + 0.01]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show error popup over whole screen
|
/// Draw heading bar at top of program, always visible
|
||||||
pub fn draw_heading_bar<B: Backend>(
|
pub fn draw_heading_bar<B: Backend>(
|
||||||
area: Rect,
|
area: Rect,
|
||||||
columns: &Columns,
|
columns: &Columns,
|
||||||
@@ -457,13 +456,25 @@ pub fn draw_heading_bar<B: Backend>(
|
|||||||
f.render_widget(paragraph, split_bar[index]);
|
f.render_widget(paragraph, split_bar[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show error popup over whole screen
|
/// From a given &String, return the maximum number of chars on a single line
|
||||||
|
fn max_line_width(text: &str) -> usize {
|
||||||
|
let mut max_line_width = 0;
|
||||||
|
text.lines().into_iter().for_each(|line| {
|
||||||
|
let width = line.chars().count();
|
||||||
|
if width > max_line_width {
|
||||||
|
max_line_width = width;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
max_line_width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw the help box in the centre of the screen
|
||||||
pub fn draw_help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
pub fn draw_help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
||||||
let title = format!(" {} ", VERSION);
|
let title = format!(" {} ", VERSION);
|
||||||
|
|
||||||
let description_text = format!("\n{}", DESCRIPTION);
|
let description_text = format!("\n{}", DESCRIPTION);
|
||||||
|
|
||||||
let mut help_text = String::from("\n ( tab ) or ( alt+tab ) to change panels");
|
let mut help_text = String::from("\n ( tab ) or ( shift+tab ) to change panels");
|
||||||
help_text
|
help_text
|
||||||
.push_str("\n ( ↑ ↓ ) or ( j k ) or (PgUp PgDown) or (Home End) to change selected line");
|
.push_str("\n ( ↑ ↓ ) or ( j k ) or (PgUp PgDown) or (Home End) to change selected line");
|
||||||
help_text.push_str("\n ( enter ) to send docker container commands");
|
help_text.push_str("\n ( enter ) to send docker container commands");
|
||||||
@@ -476,17 +487,9 @@ pub fn draw_help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
|||||||
help_text.push_str("\n\n currenty an early work in progress, all and any input appreciated");
|
help_text.push_str("\n\n currenty an early work in progress, all and any input appreciated");
|
||||||
help_text.push_str(format!("\n {}", REPO.trim()).as_str());
|
help_text.push_str(format!("\n {}", REPO.trim()).as_str());
|
||||||
|
|
||||||
let mut max_line_width = 0;
|
// Find the maximum line widths & height
|
||||||
|
|
||||||
let all_text = format!("{}{}{}", NAME_TEXT, description_text, help_text);
|
let all_text = format!("{}{}{}", NAME_TEXT, description_text, help_text);
|
||||||
|
let mut max_line_width = max_line_width(&all_text);
|
||||||
all_text.lines().into_iter().for_each(|line| {
|
|
||||||
let width = line.chars().count();
|
|
||||||
if width > max_line_width {
|
|
||||||
max_line_width = width;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut lines = all_text.lines().count();
|
let mut lines = all_text.lines().count();
|
||||||
|
|
||||||
// Add some vertical and horizontal padding to the info box
|
// Add some vertical and horizontal padding to the info box
|
||||||
@@ -541,7 +544,7 @@ pub fn draw_help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
|||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show error popup over whole screen
|
/// Draw an error popup over whole screen
|
||||||
pub fn draw_error<B: Backend>(f: &mut Frame<'_, B>, error: AppError, seconds: Option<u8>) {
|
pub fn draw_error<B: Backend>(f: &mut Frame<'_, B>, error: AppError, seconds: Option<u8>) {
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title(" Error ")
|
.title(" Error ")
|
||||||
@@ -565,14 +568,8 @@ pub fn draw_error<B: Backend>(f: &mut Frame<'_, B>, error: AppError, seconds: Op
|
|||||||
|
|
||||||
text.push_str(to_push.as_str());
|
text.push_str(to_push.as_str());
|
||||||
|
|
||||||
let mut max_line_width = 0;
|
// Find the maximum line width & height
|
||||||
text.lines().into_iter().for_each(|line| {
|
let mut max_line_width = max_line_width(&text);
|
||||||
let width = line.chars().count();
|
|
||||||
if width > max_line_width {
|
|
||||||
max_line_width = width;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut lines = text.lines().count();
|
let mut lines = text.lines().count();
|
||||||
|
|
||||||
// Add some horizontal & vertical margins
|
// Add some horizontal & vertical margins
|
||||||
@@ -594,21 +591,14 @@ pub fn draw_error<B: Backend>(f: &mut Frame<'_, B>, error: AppError, seconds: Op
|
|||||||
f.render_widget(paragraph, area);
|
f.render_widget(paragraph, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show info box in bottom right corner
|
/// Draw info box in one of the 9 BoxLocations
|
||||||
pub fn draw_info<B: Backend>(f: &mut Frame<'_, B>, text: String) {
|
pub fn draw_info<B: Backend>(f: &mut Frame<'_, B>, text: String) {
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title("")
|
.title("")
|
||||||
.title_alignment(Alignment::Center)
|
.title_alignment(Alignment::Center)
|
||||||
.borders(Borders::NONE);
|
.borders(Borders::NONE);
|
||||||
|
|
||||||
let mut max_line_width = 0;
|
let mut max_line_width = max_line_width(&text);
|
||||||
text.lines().into_iter().for_each(|line| {
|
|
||||||
let width = line.chars().count();
|
|
||||||
if width > max_line_width {
|
|
||||||
max_line_width = width;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut lines = text.lines().count();
|
let mut lines = text.lines().count();
|
||||||
|
|
||||||
// Add some horizontal & vertical margins
|
// Add some horizontal & vertical margins
|
||||||
@@ -630,7 +620,7 @@ pub fn draw_info<B: Backend>(f: &mut Frame<'_, B>, text: String) {
|
|||||||
f.render_widget(paragraph, area);
|
f.render_widget(paragraph, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// draw a box in the center of the screen, based on max line width + number of lines
|
/// draw a box in the one of the BoxLocations, based on max line width + number of lines
|
||||||
fn draw_popup(text_lines: u16, text_width: u16, r: Rect, box_location: BoxLocation) -> Rect {
|
fn draw_popup(text_lines: u16, text_width: u16, r: Rect, box_location: BoxLocation) -> Rect {
|
||||||
// Make sure blank_space can't be an negative, as will crash
|
// Make sure blank_space can't be an negative, as will crash
|
||||||
let blank_vertical = if r.height > text_lines {
|
let blank_vertical = if r.height > text_lines {
|
||||||
@@ -644,19 +634,18 @@ fn draw_popup(text_lines: u16, text_width: u16, r: Rect, box_location: BoxLocati
|
|||||||
1
|
1
|
||||||
};
|
};
|
||||||
|
|
||||||
let vertical_constraints = box_location.get_vertical_constraints(blank_vertical, text_lines);
|
let v_constraints = box_location.get_vertical_constraints(blank_vertical, text_lines);
|
||||||
let horizontal_constraints =
|
let h_constraints = box_location.get_horizontal_constraints(blank_horizontal, text_width);
|
||||||
box_location.get_horizontal_constraints(blank_horizontal, text_width);
|
|
||||||
|
|
||||||
let indexes = box_location.get_indexes();
|
let indexes = box_location.get_indexes();
|
||||||
|
|
||||||
let popup_layout = Layout::default()
|
let popup_layout = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints(vertical_constraints)
|
.constraints(v_constraints)
|
||||||
.split(r);
|
.split(r);
|
||||||
|
|
||||||
Layout::default()
|
Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints(horizontal_constraints)
|
.constraints(h_constraints)
|
||||||
.split(popup_layout[indexes.0])[indexes.1]
|
.split(popup_layout[indexes.0])[indexes.1]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user