diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 3734b34..6e363dd 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -11,6 +11,45 @@ const ONE_KB: f64 = 1000.0; const ONE_MB: f64 = ONE_KB * 1000.0; const ONE_GB: f64 = ONE_MB * 1000.0; +#[derive(Debug, Clone, Eq, Hash, PartialEq)] +pub struct ContainerId(String); + +impl From for ContainerId { + fn from(x: String) -> Self { + Self(x) + } +} + +impl From<&String> for ContainerId { + fn from(x: &String) -> Self { + Self(x.clone()) + } +} + +impl From<&str> for ContainerId { + fn from(x: &str) -> Self { + Self(x.to_owned()) + } +} + +impl ContainerId { + pub fn get(&self) -> &str { + self.0.as_str() + } +} + +impl Ord for ContainerId { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for ContainerId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + #[derive(Debug, Clone)] pub struct StatefulList { pub state: ListState, @@ -326,7 +365,7 @@ pub type CpuTuple = (Vec<(f64, f64)>, CpuStats, State); pub struct ContainerItem { pub cpu_stats: VecDeque, pub docker_controls: StatefulList, - pub id: String, + pub id: ContainerId, pub image: String, pub last_updated: u64, pub logs: StatefulList>, @@ -341,7 +380,7 @@ pub struct ContainerItem { impl ContainerItem { /// Create a new container item - pub fn new(id: String, status: String, image: String, state: State, name: String) -> Self { + pub fn new(id: ContainerId, status: String, image: String, state: State, name: String) -> Self { let mut docker_controls = StatefulList::new(DockerControls::gen_vec(state)); docker_controls.start(); Self { diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index a0ba4d9..f1a8f7a 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -163,20 +163,14 @@ impl AppData { } /// 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 container id /// Only returns None when no containers found. - pub fn get_selected_container_id(&self) -> Option { + pub fn get_selected_container_id(&self) -> Option { let mut output = None; if let Some(index) = self.containers.state.selected() { - let id = self - .containers - .items - .iter() - .skip(index) - .take(1) - .map(|i| i.id.clone()) - .collect::(); - output = Some(id); + if let Some(x) = self.containers.items.get(index) { + output = Some(x.id.clone()); + } } output } @@ -307,7 +301,7 @@ impl AppData { } } - pub fn initialised(&mut self, all_ids: &[(bool, String)]) -> bool { + pub fn initialised(&mut self, all_ids: &[(bool, ContainerId)]) -> bool { let count_is_running = all_ids.iter().filter(|i| i.0).count(); let number_with_cpu_status = self .containers @@ -379,7 +373,7 @@ impl AppData { } /// Get all containers ids - pub fn get_all_ids(&self) -> Vec { + pub fn get_all_ids(&self) -> Vec { self.containers .items .iter() @@ -388,14 +382,14 @@ impl AppData { } /// find container given id - fn get_container_by_id(&mut self, id: &str) -> Option<&mut ContainerItem> { - self.containers.items.iter_mut().find(|i| i.id == id) + fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> { + self.containers.items.iter_mut().find(|i| &i.id == id) } /// Update container mem, cpu, & network stats, in single function so only need to call .lock() once pub fn update_stats( &mut self, - id: &str, + id: &ContainerId, cpu_stat: Option, mem_stat: Option, mem_limit: u64, @@ -435,7 +429,7 @@ impl AppData { if !containers .iter() .filter_map(|i| i.id.as_ref()) - .any(|x| x == id) + .any(|x| ContainerId::from(x) == id.clone()) { // If removed container is currently selected, then change selected to previous // This will default to 0 in any edge cases @@ -476,7 +470,8 @@ impl AppData { .as_ref() .map_or("".to_owned(), std::clone::Clone::clone); - if let Some(current_container) = self.get_container_by_id(id) { + let id = ContainerId::from(id.as_str()); + if let Some(current_container) = self.get_container_by_id(&id) { if current_container.name != name { current_container.name = name; }; @@ -512,7 +507,7 @@ impl AppData { } /// update logs of a given container, based on id - pub fn update_log_by_id(&mut self, output: &[String], id: &str) { + pub fn update_log_by_id(&mut self, output: &[String], id: &ContainerId) { let tz = Self::get_systemtime(); let color = self.args.color; let raw = self.args.raw; diff --git a/src/docker_data/message.rs b/src/docker_data/message.rs index a730830..f7de6f5 100644 --- a/src/docker_data/message.rs +++ b/src/docker_data/message.rs @@ -1,10 +1,12 @@ +use crate::app_data::ContainerId; + #[derive(Debug, Clone)] pub enum DockerMessage { Update, - Start(String), - Restart(String), - Pause(String), - Unpause(String), - Stop(String), + Start(ContainerId), + Restart(ContainerId), + Pause(ContainerId), + Unpause(ContainerId), + Stop(ContainerId), Quit, } diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index c0fcd41..6744f4a 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -16,7 +16,7 @@ use tokio::{sync::mpsc::Receiver, task::JoinHandle}; use uuid::Uuid; use crate::{ - app_data::{AppData, DockerControls}, + app_data::{AppData, ContainerId, DockerControls}, app_error::AppError, parse_args::CliArgs, ui::GuiState, @@ -26,8 +26,8 @@ pub use message::DockerMessage; #[derive(Debug, Clone, Eq, Hash, PartialEq)] enum SpawnId { - Stats((String, Binate)), - Log(String), + Stats((ContainerId, Binate)), + Log(ContainerId), } /// Cpu & Mem stats take twice as long as the update interval to get a value, so will have two being executed at the same time @@ -94,7 +94,7 @@ impl DockerData { /// remove if from spawns hashmap when complete async fn update_container_stat( docker: Arc, - id: String, + id: ContainerId, app_data: Arc>, is_running: bool, spawns: Arc>>>, @@ -102,7 +102,7 @@ impl DockerData { ) { let mut stream = docker .stats( - &id, + id.get(), Some(StatsOptions { stream: false, one_shot: !is_running, @@ -149,7 +149,7 @@ impl DockerData { } /// Update all stats, spawn each container into own tokio::spawn thread - fn update_all_container_stats(&mut self, all_ids: &[(bool, String)]) { + fn update_all_container_stats(&mut self, all_ids: &[(bool, ContainerId)]) { for (is_running, id) in all_ids.iter() { let docker = Arc::clone(&self.docker); let app_data = Arc::clone(&self.app_data); @@ -174,7 +174,7 @@ impl DockerData { /// 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 /// Will ignore any container that contains `oxker` as an entry point - pub async fn update_all_containers(&mut self) -> Vec<(bool, String)> { + pub async fn update_all_containers(&mut self) -> Vec<(bool, ContainerId)> { let containers = self .docker .list_containers(Some(ListContainersOptions:: { @@ -211,7 +211,7 @@ impl DockerData { ( i.state == Some("running".to_owned()) || i.state == Some("restarting".to_owned()), - id.clone(), + ContainerId::from(id.as_str()), ) }) }) @@ -223,7 +223,7 @@ impl DockerData { /// remove if from spawns hashmap when complete async fn update_log( docker: Arc, - id: String, + id: ContainerId, timestamps: bool, since: u64, app_data: Arc>, @@ -232,11 +232,11 @@ impl DockerData { let options = Some(LogsOptions:: { stdout: true, timestamps, - since: since as i64, + since: i64::try_from(since).unwrap_or_default(), ..Default::default() }); - let mut logs = docker.logs(&id, options); + let mut logs = docker.logs(id.get(), options); let mut output = vec![]; while let Some(value) = logs.next().await { @@ -252,7 +252,7 @@ impl DockerData { } /// Update all logs, spawn each container into own tokio::spawn thread - fn init_all_logs(&mut self, all_ids: &[(bool, String)]) { + fn init_all_logs(&mut self, all_ids: &[(bool, ContainerId)]) { for (_, id) in all_ids.iter() { let docker = Arc::clone(&self.docker); let app_data = Arc::clone(&self.app_data); @@ -349,7 +349,7 @@ impl DockerData { match message { DockerMessage::Pause(id) => { let loading_spin = self.loading_spin(loading_uuid).await; - if docker.pause_container(&id).await.is_err() { + if docker.pause_container(id.get()).await.is_err() { app_data .lock() .set_error(AppError::DockerCommand(DockerControls::Pause)); @@ -358,7 +358,7 @@ impl DockerData { } DockerMessage::Restart(id) => { let loading_spin = self.loading_spin(loading_uuid).await; - if docker.restart_container(&id, None).await.is_err() { + if docker.restart_container(id.get(), None).await.is_err() { app_data .lock() .set_error(AppError::DockerCommand(DockerControls::Restart)); @@ -368,7 +368,7 @@ impl DockerData { DockerMessage::Start(id) => { let loading_spin = self.loading_spin(loading_uuid).await; if docker - .start_container(&id, None::>) + .start_container(id.get(), None::>) .await .is_err() { @@ -380,7 +380,7 @@ impl DockerData { } DockerMessage::Stop(id) => { let loading_spin = self.loading_spin(loading_uuid).await; - if docker.stop_container(&id, None).await.is_err() { + if docker.stop_container(id.get(), None).await.is_err() { app_data .lock() .set_error(AppError::DockerCommand(DockerControls::Stop)); @@ -389,7 +389,7 @@ impl DockerData { } DockerMessage::Unpause(id) => { let loading_spin = self.loading_spin(loading_uuid).await; - if docker.unpause_container(&id).await.is_err() { + if docker.unpause_container(id.get()).await.is_err() { app_data .lock() .set_error(AppError::DockerCommand(DockerControls::Unpause)); diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index b253beb..abbaf3d 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -137,6 +137,7 @@ impl InputHandler { } /// 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;