From 59797685dffa29752a48c98e6cf465884d6d9df6 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 13 Oct 2022 02:33:05 +0000 Subject: [PATCH 01/20] fix: loading icon color & shifting, closes #15 --- src/ui/draw_blocks.rs | 38 +++++++++++++++++++++++++++----------- src/ui/gui_state.rs | 4 +++- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 50c4177..3f2048b 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -337,6 +337,7 @@ fn make_chart<'a, T: Stats + Display>( } /// Draw heading bar at top of program, always visible +/// TODO Should seperate into loading icon/headers/help functions #[allow(clippy::too_many_lines)] pub fn heading_bar( area: Rect, @@ -347,10 +348,10 @@ pub fn heading_bar( sorted_by: Option<(Header, SortedOrder)>, gui_state: &Arc>, ) { - let block = || Block::default().style(Style::default().bg(Color::Magenta).fg(Color::Black)); + let block = |fg: Color| Block::default().style(Style::default().bg(Color::Magenta).fg(fg)); let info_visible = gui_state.lock().show_help; - f.render_widget(block(), area); + f.render_widget(block(Color::Black), area); // Generate a bloack for the header, if the header is currently being used to sort a column, then highlight it white let header_block = |x: &Header| { @@ -380,8 +381,7 @@ pub fn heading_bar( let block = header_block(header); let text = match header { Header::State => format!( - " {}{:>width$}{ic}", - loading_icon, + "{:>width$}{ic}", header, ic = block.1, width = width - block.2, @@ -393,7 +393,6 @@ pub fn heading_bar( ic = block.1, width = width - block.2 ), - _ => format!( "{}{:>width$}{ic}", MARGIN, @@ -438,6 +437,7 @@ pub fn heading_bar( let column_width = if column_width > 0 { column_width } else { 1 }; let splits = if has_containers { vec![ + Constraint::Min(2), Constraint::Min(column_width.try_into().unwrap_or_default()), Constraint::Min(info_width.try_into().unwrap_or_default()), ] @@ -450,12 +450,20 @@ pub fn heading_bar( .constraints(splits.as_ref()) .split(area); if has_containers { - let container_splits = header_data.iter().map(|i| i.2).collect::>(); + // Draw loading icon, or not, and a prefix with a single space + let loading_icon = format!("{:>2}", loading_icon); + let loading_paragraph = Paragraph::new(loading_icon) + .block(block(Color::White)) + .alignment(Alignment::Center); + f.render_widget(loading_paragraph, split_bar[0]); + + + let container_splits = header_data.iter().map(|i| i.2).collect::>(); let headers_section = Layout::default() .direction(Direction::Horizontal) .constraints(container_splits.as_ref()) - .split(split_bar[0]); + .split(split_bar[1]); // draw the actual header blocks for (index, (paragraph, header, _)) in header_data.into_iter().enumerate() { @@ -465,13 +473,21 @@ pub fn heading_bar( } } - let paragraph = Paragraph::new(info_text) - .block(block()) + /// show/hide help + let color = if info_visible { + Color::White + } else { + Color::Black + }; + let help_paragraph = Paragraph::new(info_text) + .block(block(color)) .alignment(Alignment::Right); // If no containers, don't display the headers, could maybe do this first? - let index = if has_containers { 1 } else { 0 }; - f.render_widget(paragraph, split_bar[index]); + let help_index = if has_containers { 2 } else { 0 }; + // render help info + + f.render_widget(help_paragraph, split_bar[help_index]); } /// From a given &str, return the maximum number of chars on a single line diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index f98b990..9279d19 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -59,6 +59,7 @@ pub enum BoxLocation { } impl BoxLocation { + /// Screen is divided into 3x3 sections pub const fn get_indexes(self) -> (usize, usize) { match self { Self::TopLeft => (0, 0), @@ -258,9 +259,10 @@ impl GuiState { } /// If is_loading has any entries, return the current loading_icon, else an emtpy string + // Option? pub fn get_loading(&mut self) -> String { if self.is_loading.is_empty() { - String::new() + String::from(" ") } else { self.loading_icon.to_string() } From ea08a12246def3c9a35e1f2bc35fe8ba004bac04 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 13 Oct 2022 02:33:32 +0000 Subject: [PATCH 02/20] fix: comment typo --- src/ui/draw_blocks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 3f2048b..086dad4 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -473,7 +473,7 @@ pub fn heading_bar( } } - /// show/hide help + // show/hide help let color = if info_visible { Color::White } else { From 3c8d59c666bd4cda9ca54989b2f1b48bba17bc57 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 13 Oct 2022 17:44:41 +0000 Subject: [PATCH 03/20] fix: create_release.sh release_continue --- create_release.sh | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/create_release.sh b/create_release.sh index 3adbf36..799056f 100755 --- a/create_release.sh +++ b/create_release.sh @@ -1,7 +1,7 @@ #!/bin/bash # rust create_release -# v0.0.15 +# v0.1.0 PACKAGE_NAME='oxker' STAR_LINE='****************************************' @@ -193,34 +193,66 @@ cargo_build () { ask_continue } +# $1 text to colourise +release_continue () { + echo -e "\n${PURPLE}$1${RESET}" + ask_continue + +} # Full flow to create a new release release_flow() { check_git get_git_remote_url cargo_test cargo_build + cd "${CWD}" || error_close "Can't find ${CWD}" check_tag NEW_TAG_WITH_V="v${MAJOR}.${MINOR}.${PATCH}" printf "\nnew tag chosen: %s\n\n" "${NEW_TAG_WITH_V}" + RELEASE_BRANCH=release-$NEW_TAG_WITH_V echo -e ask_changelog_update + + release_continue "checkout ${RELEASE_BRANCH}" git checkout -b "$RELEASE_BRANCH" - update_version_number_in_files - cargo fmt - git add . - git commit -m "chore: release $NEW_TAG_WITH_V" + release_continue "update_version_number_in_files" + update_version_number_in_files + + echo -e "\ncargo fmt" + cargo fmt + + release_continue "git add ." + git add . + + release_continue "git commit -mg \"chore: release \"${NEW_TAG_WITH_V}\"" + git commit -m "chore: release ${NEW_TAG_WITH_V}" + + release_continue "git checkout main" git checkout main + + release_continue "git merge --no-ff \"${RELEASE_BRANCH}\" -m \"chore: merge ${RELEASE_BRANCH} into main\"" git merge --no-ff "$RELEASE_BRANCH" -m "chore: merge ${RELEASE_BRANCH} into main" + + release_continue "git tag -am \"${RELEASE_BRANCH}\" \"$NEW_TAG_WITH_V\"" git tag -am "${RELEASE_BRANCH}" "$NEW_TAG_WITH_V" - echo "git tag -am \"${RELEASE_BRANCH}\" \"$NEW_TAG_WITH_V\"" + + release_continue "git push --atomic origin main \"$NEW_TAG_WITH_V\"" git push --atomic origin main "$NEW_TAG_WITH_V" + + release_continue "git checkout dev" git checkout dev + + release_continue "git merge --no-ff main -m 'chore: merge main into dev'" git merge --no-ff main -m 'chore: merge main into dev' + + release_continue "git push origin dev" git push origin dev + + release_continue "git branch -d \"$RELEASE_BRANCH\"" git branch -d "$RELEASE_BRANCH" } From 67c49575682cb271fac0998ff377a6504cd0bc86 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 13 Oct 2022 17:45:29 +0000 Subject: [PATCH 04/20] feat: terminal.clear() after run_app --- src/ui/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b126dbd..ff54d2d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -56,6 +56,7 @@ pub async fn create_ui( update_duration, ) .await; + terminal.clear()?; disable_raw_mode()?; execute!( From 46ffbeef6a86032c3ca92d7372bda68a8e6e4d09 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 14 Oct 2022 19:07:42 +0000 Subject: [PATCH 05/20] docs: changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac02d8..3b16115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,13 +16,13 @@ + replace async fn with just fn, [17dc604b](https://github.com/mrjackwills/oxker/commit/17dc604befac75cb9dc0311a0e43f9927fe0ca30), + remove pointless clone()'s & variable declarations, [6731002e](https://github.com/mrjackwills/oxker/commit/6731002ee42c9460042c2c38aff5101b1bcebbe6), + replace String::from("") with String::new(), [62fb2247](https://github.com/mrjackwills/oxker/commit/62fb22478697cc9a7ab9fb562a724965b437233a), -+ replace map_or_else with map_or, [3e26f292](https://github.com/mrjackwills/oxker/commit/3e26f292c7dc5e13af4580952767ebe821aa5183),, [5660b34d](https://github.com/mrjackwills/oxker/commit/5660b34d5149dce27706ff6daa90b854e6f84e14), ++ replace map_or_else with map_or, [3e26f292](https://github.com/mrjackwills/oxker/commit/3e26f292c7dc5e13af4580952767ebe821aa5183), [5660b34d](https://github.com/mrjackwills/oxker/commit/5660b34d5149dce27706ff6daa90b854e6f84e14), # v0.1.4 ### 2022-09-07 ### Chores -+ dependencies updated, [a3168daa](https://github.com/mrjackwills/oxker/commit/a3168daa3f769a6747dfbe61103073a7e80a1485),, [78e59160](https://github.com/mrjackwills/oxker/commit/78e59160bb6a978ee80e3a99eb72f051fb64e737), ++ dependencies updated, [a3168daa](https://github.com/mrjackwills/oxker/commit/a3168daa3f769a6747dfbe61103073a7e80a1485),[78e59160](https://github.com/mrjackwills/oxker/commit/78e59160bb6a978ee80e3a99eb72f051fb64e737), ### Features + containerize self, github action to build and push to [Docker Hub](https://hub.docker.com/r/mrjackwills/oxker), [07f97202](https://github.com/mrjackwills/oxker/commit/07f972022a69f22bac57925e6ad84234381f7890), From 90e26c300ee0614a7e33d00ce4c3b2c1bb7b4dc8 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 14 Oct 2022 21:26:20 +0000 Subject: [PATCH 06/20] wip: gui_status, should use a hashset? --- src/app_data/mod.rs | 6 +- src/docker_data/mod.rs | 38 +++--- src/input_handler/mod.rs | 264 ++++++++++++++++++++------------------- src/main.rs | 12 +- src/ui/draw_blocks.rs | 76 ++++++----- src/ui/gui_state.rs | 23 +++- src/ui/mod.rs | 13 +- 7 files changed, 240 insertions(+), 192 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index abb6f8b..1f4591e 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -15,8 +15,8 @@ pub struct AppData { error: Option, logs_parsed: bool, pub containers: StatefulList, - pub init: bool, - pub show_error: bool, + // pub init: bool, + // pub show_error: bool, sorted_by: Option<(Header, SortedOrder)>, } @@ -79,9 +79,7 @@ impl AppData { args, containers: StatefulList::new(vec![]), error: None, - init: false, logs_parsed: false, - show_error: false, sorted_by: None, } } diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index d0a3e47..635c8dd 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -19,7 +19,7 @@ use crate::{ app_data::{AppData, ContainerId, DockerControls}, app_error::AppError, parse_args::CliArgs, - ui::GuiState, + ui::{GuiState, Status}, }; mod message; pub use message::DockerMessage; @@ -336,32 +336,40 @@ impl DockerData { tokio::time::sleep(std::time::Duration::from_millis(100)).await; self.initialised = self.app_data.lock().initialised(&all_ids); } - self.app_data.lock().init = true; + // self.app_data.lock().init = true; + self.gui_state.lock().set_status(Status::Normal); self.stop_loading_spin(&loading_spin, loading_uuid); } + /// Set the global error as the dockererror, and set gui_state to errro + fn set_error(&mut self, error: DockerControls) { + self.app_data + .lock() + .set_error(AppError::DockerCommand(error)); + self.gui_state.lock().set_status(Status::Error); + } + /// Handle incoming messages, container controls & all container information update async fn message_handler(&mut self) { while let Some(message) = self.receiver.recv().await { let docker = Arc::clone(&self.docker); - let app_data = Arc::clone(&self.app_data); let loading_uuid = Uuid::new_v4(); match message { DockerMessage::Pause(id) => { let loading_spin = self.loading_spin(loading_uuid).await; if docker.pause_container(id.get()).await.is_err() { - app_data - .lock() - .set_error(AppError::DockerCommand(DockerControls::Pause)); + self.set_error(DockerControls::Pause); }; self.stop_loading_spin(&loading_spin, loading_uuid); } DockerMessage::Restart(id) => { + // DEBUG + // self.set_error(DockerControls::Restart); + // DEBUG + let loading_spin = self.loading_spin(loading_uuid).await; if docker.restart_container(id.get(), None).await.is_err() { - app_data - .lock() - .set_error(AppError::DockerCommand(DockerControls::Restart)); + self.set_error(DockerControls::Restart); }; self.stop_loading_spin(&loading_spin, loading_uuid); } @@ -372,27 +380,21 @@ impl DockerData { .await .is_err() { - app_data - .lock() - .set_error(AppError::DockerCommand(DockerControls::Start)); + self.set_error(DockerControls::Start); }; self.stop_loading_spin(&loading_spin, loading_uuid); } DockerMessage::Stop(id) => { let loading_spin = self.loading_spin(loading_uuid).await; if docker.stop_container(id.get(), None).await.is_err() { - app_data - .lock() - .set_error(AppError::DockerCommand(DockerControls::Stop)); + self.set_error(DockerControls::Stop); }; self.stop_loading_spin(&loading_spin, loading_uuid); } DockerMessage::Unpause(id) => { let loading_spin = self.loading_spin(loading_uuid).await; if docker.unpause_container(id.get()).await.is_err() { - app_data - .lock() - .set_error(AppError::DockerCommand(DockerControls::Unpause)); + self.set_error(DockerControls::Unpause); }; self.stop_loading_spin(&loading_spin, loading_uuid); self.update_everything().await; diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 91f2418..beb01ad 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -21,7 +21,7 @@ use crate::{ app_data::{AppData, DockerControls, Header, SortedOrder}, app_error::AppError, docker_data::DockerMessage, - ui::{GuiState, SelectablePanel}, + ui::{GuiState, SelectablePanel, Status}, }; pub use message::InputMessages; @@ -64,10 +64,11 @@ impl InputHandler { match message { InputMessages::ButtonPress(key_code) => self.button_press(key_code).await, InputMessages::MouseEvent(mouse_event) => { - let show_error = self.app_data.lock().show_error; - let show_info = self.gui_state.lock().show_help; - if !show_error && !show_info { - self.mouse_press(mouse_event); + // let show_error = self.app_data.lock().show_error; + let status = self.gui_state.lock().get_status(); + match status { + Status::Error | Status::Help => (), + _ => self.mouse_press(mouse_event), } } } @@ -85,10 +86,11 @@ impl InputHandler { .gui_state .lock() .set_info_box("✖ mouse capture disabled".to_owned()), - Err(_) => self - .app_data - .lock() - .set_error(AppError::MouseCapture(false)), + Err(_) => { + self.app_data + .lock() + .set_error(AppError::MouseCapture(false)); + } } } else { match execute!(std::io::stdout(), EnableMouseCapture) { @@ -96,7 +98,9 @@ impl InputHandler { .gui_state .lock() .set_info_box("✓ mouse capture enabled".to_owned()), - Err(_) => self.app_data.lock().set_error(AppError::MouseCapture(true)), + Err(_) => { + self.app_data.lock().set_error(AppError::MouseCapture(true)); + } } }; @@ -129,147 +133,155 @@ impl InputHandler { } /// Send a quit message to docker, to abort all spawns, if an error is return, set is_running to false here instead - async fn quit(&self) { - match self.docker_sender.send(DockerMessage::Quit).await { - Ok(_) => (), - Err(_) => self.is_running.store(false, Ordering::SeqCst), + async fn quit(&self, status: Status) { + match status { + Status::Error | Status::Init => self.is_running.store(false, Ordering::SeqCst), + _ => { + if self.docker_sender.send(DockerMessage::Quit).await.is_err() { + self.is_running.store(false, Ordering::SeqCst) + } + } } + + // match self.docker_sender.send(DockerMessage::Quit).await { + // Ok(_) => (), + // Err(_) => self.is_running.store(false, Ordering::SeqCst), + // } } /// Handle any keyboard button events #[allow(clippy::too_many_lines)] async fn button_press(&mut self, key_code: KeyCode) { - let show_error = self.app_data.lock().show_error; - let show_info = self.gui_state.lock().show_help; + let status = self.gui_state.lock().get_status(); - if show_error { - match key_code { - KeyCode::Char('q' | 'Q') => self.quit().await, + match status { + Status::Error => match key_code { + KeyCode::Char('q' | 'Q') => self.quit(status).await, KeyCode::Char('c' | 'C') => { - self.app_data.lock().show_error = false; self.app_data.lock().remove_error(); + self.gui_state.lock().set_status(Status::Normal); } _ => (), - } - } else if show_info { - match key_code { - KeyCode::Char('q' | 'Q') => self.quit().await, - KeyCode::Char('h' | 'H') => self.gui_state.lock().show_help = false, + }, + Status::Help => match key_code { + KeyCode::Char('q' | 'Q') => self.quit(status).await, + KeyCode::Char('h' | 'H') => self.gui_state.lock().set_status(Status::Help), KeyCode::Char('m' | 'M') => self.m_key(), _ => (), - } - } else { - match key_code { - KeyCode::Char('0') => self.app_data.lock().set_sorted(None), - KeyCode::Char('1') => self.sort(Header::State), - KeyCode::Char('2') => self.sort(Header::Status), - KeyCode::Char('3') => self.sort(Header::Cpu), - KeyCode::Char('4') => self.sort(Header::Memory), - KeyCode::Char('5') => self.sort(Header::Id), - KeyCode::Char('6') => self.sort(Header::Name), - KeyCode::Char('7') => self.sort(Header::Image), - KeyCode::Char('8') => self.sort(Header::Rx), - KeyCode::Char('9') => self.sort(Header::Tx), - KeyCode::Char('q' | 'Q') => self.quit().await, - KeyCode::Char('h' | 'H') => self.gui_state.lock().show_help = true, - KeyCode::Char('m' | 'M') => self.m_key(), - KeyCode::Tab => { - // Skip control panel if no containers, could be refactored - let has_containers = self.app_data.lock().get_container_len() == 0; - let is_containers = - self.gui_state.lock().selected_panel == SelectablePanel::Containers; - let count = if has_containers && is_containers { - 2 - } else { - 1 - }; - for _ in 0..count { - self.gui_state.lock().next_panel(); + }, + _ => { + match key_code { + KeyCode::Char('0') => self.app_data.lock().set_sorted(None), + KeyCode::Char('1') => self.sort(Header::State), + KeyCode::Char('2') => self.sort(Header::Status), + KeyCode::Char('3') => self.sort(Header::Cpu), + KeyCode::Char('4') => self.sort(Header::Memory), + KeyCode::Char('5') => self.sort(Header::Id), + KeyCode::Char('6') => self.sort(Header::Name), + KeyCode::Char('7') => self.sort(Header::Image), + KeyCode::Char('8') => self.sort(Header::Rx), + KeyCode::Char('9') => self.sort(Header::Tx), + KeyCode::Char('q' | 'Q') => self.quit(status).await, + KeyCode::Char('h' | 'H') => self.gui_state.lock().set_status(Status::Help), + KeyCode::Char('m' | 'M') => self.m_key(), + KeyCode::Tab => { + // Skip control panel if no containers, could be refactored + let has_containers = self.app_data.lock().get_container_len() == 0; + let is_containers = + self.gui_state.lock().selected_panel == SelectablePanel::Containers; + let count = if has_containers && is_containers { + 2 + } else { + 1 + }; + for _ in 0..count { + self.gui_state.lock().next_panel(); + } } - } - KeyCode::BackTab => { - // Skip control panel if no containers, could be refactored - let has_containers = self.app_data.lock().get_container_len() == 0; - let is_containers = - self.gui_state.lock().selected_panel == SelectablePanel::Logs; - let count = if has_containers && is_containers { - 2 - } else { - 1 - }; - for _ in 0..count { - self.gui_state.lock().previous_panel(); + KeyCode::BackTab => { + // Skip control panel if no containers, could be refactored + let has_containers = self.app_data.lock().get_container_len() == 0; + let is_containers = + self.gui_state.lock().selected_panel == SelectablePanel::Logs; + let count = if has_containers && is_containers { + 2 + } else { + 1 + }; + for _ in 0..count { + self.gui_state.lock().previous_panel(); + } } - } - KeyCode::Home => { - let mut locked_data = self.app_data.lock(); - match self.gui_state.lock().selected_panel { - SelectablePanel::Containers => locked_data.containers.start(), - SelectablePanel::Logs => locked_data.log_start(), - SelectablePanel::Commands => locked_data.docker_command_start(), + KeyCode::Home => { + let mut locked_data = self.app_data.lock(); + match self.gui_state.lock().selected_panel { + SelectablePanel::Containers => locked_data.containers.start(), + SelectablePanel::Logs => locked_data.log_start(), + SelectablePanel::Commands => locked_data.docker_command_start(), + } } - } - KeyCode::End => { - let mut locked_data = self.app_data.lock(); - match self.gui_state.lock().selected_panel { - SelectablePanel::Containers => locked_data.containers.end(), - SelectablePanel::Logs => locked_data.log_end(), - SelectablePanel::Commands => locked_data.docker_command_end(), + KeyCode::End => { + let mut locked_data = self.app_data.lock(); + match self.gui_state.lock().selected_panel { + SelectablePanel::Containers => locked_data.containers.end(), + SelectablePanel::Logs => locked_data.log_end(), + SelectablePanel::Commands => locked_data.docker_command_end(), + } } - } - KeyCode::Up | KeyCode::Char('k' | 'K') => self.previous(), - KeyCode::PageUp => { - for _ in 0..=6 { - self.previous(); + KeyCode::Up | KeyCode::Char('k' | 'K') => self.previous(), + KeyCode::PageUp => { + for _ in 0..=6 { + self.previous(); + } } - } - KeyCode::Down | KeyCode::Char('j' | 'J') => self.next(), - KeyCode::PageDown => { - for _ in 0..=6 { - self.next(); + KeyCode::Down | KeyCode::Char('j' | 'J') => self.next(), + KeyCode::PageDown => { + for _ in 0..=6 { + self.next(); + } } - } - KeyCode::Enter => { - // 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(); + KeyCode::Enter => { + // 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(); - if let Some(command) = option_command { - let option_id = self.app_data.lock().get_selected_container_id(); - if let Some(id) = option_id { - match command { - DockerControls::Pause => self - .docker_sender - .send(DockerMessage::Pause(id)) - .await - .unwrap_or(()), - DockerControls::Unpause => self - .docker_sender - .send(DockerMessage::Unpause(id)) - .await - .unwrap_or(()), - DockerControls::Start => self - .docker_sender - .send(DockerMessage::Start(id)) - .await - .unwrap_or(()), - DockerControls::Stop => self - .docker_sender - .send(DockerMessage::Stop(id)) - .await - .unwrap_or(()), - DockerControls::Restart => self - .docker_sender - .send(DockerMessage::Restart(id)) - .await - .unwrap_or(()), + if let Some(command) = option_command { + let option_id = self.app_data.lock().get_selected_container_id(); + if let Some(id) = option_id { + match command { + DockerControls::Pause => self + .docker_sender + .send(DockerMessage::Pause(id)) + .await + .unwrap_or(()), + DockerControls::Unpause => self + .docker_sender + .send(DockerMessage::Unpause(id)) + .await + .unwrap_or(()), + DockerControls::Start => self + .docker_sender + .send(DockerMessage::Start(id)) + .await + .unwrap_or(()), + DockerControls::Stop => self + .docker_sender + .send(DockerMessage::Stop(id)) + .await + .unwrap_or(()), + DockerControls::Restart => self + .docker_sender + .send(DockerMessage::Restart(id)) + .await + .unwrap_or(()), + } } } } } + _ => (), } - _ => (), } } } diff --git a/src/main.rs b/src/main.rs index ae69b15..cc97e7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ mod input_handler; mod parse_args; mod ui; -use ui::{create_ui, GuiState}; +use ui::{create_ui, GuiState, Status}; fn setup_tracing() { tracing_subscriber::fmt().with_max_level(Level::INFO).init(); @@ -59,9 +59,15 @@ async fn main() { is_running, )); } - Err(_) => app_data.lock().set_error(AppError::DockerConnect), + Err(_) => { + app_data.lock().set_error(AppError::DockerConnect); + docker_gui_state.lock().set_status(Status::DockerConnect) + } }, - Err(_) => app_data.lock().set_error(AppError::DockerConnect), + Err(_) => { + app_data.lock().set_error(AppError::DockerConnect); + docker_gui_state.lock().set_status(Status::DockerConnect) + } } let input_app_data = Arc::clone(&app_data); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 086dad4..4f855d5 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -15,6 +15,7 @@ use tui::{ }; use crate::app_data::{Header, SortedOrder}; +use crate::ui::Status; use crate::{ app_data::{AppData, ByteStats, Columns, CpuStats, State, Stats}, app_error::AppError, @@ -215,37 +216,43 @@ pub fn logs( loading_icon: &str, ) { let block = generate_block(app_data, area, gui_state, SelectablePanel::Logs); + let status = gui_state.lock().get_status(); + match status { + Status::Init => { + let paragraph = Paragraph::new(format!("parsing logs {}", loading_icon)) + .style(Style::default()) + .block(block) + .alignment(Alignment::Center); + f.render_widget(paragraph, area); + } - let init = app_data.lock().init; - if !init { - let paragraph = Paragraph::new(format!("parsing logs {}", loading_icon)) - .style(Style::default()) - .block(block) - .alignment(Alignment::Center); - f.render_widget(paragraph, area); - } else if let Some(index) = index { - let items = app_data.lock().containers.items[index] - .logs - .items - .iter() - .enumerate() - .map(|i| i.1.clone()) - .collect::>(); + _ => { + if let Some(index) = index { + let items = app_data.lock().containers.items[index] + .logs + .items + .iter() + .enumerate() + .map(|i| i.1.clone()) + .collect::>(); - let items = List::new(items) - .block(block) - .highlight_symbol(ARROW) - .highlight_style(Style::default().add_modifier(Modifier::BOLD)); - f.render_stateful_widget( - items, - area, - &mut 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); + let items = List::new(items) + .block(block) + .highlight_symbol(ARROW) + .highlight_style(Style::default().add_modifier(Modifier::BOLD)); + f.render_stateful_widget( + items, + area, + &mut 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); + // } + } + } } } @@ -349,7 +356,7 @@ pub fn heading_bar( gui_state: &Arc>, ) { let block = |fg: Color| Block::default().style(Style::default().bg(Color::Magenta).fg(fg)); - let info_visible = gui_state.lock().show_help; + let info_visible = gui_state.lock().get_status() == Status::Help; f.render_widget(block(Color::Black), area); @@ -430,7 +437,12 @@ pub fn heading_bar( .collect::>(); let suffix = if info_visible { "exit" } else { "show" }; - let info_text = format!("( h ) {} help {}", suffix, MARGIN); + let info_text = format!( + "( h ) {} help {} {:?}", + suffix, + MARGIN, + gui_state.lock().get_status() + ); let info_width = info_text.chars().count(); let column_width = usize::from(area.width) - info_width; @@ -457,8 +469,6 @@ pub fn heading_bar( .alignment(Alignment::Center); f.render_widget(loading_paragraph, split_bar[0]); - - let container_splits = header_data.iter().map(|i| i.2).collect::>(); let headers_section = Layout::default() .direction(Direction::Horizontal) diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index 9279d19..e504095 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -173,6 +173,17 @@ impl fmt::Display for Loading { } } +/// The application can be in these four states +/// Various functions (e.g input handler), operate differently depending upon current Status +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum Status { + Init, + Help, + DockerConnect, + Error, + Normal, +} + /// Global gui_state, stored in an Arc #[derive(Debug, Clone)] pub struct GuiState { @@ -180,8 +191,8 @@ pub struct GuiState { heading_map: HashMap, loading_icon: Loading, is_loading: HashSet, + status: Status, pub selected_panel: SelectablePanel, - pub show_help: bool, pub info_box_text: Option, } impl GuiState { @@ -192,9 +203,9 @@ impl GuiState { heading_map: HashMap::new(), loading_icon: Loading::One, selected_panel: SelectablePanel::Containers, - show_help: false, is_loading: HashSet::new(), info_box_text: None, + status: Status::Init, } } @@ -242,6 +253,14 @@ impl GuiState { }; } + pub fn set_status(&mut self, status: Status) { + self.status = status + } + + pub fn get_status(&self) -> Status { + self.status + } + /// Change to next selectable panel pub fn next_panel(&mut self) { self.selected_panel = self.selected_panel.next(); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index ff54d2d..8b414ac 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -25,7 +25,7 @@ mod draw_blocks; mod gui_state; pub use self::color_match::*; -pub use self::gui_state::{GuiState, SelectablePanel}; +pub use self::gui_state::{GuiState, SelectablePanel, Status}; use crate::{ app_data::AppData, app_error::AppError, docker_data::DockerMessage, input_handler::InputMessages, @@ -56,7 +56,7 @@ pub async fn create_ui( update_duration, ) .await; - terminal.clear()?; + terminal.clear()?; disable_raw_mode()?; execute!( @@ -85,8 +85,9 @@ async fn run_app( let input_poll_rate = std::time::Duration::from_millis(75); // Check for docker connect errors before attempting to draw the gui - let e = app_data.lock().get_error(); - if let Some(AppError::DockerConnect) = e { + let status = gui_state.lock().get_status(); + + if status == Status::DockerConnect { let mut seconds = 5; loop { if seconds < 1 { @@ -138,6 +139,7 @@ async fn run_app( break; } } + // } } Ok(()) } @@ -157,7 +159,7 @@ fn ui( let log_index = app_data.lock().get_selected_log_index(); let sorted_by = app_data.lock().get_sorted(); - let show_help = gui_state.lock().show_help; + let show_help = gui_state.lock().get_status() == Status::Help; let info_text = gui_state.lock().info_box_text.clone(); let loading_icon = gui_state.lock().get_loading(); @@ -241,7 +243,6 @@ fn ui( } if let Some(error) = has_error { - app_data.lock().show_error = true; draw_blocks::error(f, error, None); } } From 9e9d51559a13944622abf4fcbd3bd63766d11467 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 15 Oct 2022 00:23:26 +0000 Subject: [PATCH 07/20] feat: use gui_status for various gui states --- src/app_data/mod.rs | 4 +- src/docker_data/mod.rs | 87 +++++++------ src/input_handler/mod.rs | 256 +++++++++++++++++++-------------------- src/main.rs | 4 +- src/ui/draw_blocks.rs | 82 ++++++------- src/ui/gui_state.rs | 17 +-- src/ui/mod.rs | 6 +- 7 files changed, 227 insertions(+), 229 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 1f4591e..3821f99 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -14,10 +14,8 @@ pub struct AppData { args: CliArgs, error: Option, logs_parsed: bool, - pub containers: StatefulList, - // pub init: bool, - // pub show_error: bool, sorted_by: Option<(Header, SortedOrder)>, + pub containers: StatefulList, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 635c8dd..ed1d4d5 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -3,7 +3,7 @@ use bollard::{ service::ContainerSummary, Docker, }; -use futures_util::StreamExt; +use futures_util::{Future, StreamExt}; use parking_lot::Mutex; use std::{ collections::HashMap, @@ -318,6 +318,7 @@ impl DockerData { // Initialize docker container data, before any messages are received async fn initialise_container_data(&mut self) { + self.gui_state.lock().status_push(Status::Init); let loading_uuid = Uuid::new_v4(); let loading_spin = self.loading_spin(loading_uuid).await; @@ -336,8 +337,7 @@ impl DockerData { tokio::time::sleep(std::time::Duration::from_millis(100)).await; self.initialised = self.app_data.lock().initialised(&all_ids); } - // self.app_data.lock().init = true; - self.gui_state.lock().set_status(Status::Normal); + self.gui_state.lock().status_del(Status::Init); self.stop_loading_spin(&loading_spin, loading_uuid); } @@ -346,57 +346,68 @@ impl DockerData { self.app_data .lock() .set_error(AppError::DockerCommand(error)); - self.gui_state.lock().set_status(Status::Error); + self.gui_state.lock().status_push(Status::Error); + } + + /// Execute docker commands, will start and stop the loading spinner + async fn exec_docker( + &mut self, + docker_fn: impl Future>, + uuid: Uuid, + control: DockerControls, + ) { + let loading_spin = self.loading_spin(uuid).await; + if docker_fn.await.is_err() { + self.set_error(control); + }; + self.stop_loading_spin(&loading_spin, uuid); } /// Handle incoming messages, container controls & all container information update async fn message_handler(&mut self) { while let Some(message) = self.receiver.recv().await { - let docker = Arc::clone(&self.docker); let loading_uuid = Uuid::new_v4(); + let docker = Arc::clone(&self.docker); match message { DockerMessage::Pause(id) => { - let loading_spin = self.loading_spin(loading_uuid).await; - if docker.pause_container(id.get()).await.is_err() { - self.set_error(DockerControls::Pause); - }; - self.stop_loading_spin(&loading_spin, loading_uuid); + self.exec_docker( + docker.pause_container(id.get()), + loading_uuid, + DockerControls::Pause, + ) + .await; } DockerMessage::Restart(id) => { - // DEBUG - // self.set_error(DockerControls::Restart); - // DEBUG - - let loading_spin = self.loading_spin(loading_uuid).await; - if docker.restart_container(id.get(), None).await.is_err() { - self.set_error(DockerControls::Restart); - }; - self.stop_loading_spin(&loading_spin, loading_uuid); + self.exec_docker( + docker.restart_container(id.get(), None), + loading_uuid, + DockerControls::Restart, + ) + .await; } DockerMessage::Start(id) => { - let loading_spin = self.loading_spin(loading_uuid).await; - if docker - .start_container(id.get(), None::>) - .await - .is_err() - { - self.set_error(DockerControls::Start); - }; - self.stop_loading_spin(&loading_spin, loading_uuid); + self.exec_docker( + docker.start_container(id.get(), None::>), + loading_uuid, + DockerControls::Start, + ) + .await; } DockerMessage::Stop(id) => { - let loading_spin = self.loading_spin(loading_uuid).await; - if docker.stop_container(id.get(), None).await.is_err() { - self.set_error(DockerControls::Stop); - }; - self.stop_loading_spin(&loading_spin, loading_uuid); + self.exec_docker( + docker.stop_container(id.get(), None), + loading_uuid, + DockerControls::Stop, + ) + .await; } DockerMessage::Unpause(id) => { - let loading_spin = self.loading_spin(loading_uuid).await; - if docker.unpause_container(id.get()).await.is_err() { - self.set_error(DockerControls::Unpause); - }; - self.stop_loading_spin(&loading_spin, loading_uuid); + self.exec_docker( + docker.unpause_container(id.get()), + loading_uuid, + DockerControls::Unpause, + ) + .await; self.update_everything().await; } DockerMessage::Update => self.update_everything().await, diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index beb01ad..9cc6208 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -64,11 +64,12 @@ impl InputHandler { match message { InputMessages::ButtonPress(key_code) => self.button_press(key_code).await, InputMessages::MouseEvent(mouse_event) => { - // let show_error = self.app_data.lock().show_error; - let status = self.gui_state.lock().get_status(); - match status { - Status::Error | Status::Help => (), - _ => self.mouse_press(mouse_event), + let error_or_help = self + .gui_state + .lock() + .status_contains(&[Status::Error, Status::Help]); + if !error_or_help { + self.mouse_press(mouse_event) } } } @@ -133,155 +134,152 @@ impl InputHandler { } /// Send a quit message to docker, to abort all spawns, if an error is return, set is_running to false here instead - async fn quit(&self, status: Status) { - match status { - Status::Error | Status::Init => self.is_running.store(false, Ordering::SeqCst), - _ => { - if self.docker_sender.send(DockerMessage::Quit).await.is_err() { - self.is_running.store(false, Ordering::SeqCst) - } - } + async fn quit(&self) { + let error_init = self + .gui_state + .lock() + .status_contains(&[Status::Error, Status::Init]); + if error_init { + self.is_running.store(false, Ordering::SeqCst); + } else if self.docker_sender.send(DockerMessage::Quit).await.is_err() { + self.is_running.store(false, Ordering::SeqCst) } - - // match self.docker_sender.send(DockerMessage::Quit).await { - // Ok(_) => (), - // Err(_) => self.is_running.store(false, Ordering::SeqCst), - // } } /// Handle any keyboard button events #[allow(clippy::too_many_lines)] async fn button_press(&mut self, key_code: KeyCode) { - let status = self.gui_state.lock().get_status(); + let contains_error = self.gui_state.lock().status_contains(&[Status::Error]); + let contains_help = self.gui_state.lock().status_contains(&[Status::Help]); - match status { - Status::Error => match key_code { - KeyCode::Char('q' | 'Q') => self.quit(status).await, + if contains_error { + match key_code { + KeyCode::Char('q' | 'Q') => self.quit().await, KeyCode::Char('c' | 'C') => { self.app_data.lock().remove_error(); - self.gui_state.lock().set_status(Status::Normal); + self.gui_state.lock().status_del(Status::Error); } _ => (), - }, - Status::Help => match key_code { - KeyCode::Char('q' | 'Q') => self.quit(status).await, - KeyCode::Char('h' | 'H') => self.gui_state.lock().set_status(Status::Help), + } + } else if contains_help { + match key_code { + KeyCode::Char('q' | 'Q') => self.quit().await, + KeyCode::Char('h' | 'H') => self.gui_state.lock().status_del(Status::Help), KeyCode::Char('m' | 'M') => self.m_key(), _ => (), - }, - _ => { - match key_code { - KeyCode::Char('0') => self.app_data.lock().set_sorted(None), - KeyCode::Char('1') => self.sort(Header::State), - KeyCode::Char('2') => self.sort(Header::Status), - KeyCode::Char('3') => self.sort(Header::Cpu), - KeyCode::Char('4') => self.sort(Header::Memory), - KeyCode::Char('5') => self.sort(Header::Id), - KeyCode::Char('6') => self.sort(Header::Name), - KeyCode::Char('7') => self.sort(Header::Image), - KeyCode::Char('8') => self.sort(Header::Rx), - KeyCode::Char('9') => self.sort(Header::Tx), - KeyCode::Char('q' | 'Q') => self.quit(status).await, - KeyCode::Char('h' | 'H') => self.gui_state.lock().set_status(Status::Help), - KeyCode::Char('m' | 'M') => self.m_key(), - KeyCode::Tab => { - // Skip control panel if no containers, could be refactored - let has_containers = self.app_data.lock().get_container_len() == 0; - let is_containers = - self.gui_state.lock().selected_panel == SelectablePanel::Containers; - let count = if has_containers && is_containers { - 2 - } else { - 1 - }; - for _ in 0..count { - self.gui_state.lock().next_panel(); - } + } + } else { + match key_code { + KeyCode::Char('0') => self.app_data.lock().set_sorted(None), + KeyCode::Char('1') => self.sort(Header::State), + KeyCode::Char('2') => self.sort(Header::Status), + KeyCode::Char('3') => self.sort(Header::Cpu), + KeyCode::Char('4') => self.sort(Header::Memory), + KeyCode::Char('5') => self.sort(Header::Id), + KeyCode::Char('6') => self.sort(Header::Name), + KeyCode::Char('7') => self.sort(Header::Image), + KeyCode::Char('8') => self.sort(Header::Rx), + KeyCode::Char('9') => self.sort(Header::Tx), + KeyCode::Char('q' | 'Q') => self.quit().await, + KeyCode::Char('h' | 'H') => self.gui_state.lock().status_push(Status::Help), + KeyCode::Char('m' | 'M') => self.m_key(), + KeyCode::Tab => { + // Skip control panel if no containers, could be refactored + let has_containers = self.app_data.lock().get_container_len() == 0; + let is_containers = + self.gui_state.lock().selected_panel == SelectablePanel::Containers; + let count = if has_containers && is_containers { + 2 + } else { + 1 + }; + for _ in 0..count { + self.gui_state.lock().next_panel(); } - KeyCode::BackTab => { - // Skip control panel if no containers, could be refactored - let has_containers = self.app_data.lock().get_container_len() == 0; - let is_containers = - self.gui_state.lock().selected_panel == SelectablePanel::Logs; - let count = if has_containers && is_containers { - 2 - } else { - 1 - }; - for _ in 0..count { - self.gui_state.lock().previous_panel(); - } + } + KeyCode::BackTab => { + // Skip control panel if no containers, could be refactored + let has_containers = self.app_data.lock().get_container_len() == 0; + let is_containers = + self.gui_state.lock().selected_panel == SelectablePanel::Logs; + let count = if has_containers && is_containers { + 2 + } else { + 1 + }; + for _ in 0..count { + self.gui_state.lock().previous_panel(); } - KeyCode::Home => { - let mut locked_data = self.app_data.lock(); - match self.gui_state.lock().selected_panel { - SelectablePanel::Containers => locked_data.containers.start(), - SelectablePanel::Logs => locked_data.log_start(), - SelectablePanel::Commands => locked_data.docker_command_start(), - } + } + KeyCode::Home => { + let mut locked_data = self.app_data.lock(); + match self.gui_state.lock().selected_panel { + SelectablePanel::Containers => locked_data.containers.start(), + SelectablePanel::Logs => locked_data.log_start(), + SelectablePanel::Commands => locked_data.docker_command_start(), } - KeyCode::End => { - let mut locked_data = self.app_data.lock(); - match self.gui_state.lock().selected_panel { - SelectablePanel::Containers => locked_data.containers.end(), - SelectablePanel::Logs => locked_data.log_end(), - SelectablePanel::Commands => locked_data.docker_command_end(), - } + } + KeyCode::End => { + let mut locked_data = self.app_data.lock(); + match self.gui_state.lock().selected_panel { + SelectablePanel::Containers => locked_data.containers.end(), + SelectablePanel::Logs => locked_data.log_end(), + SelectablePanel::Commands => locked_data.docker_command_end(), } - KeyCode::Up | KeyCode::Char('k' | 'K') => self.previous(), - KeyCode::PageUp => { - for _ in 0..=6 { - self.previous(); - } + } + KeyCode::Up | KeyCode::Char('k' | 'K') => self.previous(), + KeyCode::PageUp => { + for _ in 0..=6 { + self.previous(); } - KeyCode::Down | KeyCode::Char('j' | 'J') => self.next(), - KeyCode::PageDown => { - for _ in 0..=6 { - self.next(); - } + } + KeyCode::Down | KeyCode::Char('j' | 'J') => self.next(), + KeyCode::PageDown => { + for _ in 0..=6 { + self.next(); } - KeyCode::Enter => { - // 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(); + } + KeyCode::Enter => { + // 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(); - if let Some(command) = option_command { - let option_id = self.app_data.lock().get_selected_container_id(); - if let Some(id) = option_id { - match command { - DockerControls::Pause => self - .docker_sender - .send(DockerMessage::Pause(id)) - .await - .unwrap_or(()), - DockerControls::Unpause => self - .docker_sender - .send(DockerMessage::Unpause(id)) - .await - .unwrap_or(()), - DockerControls::Start => self - .docker_sender - .send(DockerMessage::Start(id)) - .await - .unwrap_or(()), - DockerControls::Stop => self - .docker_sender - .send(DockerMessage::Stop(id)) - .await - .unwrap_or(()), - DockerControls::Restart => self - .docker_sender - .send(DockerMessage::Restart(id)) - .await - .unwrap_or(()), - } + if let Some(command) = option_command { + let option_id = self.app_data.lock().get_selected_container_id(); + if let Some(id) = option_id { + match command { + DockerControls::Pause => self + .docker_sender + .send(DockerMessage::Pause(id)) + .await + .unwrap_or(()), + DockerControls::Unpause => self + .docker_sender + .send(DockerMessage::Unpause(id)) + .await + .unwrap_or(()), + DockerControls::Start => self + .docker_sender + .send(DockerMessage::Start(id)) + .await + .unwrap_or(()), + DockerControls::Stop => self + .docker_sender + .send(DockerMessage::Stop(id)) + .await + .unwrap_or(()), + DockerControls::Restart => self + .docker_sender + .send(DockerMessage::Restart(id)) + .await + .unwrap_or(()), } } } } - _ => (), } + _ => (), } } } diff --git a/src/main.rs b/src/main.rs index cc97e7c..9d03204 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,12 +61,12 @@ async fn main() { } Err(_) => { app_data.lock().set_error(AppError::DockerConnect); - docker_gui_state.lock().set_status(Status::DockerConnect) + docker_gui_state.lock().status_push(Status::DockerConnect) } }, Err(_) => { app_data.lock().set_error(AppError::DockerConnect); - docker_gui_state.lock().set_status(Status::DockerConnect) + docker_gui_state.lock().status_push(Status::DockerConnect) } } let input_app_data = Arc::clone(&app_data); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 4f855d5..e827cdb 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -216,43 +216,36 @@ pub fn logs( loading_icon: &str, ) { let block = generate_block(app_data, area, gui_state, SelectablePanel::Logs); - let status = gui_state.lock().get_status(); - match status { - Status::Init => { - let paragraph = Paragraph::new(format!("parsing logs {}", loading_icon)) - .style(Style::default()) - .block(block) - .alignment(Alignment::Center); - f.render_widget(paragraph, area); - } + let contains_init = gui_state.lock().status_contains(&[Status::Init]); + if contains_init { + let paragraph = Paragraph::new(format!("parsing logs {}", loading_icon)) + .style(Style::default()) + .block(block) + .alignment(Alignment::Center); + f.render_widget(paragraph, area); + } else if let Some(index) = index { + let items = app_data.lock().containers.items[index] + .logs + .items + .iter() + .enumerate() + .map(|i| i.1.clone()) + .collect::>(); - _ => { - if let Some(index) = index { - let items = app_data.lock().containers.items[index] - .logs - .items - .iter() - .enumerate() - .map(|i| i.1.clone()) - .collect::>(); - - let items = List::new(items) - .block(block) - .highlight_symbol(ARROW) - .highlight_style(Style::default().add_modifier(Modifier::BOLD)); - f.render_stateful_widget( - items, - area, - &mut 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); - // } - } - } + let items = List::new(items) + .block(block) + .highlight_symbol(ARROW) + .highlight_style(Style::default().add_modifier(Modifier::BOLD)); + f.render_stateful_widget( + items, + area, + &mut 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); } } @@ -356,7 +349,7 @@ pub fn heading_bar( gui_state: &Arc>, ) { let block = |fg: Color| Block::default().style(Style::default().bg(Color::Magenta).fg(fg)); - let info_visible = gui_state.lock().get_status() == Status::Help; + let help_visible = gui_state.lock().status_contains(&[Status::Help]); f.render_widget(block(Color::Black), area); @@ -436,13 +429,8 @@ pub fn heading_bar( }) .collect::>(); - let suffix = if info_visible { "exit" } else { "show" }; - let info_text = format!( - "( h ) {} help {} {:?}", - suffix, - MARGIN, - gui_state.lock().get_status() - ); + let suffix = if help_visible { "exit" } else { "show" }; + let info_text = format!("( h ) {} help {}", suffix, MARGIN,); let info_width = info_text.chars().count(); let column_width = usize::from(area.width) - info_width; @@ -484,10 +472,10 @@ pub fn heading_bar( } // show/hide help - let color = if info_visible { - Color::White - } else { + let color = if help_visible { Color::Black + } else { + Color::White }; let help_paragraph = Paragraph::new(info_text) .block(block(color)) diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index e504095..8a9bc93 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -181,7 +181,6 @@ pub enum Status { Help, DockerConnect, Error, - Normal, } /// Global gui_state, stored in an Arc @@ -191,7 +190,7 @@ pub struct GuiState { heading_map: HashMap, loading_icon: Loading, is_loading: HashSet, - status: Status, + status: HashSet, pub selected_panel: SelectablePanel, pub info_box_text: Option, } @@ -205,7 +204,7 @@ impl GuiState { selected_panel: SelectablePanel::Containers, is_loading: HashSet::new(), info_box_text: None, - status: Status::Init, + status: HashSet::new(), } } @@ -253,12 +252,16 @@ impl GuiState { }; } - pub fn set_status(&mut self, status: Status) { - self.status = status + pub fn status_push(&mut self, status: Status) { + self.status.insert(status); } - pub fn get_status(&self) -> Status { - self.status + pub fn status_del(&mut self, status: Status) { + self.status.remove(&status); + } + + pub fn status_contains(&self, status: &[Status]) -> bool { + status.iter().any(|i| self.status.contains(i)) } /// Change to next selectable panel diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8b414ac..e93a671 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -85,9 +85,9 @@ async fn run_app( let input_poll_rate = std::time::Duration::from_millis(75); // Check for docker connect errors before attempting to draw the gui - let status = gui_state.lock().get_status(); + let status_dockerconnect = gui_state.lock().status_contains(&[Status::DockerConnect]); - if status == Status::DockerConnect { + if status_dockerconnect { let mut seconds = 5; loop { if seconds < 1 { @@ -159,7 +159,7 @@ fn ui( let log_index = app_data.lock().get_selected_log_index(); let sorted_by = app_data.lock().get_sorted(); - let show_help = gui_state.lock().get_status() == Status::Help; + let show_help = gui_state.lock().status_contains(&[Status::Help]); let info_text = gui_state.lock().info_box_text.clone(); let loading_icon = gui_state.lock().get_loading(); From a98e8b7260f00dfa882ded23fd24fdbe941cdbb9 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 00:00:41 +0000 Subject: [PATCH 08/20] chore: cargo fmt --- src/docker_data/mod.rs | 37 +++++++++++-------------------------- src/input_handler/mod.rs | 10 +++++----- src/main.rs | 38 +++++++++++++++++--------------------- src/ui/draw_blocks.rs | 10 +++++++--- src/ui/gui_state.rs | 16 +++++++++------- src/ui/mod.rs | 4 ---- 6 files changed, 49 insertions(+), 66 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index ed1d4d5..f80c516 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -316,7 +316,7 @@ impl DockerData { self.gui_state.lock().remove_loading(loading_uuid); } - // Initialize docker container data, before any messages are received + /// Initialize docker container data, before any messages are received async fn initialise_container_data(&mut self) { self.gui_state.lock().status_push(Status::Init); let loading_uuid = Uuid::new_v4(); @@ -341,7 +341,7 @@ impl DockerData { self.stop_loading_spin(&loading_spin, loading_uuid); } - /// Set the global error as the dockererror, and set gui_state to errro + /// Set the global error as the docker error, and set gui_state to error fn set_error(&mut self, error: DockerControls) { self.app_data .lock() @@ -349,13 +349,13 @@ impl DockerData { self.gui_state.lock().status_push(Status::Error); } - /// Execute docker commands, will start and stop the loading spinner + /// Execute a docker command, will start and stop the loading spinner, and set correct error async fn exec_docker( &mut self, - docker_fn: impl Future>, - uuid: Uuid, + docker_fn: impl Future> + Send, control: DockerControls, ) { + let uuid = Uuid::new_v4(); let loading_spin = self.loading_spin(uuid).await; if docker_fn.await.is_err() { self.set_error(control); @@ -366,21 +366,15 @@ impl DockerData { /// Handle incoming messages, container controls & all container information update async fn message_handler(&mut self) { while let Some(message) = self.receiver.recv().await { - let loading_uuid = Uuid::new_v4(); let docker = Arc::clone(&self.docker); match message { DockerMessage::Pause(id) => { - self.exec_docker( - docker.pause_container(id.get()), - loading_uuid, - DockerControls::Pause, - ) - .await; + self.exec_docker(docker.pause_container(id.get()), DockerControls::Pause) + .await; } DockerMessage::Restart(id) => { self.exec_docker( docker.restart_container(id.get(), None), - loading_uuid, DockerControls::Restart, ) .await; @@ -388,26 +382,17 @@ impl DockerData { DockerMessage::Start(id) => { self.exec_docker( docker.start_container(id.get(), None::>), - loading_uuid, DockerControls::Start, ) .await; } DockerMessage::Stop(id) => { - self.exec_docker( - docker.stop_container(id.get(), None), - loading_uuid, - DockerControls::Stop, - ) - .await; + self.exec_docker(docker.stop_container(id.get(), None), DockerControls::Stop) + .await; } DockerMessage::Unpause(id) => { - self.exec_docker( - docker.unpause_container(id.get()), - loading_uuid, - DockerControls::Unpause, - ) - .await; + self.exec_docker(docker.unpause_container(id.get()), DockerControls::Unpause) + .await; self.update_everything().await; } DockerMessage::Update => self.update_everything().await, diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 9cc6208..fb10171 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -69,7 +69,7 @@ impl InputHandler { .lock() .status_contains(&[Status::Error, Status::Help]); if !error_or_help { - self.mouse_press(mouse_event) + self.mouse_press(mouse_event); } } } @@ -105,7 +105,7 @@ impl InputHandler { } }; - // If the info box sleep handle is currently being executed, as in m is pressed twice within a 4000ms window + // If the info box sleep handle is currently being executed, as in 'm' is pressed twice within a 4000ms window // then cancel the first handle, as a new handle will be invoked if let Some(info_sleep_timer) = self.info_sleep.as_ref() { info_sleep_timer.abort(); @@ -134,21 +134,21 @@ impl InputHandler { } /// Send a quit message to docker, to abort all spawns, if an error is return, set is_running to false here instead + /// If gui_status is Error or Init, then just set the is_running to false immediately, for a quicker exit async fn quit(&self) { let error_init = self .gui_state .lock() .status_contains(&[Status::Error, Status::Init]); - if error_init { + if error_init || self.docker_sender.send(DockerMessage::Quit).await.is_err() { self.is_running.store(false, Ordering::SeqCst); - } else if self.docker_sender.send(DockerMessage::Quit).await.is_err() { - self.is_running.store(false, Ordering::SeqCst) } } /// Handle any keyboard button events #[allow(clippy::too_many_lines)] async fn button_press(&mut self, key_code: KeyCode) { + // TODO - refactor this to a single call, maybe return Error, Help or Normal let contains_error = self.gui_state.lock().status_contains(&[Status::Error]); let contains_help = self.gui_state.lock().status_contains(&[Status::Help]); diff --git a/src/main.rs b/src/main.rs index 9d03204..6f69c8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,29 +45,25 @@ async fn main() { 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 - match Docker::connect_with_socket_defaults() { - Ok(docker) => match docker.ping().await { - Ok(_) => { - let docker = Arc::new(docker); - let is_running = Arc::clone(&is_running); - tokio::spawn(DockerData::init( - args, - docker_app_data, - docker, - docker_gui_state, - docker_rx, - is_running, - )); - } - Err(_) => { - app_data.lock().set_error(AppError::DockerConnect); - docker_gui_state.lock().status_push(Status::DockerConnect) - } - }, - Err(_) => { + if let Ok(docker) = Docker::connect_with_socket_defaults() { + if docker.ping().await.is_ok() { + let docker = Arc::new(docker); + let is_running = Arc::clone(&is_running); + tokio::spawn(DockerData::init( + args, + docker_app_data, + docker, + docker_gui_state, + docker_rx, + is_running, + )); + } else { app_data.lock().set_error(AppError::DockerConnect); - docker_gui_state.lock().status_push(Status::DockerConnect) + docker_gui_state.lock().status_push(Status::DockerConnect); } + } else { + app_data.lock().set_error(AppError::DockerConnect); + docker_gui_state.lock().status_push(Status::DockerConnect); } let input_app_data = Arc::clone(&app_data); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index e827cdb..4ca9be5 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -51,7 +51,9 @@ fn generate_block<'a>( gui_state: &Arc>, panel: SelectablePanel, ) -> Block<'a> { - gui_state.lock().update_map(Region::Panel(panel), area); + gui_state + .lock() + .update_heading_map(Region::Panel(panel), area); let current_selected_panel = gui_state.lock().selected_panel; let title = match panel { SelectablePanel::Containers => { @@ -466,7 +468,9 @@ pub fn heading_bar( // draw the actual header blocks for (index, (paragraph, header, _)) in header_data.into_iter().enumerate() { let rect = headers_section[index]; - gui_state.lock().update_map(Region::Header(header), rect); + gui_state + .lock() + .update_heading_map(Region::Header(header), rect); f.render_widget(paragraph, rect); } } @@ -501,7 +505,7 @@ fn max_line_width(text: &str) -> usize { } /// Draw the help box in the centre of the screen -/// TODO this is message, should make every line it's own renderable span +/// TODO should make every line it's own renderable span pub fn help_box(f: &mut Frame<'_, B>) { let title = format!(" {} ", VERSION); diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index 8a9bc93..9fc0a0a 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -173,7 +173,7 @@ impl fmt::Display for Loading { } } -/// The application can be in these four states +/// The application gui state can be in multiple of these four states at the same time /// Various functions (e.g input handler), operate differently depending upon current Status #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum Status { @@ -237,7 +237,7 @@ impl GuiState { } /// Insert, or updates header area panel into heading_map - pub fn update_map(&mut self, region: Region, area: Rect) { + pub fn update_heading_map(&mut self, region: Region, area: Rect) { match region { Region::Header(header) => self .heading_map @@ -252,16 +252,19 @@ impl GuiState { }; } - pub fn status_push(&mut self, status: Status) { - self.status.insert(status); + /// Check if the current gui_status contains any of the given status' + pub fn status_contains(&self, status: &[Status]) -> bool { + status.iter().any(|i| self.status.contains(i)) } + /// Remove a gui_status into the current gui_status hashset pub fn status_del(&mut self, status: Status) { self.status.remove(&status); } - pub fn status_contains(&self, status: &[Status]) -> bool { - status.iter().any(|i| self.status.contains(i)) + /// Insert a gui_status into the current gui_status hashset + pub fn status_push(&mut self, status: Status) { + self.status.insert(status); } /// Change to next selectable panel @@ -281,7 +284,6 @@ impl GuiState { } /// If is_loading has any entries, return the current loading_icon, else an emtpy string - // Option? pub fn get_loading(&mut self) -> String { if self.is_loading.is_empty() { String::from(" ") diff --git a/src/ui/mod.rs b/src/ui/mod.rs index e93a671..a3d0517 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -83,10 +83,7 @@ async fn run_app( update_duration: Duration, ) -> Result<(), AppError> { let input_poll_rate = std::time::Duration::from_millis(75); - - // Check for docker connect errors before attempting to draw the gui let status_dockerconnect = gui_state.lock().status_contains(&[Status::DockerConnect]); - if status_dockerconnect { let mut seconds = 5; loop { @@ -139,7 +136,6 @@ async fn run_app( break; } } - // } } Ok(()) } From 9cb0c414afc284947fc2b8494504387e4e7edd87 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:22:04 +0000 Subject: [PATCH 09/20] feat: log title show container name, closes #16 --- src/app_data/mod.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 3821f99..cf7887a 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -253,11 +253,20 @@ impl AppData { } /// Get the title for log panel for selected container - /// will be "logs x/x" + /// 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 pub fn get_log_title(&self) -> String { self.get_selected_log_index() .map_or("".to_owned(), |index| { - self.containers.items[index].logs.get_state_title() + let logs_len = self.containers.items[index].logs.get_state_title(); + let mut name = self.containers.items[index].name.clone(); + name.truncate(32); + if logs_len.is_empty() { + format!("- {} ", name) + } else { + format!("{} - {}", logs_len, name) + } }) } From a060d032586a0707ac91cb13d922aae0850449c5 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:23:02 +0000 Subject: [PATCH 10/20] refactor: Cpu+Mem stats use tuple struct --- src/app_data/container_state.rs | 41 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index b8b32b9..eebceb4 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -107,6 +107,7 @@ impl StatefulList { } } + /// Return the current status of the select list, e.g. 2/5, pub fn get_state_title(&self) -> String { if self.items.is_empty() { String::new() @@ -254,13 +255,11 @@ pub trait Stats { /// So can use custom display formatter /// Use trait Stats for use as generic in draw_chart function #[derive(Debug, Default, Clone, Copy)] -pub struct CpuStats { - value: f64, -} +pub struct CpuStats(f64); impl CpuStats { pub const fn new(value: f64) -> Self { - Self { value } + Self(value) } } @@ -268,21 +267,21 @@ impl Eq for CpuStats {} impl PartialEq for CpuStats { fn eq(&self, other: &Self) -> bool { - self.value == other.value + self.0 == other.0 } } impl PartialOrd for CpuStats { fn partial_cmp(&self, other: &Self) -> Option { - self.value.partial_cmp(&other.value) + self.0.partial_cmp(&other.0) } } impl Ord for CpuStats { fn cmp(&self, other: &Self) -> Ordering { - if self.value > other.value { + if self.0 > other.0 { Ordering::Greater - } else if (self.value - other.value).abs() < 0.01 { + } else if (self.0 - other.0).abs() < 0.01 { Ordering::Equal } else { Ordering::Less @@ -292,13 +291,13 @@ impl Ord for CpuStats { impl Stats for CpuStats { fn get_value(&self) -> f64 { - self.value + self.0 } } impl fmt::Display for CpuStats { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let disp = format!("{:05.2}%", self.value); + let disp = format!("{:05.2}%", self.0); write!(f, "{:>x$}", disp, x = f.width().unwrap_or(1)) } } @@ -307,41 +306,39 @@ impl fmt::Display for CpuStats { /// So can use custom display formatter /// Use trait Stats for use as generic in draw_chart function #[derive(Debug, Default, Clone, Copy, Eq)] -pub struct ByteStats { - value: u64, -} +pub struct ByteStats(u64); impl PartialEq for ByteStats { fn eq(&self, other: &Self) -> bool { - self.value == other.value + self.0 == other.0 } } impl PartialOrd for ByteStats { fn partial_cmp(&self, other: &Self) -> Option { - self.value.partial_cmp(&other.value) + self.0.partial_cmp(&other.0) } } impl Ord for ByteStats { fn cmp(&self, other: &Self) -> Ordering { - self.value.cmp(&other.value) + self.0.cmp(&other.0) } } impl ByteStats { pub const fn new(value: u64) -> Self { - Self { value } + Self(value) } pub fn update(&mut self, value: u64) { - self.value = value; + self.0 = value; } } #[allow(clippy::cast_precision_loss)] impl Stats for ByteStats { fn get_value(&self) -> f64 { - self.value as f64 + self.0 as f64 } } @@ -353,7 +350,7 @@ impl fmt::Display for ByteStats { x if x >= ONE_GB => format!("{y:.2} GB", y = as_f64 / ONE_GB), x if x >= ONE_MB => format!("{y:.2} MB", y = as_f64 / ONE_MB), x if x >= ONE_KB => format!("{y:.2} kB", y = as_f64 / ONE_KB), - _ => format!("{} B", self.value), + _ => format!("{} B", self.0), }; write!(f, "{:>x$}", p, x = f.width().unwrap_or(1)) } @@ -426,7 +423,7 @@ impl ContainerItem { self.cpu_stats .iter() .enumerate() - .map(|i| (i.0 as f64, i.1.value as f64)) + .map(|i| (i.0 as f64, i.1.0 as f64)) .collect::>() } @@ -436,7 +433,7 @@ impl ContainerItem { self.mem_stats .iter() .enumerate() - .map(|i| (i.0 as f64, i.1.value as f64)) + .map(|i| (i.0 as f64, i.1.0 as f64)) .collect::>() } From c3e72ae7369a25d903f39e55a4349cb005671dd4 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:23:09 +0000 Subject: [PATCH 11/20] chore: cargo update --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da58232..d183171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.0.10" +version = "4.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1a0a4208c6c483b952ad35c6eed505fc13b46f08f631b81e828084a9318d74" +checksum = "6bf8832993da70a4c6d13c581f4463c2bdda27b9bf1c5498dc4365543abe6d6f" dependencies = [ "atty", "bitflags", @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.10" +version = "4.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce" +checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad" dependencies = [ "heck", "proc-macro-error", @@ -433,9 +433,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "lock_api" @@ -623,9 +623,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -712,9 +712,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" dependencies = [ "itoa", "ryu", @@ -1034,9 +1034,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" @@ -1072,9 +1072,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" dependencies = [ "getrandom", "rand", From 24a791183386db64486137f59ecbeb28738ed692 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:35:32 +0000 Subject: [PATCH 12/20] docs: changelog --- CHANGELOG.md | 13 ++++++++++++- create_release.sh | 14 ++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b16115..7cd41fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,18 @@ +### Chores ++ Cargo update, [c3e72ae7369a25d903f39e55a4349cb005671dd4] ++ create_release.sh v0.1.0, [3c8d59c666bd4cda9ca54989b2f1b48bba17bc57] + +### Features ++ Show container name in log panel title, [9cb0c414afc284947fc2b8494504387e4e7edd87] ++ use gui_state HashSet to keep track of application state, [9e9d51559a13944622abf4fcbd3bd63766d11467] ++ terminal.clear() after run_app finished, [67c49575682cb271fac0998ff377a6504cd0bc86] + +### Refactors ++ CpuStats & MemStats use tuple struct, [a060d032586a0707ac91cb13d922aae0850449c5] + # v0.1.5 ### 2022-10-07 - ### Chores + Update clap to v4, [15597dbe](https://github.com/mrjackwills/oxker/commit/15597dbe6942ec053541398ce0e9dedc10a4d3ea), diff --git a/create_release.sh b/create_release.sh index 799056f..4d316ef 100755 --- a/create_release.sh +++ b/create_release.sh @@ -1,9 +1,8 @@ #!/bin/bash # rust create_release -# v0.1.0 +# v0.1.1 -PACKAGE_NAME='oxker' STAR_LINE='****************************************' CWD=$(pwd) @@ -20,11 +19,6 @@ error_close() { exit 1 } -if [ -z "$PACKAGE_NAME" ] -then - error_close "No package name" -fi - # $1 string - question to ask ask_yn () { printf "%b%s? [y/N]:%b " "${GREEN}" "$1" "${RESET}" @@ -228,7 +222,7 @@ release_flow() { release_continue "git add ." git add . - release_continue "git commit -mg \"chore: release \"${NEW_TAG_WITH_V}\"" + release_continue "git commit -m \"chore: release \"${NEW_TAG_WITH_V}\"" git commit -m "chore: release ${NEW_TAG_WITH_V}" release_continue "git checkout main" @@ -246,8 +240,8 @@ release_flow() { release_continue "git checkout dev" git checkout dev - release_continue "git merge --no-ff main -m 'chore: merge main into dev'" - git merge --no-ff main -m 'chore: merge main into dev' + release_continue "git merge --no-ff main -m \"chore: merge main into dev\"" + git merge --no-ff main -m "chore: merge main into dev" release_continue "git push origin dev" git push origin dev From 278fd84131999f8ead6af7a698a005182f93fd68 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:35:50 +0000 Subject: [PATCH 13/20] docs: changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cd41fe..c1e508b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features + Show container name in log panel title, [9cb0c414afc284947fc2b8494504387e4e7edd87] -+ use gui_state HashSet to keep track of application state, [9e9d51559a13944622abf4fcbd3bd63766d11467] ++ use gui_state HashSet to keep track of application gui state, [9e9d51559a13944622abf4fcbd3bd63766d11467] + terminal.clear() after run_app finished, [67c49575682cb271fac0998ff377a6504cd0bc86] ### Refactors From 89023cd98c372e525403f50ab35350759f46f521 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:42:51 +0000 Subject: [PATCH 14/20] chore: platform.sh indent --- containerised/platform.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/containerised/platform.sh b/containerised/platform.sh index 393769a..86eab51 100644 --- a/containerised/platform.sh +++ b/containerised/platform.sh @@ -4,15 +4,15 @@ case $TARGETARCH in "amd64") - echo "x86_64-unknown-linux-musl" > /.platform - echo "" > /.compiler - ;; + echo "x86_64-unknown-linux-musl" > /.platform + echo "" > /.compiler + ;; "arm64") - echo "aarch64-unknown-linux-musl" > /.platform - echo "gcc-aarch64-linux-gnu" > /.compiler - ;; + echo "aarch64-unknown-linux-musl" > /.platform + echo "gcc-aarch64-linux-gnu" > /.compiler + ;; "arm") - echo "arm-unknown-linux-musleabihf" > /.platform - echo "gcc-arm-linux-gnueabihf" > /.compiler - ;; + echo "arm-unknown-linux-musleabihf" > /.platform + echo "gcc-arm-linux-gnueabihf" > /.compiler + ;; esac \ No newline at end of file From 7d958abf039aab8a5a6db2b3958930e024de3d2a Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:45:08 +0000 Subject: [PATCH 15/20] docs: changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e508b..a04f2f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ + Cargo update, [c3e72ae7369a25d903f39e55a4349cb005671dd4] + create_release.sh v0.1.0, [3c8d59c666bd4cda9ca54989b2f1b48bba17bc57] +### Fixes ++ loading icon shifting error fix, also make icon white, closes #15, [59797685dffa29752a48c98e6cf465884d6d9df6] + ### Features + Show container name in log panel title, [9cb0c414afc284947fc2b8494504387e4e7edd87] + use gui_state HashSet to keep track of application gui state, [9e9d51559a13944622abf4fcbd3bd63766d11467] From 752113672715aa8ecb99eeb09609c4dfddb22a27 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:49:20 +0000 Subject: [PATCH 16/20] docs: changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a04f2f2..8688b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ + loading icon shifting error fix, also make icon white, closes #15, [59797685dffa29752a48c98e6cf465884d6d9df6] ### Features -+ Show container name in log panel title, [9cb0c414afc284947fc2b8494504387e4e7edd87] ++ Show container name in log panel title, closes #16, [9cb0c414afc284947fc2b8494504387e4e7edd87] + use gui_state HashSet to keep track of application gui state, [9e9d51559a13944622abf4fcbd3bd63766d11467] + terminal.clear() after run_app finished, [67c49575682cb271fac0998ff377a6504cd0bc86] From 71ae53cc82d7f073f01c007b8091474586f08f5b Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:53:29 +0000 Subject: [PATCH 17/20] fix: create_release.sh closes sed --- create_release.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/create_release.sh b/create_release.sh index 4d316ef..268acaf 100755 --- a/create_release.sh +++ b/create_release.sh @@ -1,7 +1,7 @@ #!/bin/bash # rust create_release -# v0.1.1 +# v0.1.2 STAR_LINE='****************************************' CWD=$(pwd) @@ -108,8 +108,8 @@ update_release_body_and_changelog () { sed -i -E "s=(\s)\[([0-9a-f]{8})([0-9a-f]{32})\]= [\2](${GIT_REPO_URL}/commit/\2\3),=g" ./CHANGELOG.md # Update changelog to add links to closed issues - comma included! - # "closes [#1]," -> "closes [#1](https:/www.../issues/1),"" - sed -i -r -E "s=closes \[#([0-9]+)\],=closes [#\1](${GIT_REPO_URL}/issues/\1),=g" ./CHANGELOG.md + # "closes #1," -> "closes [#1](https:/www.../issues/1),"" + sed -i -r -E "s=closes \#([0-9]+)\,=closes [#\1](${GIT_REPO_URL}/issues/\1),=g" ./CHANGELOG.md } # update version in cargo.toml, to match selected current version From 438ad770f4a5ecb5f4bbc308066ad9e808f66514 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 02:03:33 +0000 Subject: [PATCH 18/20] chore: uuid updated v1.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bb371ca..446a822 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ tokio = {version = "1.21", features=["full"]} tracing = "0.1" tracing-subscriber = "0.3" tui = "0.19" -uuid = {version = "1.1", features = ["v4", "fast-rng"]} +uuid = {version = "1.2", features = ["v4", "fast-rng"]} [dev-dependencies] From d73b7b864e0c769d0664d913ae75cd2a6241bdfc Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 02:04:03 +0000 Subject: [PATCH 19/20] docs: changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8688b2d..eccdaa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### Chores + Cargo update, [c3e72ae7369a25d903f39e55a4349cb005671dd4] + create_release.sh v0.1.0, [3c8d59c666bd4cda9ca54989b2f1b48bba17bc57] ++ uuid updated to version 1.2, [438ad770f4a5ecb5f4bbc308066ad9e808f66514] ### Fixes + loading icon shifting error fix, also make icon white, closes #15, [59797685dffa29752a48c98e6cf465884d6d9df6] From 283fda69824218e7b5087dabcd73dc5cacbf542b Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 16 Oct 2022 02:15:04 +0000 Subject: [PATCH 20/20] chore: release v0.1.6 --- .github/release-body.md | 21 ++++++++++----------- CHANGELOG.md | 23 +++++++++++++---------- Cargo.lock | 2 +- Cargo.toml | 2 +- src/app_data/container_state.rs | 6 +++--- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/.github/release-body.md b/.github/release-body.md index f83c62b..91c0fff 100644 --- a/.github/release-body.md +++ b/.github/release-body.md @@ -1,21 +1,20 @@ -### 2022-10-07 - +### 2022-10-16 ### Chores -+ Update clap to v4, [15597dbe6942ec053541398ce0e9dedc10a4d3ea] ++ Cargo update, [c3e72ae7369a25d903f39e55a4349cb005671dd4] ++ create_release.sh v0.1.0, [3c8d59c666bd4cda9ca54989b2f1b48bba17bc57] ++ uuid updated to version 1.2, [438ad770f4a5ecb5f4bbc308066ad9e808f66514] -### Docs -+ readme.md updated, [a05bf561cc6d96237f683ab0b3c782d6841974d9] +### Fixes ++ loading icon shifting error fix, also make icon white, closes #15, [59797685dffa29752a48c98e6cf465884d6d9df6] ### Features -+ use newtype construct for container id, [41cbb84f2896f8be2c37eba87e390d998aff7382] ++ Show container name in log panel title, closes #16, [9cb0c414afc284947fc2b8494504387e4e7edd87] ++ use gui_state HashSet to keep track of application gui state, [9e9d51559a13944622abf4fcbd3bd63766d11467] ++ terminal.clear() after run_app finished, [67c49575682cb271fac0998ff377a6504cd0bc86] ### Refactors -+ Impl Copy where able to, [e76878f424d72b943713ef84e95e25fada77d79e] -+ replace async fn with just fn, [17dc604befac75cb9dc0311a0e43f9927fe0ca30] -+ remove pointless clone()'s & variable declarations, [6731002ee42c9460042c2c38aff5101b1bcebbe6] -+ replace String::from("") with String::new(), [62fb22478697cc9a7ab9fb562a724965b437233a] -+ replace map_or_else with map_or, [3e26f292c7dc5e13af4580952767ebe821aa5183], [5660b34d5149dce27706ff6daa90b854e6f84e14] ++ CpuStats & MemStats use tuple struct, [a060d032586a0707ac91cb13d922aae0850449c5] see CHANGELOG.md for more details diff --git a/CHANGELOG.md b/CHANGELOG.md index eccdaa7..98600cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,21 @@ +# v0.1.6 +### 2022-10-16 + ### Chores -+ Cargo update, [c3e72ae7369a25d903f39e55a4349cb005671dd4] -+ create_release.sh v0.1.0, [3c8d59c666bd4cda9ca54989b2f1b48bba17bc57] -+ uuid updated to version 1.2, [438ad770f4a5ecb5f4bbc308066ad9e808f66514] ++ Cargo update, [c3e72ae7](https://github.com/mrjackwills/oxker/commit/c3e72ae7369a25d903f39e55a4349cb005671dd4), ++ create_release.sh v0.1.0, [3c8d59c6](https://github.com/mrjackwills/oxker/commit/3c8d59c666bd4cda9ca54989b2f1b48bba17bc57), ++ uuid updated to version 1.2, [438ad770](https://github.com/mrjackwills/oxker/commit/438ad770f4a5ecb5f4bbc308066ad9e808f66514), ### Fixes -+ loading icon shifting error fix, also make icon white, closes #15, [59797685dffa29752a48c98e6cf465884d6d9df6] ++ loading icon shifting error fix, also make icon white, closes [#15](https://github.com/mrjackwills/oxker/issues/15), [59797685](https://github.com/mrjackwills/oxker/commit/59797685dffa29752a48c98e6cf465884d6d9df6), ### Features -+ Show container name in log panel title, closes #16, [9cb0c414afc284947fc2b8494504387e4e7edd87] -+ use gui_state HashSet to keep track of application gui state, [9e9d51559a13944622abf4fcbd3bd63766d11467] -+ terminal.clear() after run_app finished, [67c49575682cb271fac0998ff377a6504cd0bc86] ++ Show container name in log panel title, closes [#16](https://github.com/mrjackwills/oxker/issues/16), [9cb0c414](https://github.com/mrjackwills/oxker/commit/9cb0c414afc284947fc2b8494504387e4e7edd87), ++ use gui_state HashSet to keep track of application gui state, [9e9d5155](https://github.com/mrjackwills/oxker/commit/9e9d51559a13944622abf4fcbd3bd63766d11467), ++ terminal.clear() after run_app finished, [67c49575](https://github.com/mrjackwills/oxker/commit/67c49575682cb271fac0998ff377a6504cd0bc86), ### Refactors -+ CpuStats & MemStats use tuple struct, [a060d032586a0707ac91cb13d922aae0850449c5] ++ CpuStats & MemStats use tuple struct, [a060d032](https://github.com/mrjackwills/oxker/commit/a060d032586a0707ac91cb13d922aae0850449c5), # v0.1.5 ### 2022-10-07 @@ -153,9 +156,9 @@ ### 2022-04-29 ### Features -+ allow toggling of mouse caputre, to select & copy text with mouse, closes #2, [aec184ea](https://github.com/mrjackwills/oxker/commit/aec184ea22b289e91942a4c3e6a415685884bc47), ++ allow toggling of mouse caputre, to select & copy text with mouse, closes [#2](https://github.com/mrjackwills/oxker/issues/2), [aec184ea](https://github.com/mrjackwills/oxker/commit/aec184ea22b289e91942a4c3e6a415685884bc47), + show id column, [b10f9274](https://github.com/mrjackwills/oxker/commit/b10f927481c9e38a48c1d4b94e744ec48e8b6ba6), -+ draw_popup, using enum to draw in one of 9 areas, closes #6, [1017850a](https://github.com/mrjackwills/oxker/commit/1017850a6cc91328abc1127bdb117495f5e909d8), ++ draw_popup, using enum to draw in one of 9 areas, closes [#6](https://github.com/mrjackwills/oxker/issues/6), [1017850a](https://github.com/mrjackwills/oxker/commit/1017850a6cc91328abc1127bdb117495f5e909d8), + use a message rx/sx for all docker commands, remove update loop, wait for update message from gui instead, [9b70fdfa](https://github.com/mrjackwills/oxker/commit/9b70fdfad7b38361ebee301bdc2545d3f0dfcf9e), ### Fixes diff --git a/Cargo.lock b/Cargo.lock index d183171..c7b84a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,7 +514,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "oxker" -version = "0.1.5" +version = "0.1.6" dependencies = [ "anyhow", "bollard", diff --git a/Cargo.toml b/Cargo.toml index 446a822..08497fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxker" -version = "0.1.5" +version = "0.1.6" edition = "2021" authors = ["Jack Wills "] description = "A simple tui to view & control docker containers" diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index eebceb4..5f7ed01 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -107,7 +107,7 @@ impl StatefulList { } } - /// Return the current status of the select list, e.g. 2/5, + /// Return the current status of the select list, e.g. 2/5, pub fn get_state_title(&self) -> String { if self.items.is_empty() { String::new() @@ -423,7 +423,7 @@ impl ContainerItem { self.cpu_stats .iter() .enumerate() - .map(|i| (i.0 as f64, i.1.0 as f64)) + .map(|i| (i.0 as f64, i.1 .0 as f64)) .collect::>() } @@ -433,7 +433,7 @@ impl ContainerItem { self.mem_stats .iter() .enumerate() - .map(|i| (i.0 as f64, i.1.0 as f64)) + .map(|i| (i.0 as f64, i.1 .0 as f64)) .collect::>() }