diff --git a/src/main.rs b/src/main.rs index c79dd77..e216388 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,18 @@ -use relm4::{factory::FactoryVec, RelmApp}; -use toolbx::ToolbxContainer; -use ui::app::model::{AppModel}; +use std::collections::VecDeque; + +use relm4::{ + factory::{FactoryVecDeque}, + RelmApp, +}; +use toolbx::ToolbxContainer; +use ui::app::model::AppModel; -mod ui; mod toolbx; +mod ui; fn main() { - let toolbx_list = ToolbxContainer::get_toolboxes(); - let mut factory_vec = FactoryVec::from_vec(toolbx_list); + let toolbx_list = VecDeque::from(ToolbxContainer::get_toolboxes()); + let factory_vec = FactoryVecDeque::from_vec_deque(toolbx_list); let model = AppModel { toolboxes: factory_vec, diff --git a/src/toolbx/mod.rs b/src/toolbx/mod.rs index 50cc765..5944049 100644 --- a/src/toolbx/mod.rs +++ b/src/toolbx/mod.rs @@ -1,6 +1,6 @@ use std::{fmt::Display, iter::zip, process::Command, str::FromStr, string::ParseError, sync::Arc}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum ToolbxError { ParseStatusError(String), CommandExecutionError(String), @@ -13,13 +13,17 @@ impl Display for ToolbxError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ToolbxError::ParseStatusError(parse_error) => write!(f, "{}", parse_error), - ToolbxError::CommandExecutionError(command_exec_error) => write!(f, "{}", command_exec_error), - ToolbxError::CommandUnsuccessfulError(command_unsuc_error) => write!(f, "{}", command_unsuc_error), + ToolbxError::CommandExecutionError(command_exec_error) => { + write!(f, "{}", command_exec_error) + } + ToolbxError::CommandUnsuccessfulError(command_unsuc_error) => { + write!(f, "{}", command_unsuc_error) + } } } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum ToolbxStatus { Running, Configured, @@ -48,7 +52,7 @@ impl FromStr for ToolbxStatus { } } -#[derive(Debug, PartialEq, Default)] +#[derive(Debug, PartialEq, Default, Clone)] pub struct ToolbxContainer { pub id: String, pub name: String, @@ -64,62 +68,90 @@ impl ToolbxContainer { parse_cmd_list_containers(output.as_str()) } - pub fn stop(mut self) -> Result<(), ToolbxError> { + pub fn stop(&mut self) -> Result<(), ToolbxError> { let output = Command::new("podman") .arg("stop") - .arg(self.name) + .arg(self.name.clone()) .output(); if output.is_err() { - return Err(ToolbxError::CommandExecutionError(output.unwrap_err().to_string())) + return Err(ToolbxError::CommandExecutionError( + output.unwrap_err().to_string(), + )); } let output = output.unwrap(); // Success: Output { status: ExitStatus(unix_wait_status(0)), stdout: "tbx_name\n", stderr: "" } - //Fail: - // Output { - // status: ExitStatus(unix_wait_status(32000)), - // stdout: "", - // stderr: "Error: no container with name or ID \"tbx_name\" found: no such container\n" - // } + //Fail: + // Output { + // status: ExitStatus(unix_wait_status(32000)), + // stdout: "", + // stderr: "Error: no container with name or ID \"tbx_name\" found: no such container\n" + // } if output.status.code() == Some(0) { self.status = ToolbxStatus::Exited; Ok(()) } else { - Err(ToolbxError::CommandUnsuccessfulError(String::from_utf8_lossy(&output.stderr).into_owned())) + Err(ToolbxError::CommandUnsuccessfulError( + String::from_utf8_lossy(&output.stderr).into_owned(), + )) } } - pub fn start(mut self) -> Result<(), ToolbxError> { + pub fn start(&mut self) -> Result<(), ToolbxError> { let output = Command::new("podman") - .arg("start") - .arg(self.name) - .output(); + .arg("start") + .arg(self.name.clone()) + .output(); if output.is_err() { - return Err(ToolbxError::CommandExecutionError(output.unwrap_err().to_string())) + return Err(ToolbxError::CommandExecutionError( + output.unwrap_err().to_string(), + )); } let output = output.unwrap(); // Success: status: Output { ExitStatus(unix_wait_status(0)), stdout: "tbx_name\n", stderr: "" } - // Fail: status: - // Output { - // status: ExitStatus(unix_wait_status(32000)), - // stdout: "", - // stderr: "Error: no container with name or ID \"tbx_name\" found: no such container\n" - // } + // Fail: status: + // Output { + // status: ExitStatus(unix_wait_status(32000)), + // stdout: "", + // stderr: "Error: no container with name or ID \"tbx_name\" found: no such container\n" + // } if output.status.code() == Some(0) { self.status = ToolbxStatus::Running; Ok(()) } else { - Err(ToolbxError::CommandUnsuccessfulError(String::from_utf8_lossy(&output.stderr).into_owned())) - } + Err(ToolbxError::CommandUnsuccessfulError( + String::from_utf8_lossy(&output.stderr).into_owned(), + )) + } } } - } +#[test] +fn test_start_1non_existing_containter() { + // TODO: create container that exists based on simple image + // run command + // delete container + //let tbx = ToolbxContainer{created: "".to_string(), id: "".to_string(), name: "latex".to_string(), image: "".to_string(), status: ToolbxStatus::Exited}; + + //tbx.stop(); +} + +#[test] +fn test_start_non_existing_containter() { + let mut tbx = ToolbxContainer { + created: "".to_string(), + id: "".to_string(), + name: "zy2lM6BdZoTnKHaVPkUJ".to_string(), + image: "".to_string(), + status: ToolbxStatus::Exited, + }; + + assert_eq!(Ok(()), tbx.start()); } pub fn run_cmd_toolbx_list_containers() -> String { @@ -180,8 +212,8 @@ fn tokenize_line_list_containers(line: &str) -> Vec { #[test] fn test_tokenize_line_list_containers() { - let toolbox_cmd_container_header = - "ae05203091ab rust 4 months ago running registry.fedoraproject.org/fedora-toolbox:35"; + let toolbox_cmd_container_header = "ae05203091ab rust 4 months ago \ + running registry.fedoraproject.org/fedora-toolbox:35"; let target = vec![ "ae05203091ab", @@ -210,8 +242,8 @@ fn parse_line_list_containers(line: &str) -> Result, + pub toolboxes: FactoryVecDeque, } impl Model for AppModel { diff --git a/src/ui/app/toolbox_list.rs b/src/ui/app/toolbox_list.rs index 7a3f078..2741461 100644 --- a/src/ui/app/toolbox_list.rs +++ b/src/ui/app/toolbox_list.rs @@ -4,7 +4,7 @@ use relm4::{ prelude::{BoxExt, ButtonExt, WidgetExt}, traits::{ActionRowExt, PreferencesRowExt}, }, - factory::{FactoryPrototype, FactoryVec}, + factory::{DynamicIndex, FactoryPrototype, FactoryVecDeque}, gtk, send, view, Sender, }; @@ -24,17 +24,14 @@ pub struct FactoryWidgets { } impl FactoryPrototype for ToolbxContainer { - type Factory = FactoryVec; + type Factory = FactoryVecDeque; type Widgets = FactoryWidgets; type Root = adw::ActionRow; type View = gtk::ListBox; type Msg = AppMsg; - fn init_view( - &self, - key: &>::Key, - sender: Sender, - ) -> Self::Widgets { + fn init_view(&self, key: &DynamicIndex, sender: Sender) -> Self::Widgets { + view! { suffix_box = >k::Box{ append = >k::AspectFrame{ @@ -110,6 +107,8 @@ impl FactoryPrototype for ToolbxContainer { let subtitle = format!("created {}\n{}", self.created, self.image); + let index = key.clone(); + view! { action_row = &adw::ActionRow { set_title: &self.name, @@ -122,6 +121,11 @@ impl FactoryPrototype for ToolbxContainer { set_margin_bottom: 10, set_tooltip_text: Some(status_button_tooltip), set_css_classes: &["circular"], + connect_clicked(sender) => move |btn| { + // Disable button + btn.set_sensitive(false); + send!(sender, AppMsg::ToolbxContainerToggleStartStop(index.clone())); + }, }, }, }, @@ -138,6 +142,7 @@ impl FactoryPrototype for ToolbxContainer { widgets: &Self::Widgets, ) { //widgets.action_row.set_label(&self.name.to_string()); + println!("updated {}", key.current_index()); } fn root_widget(widgets: &Self::Widgets) -> &Self::Root { diff --git a/src/ui/app/update.rs b/src/ui/app/update.rs index 002e711..1ee8a92 100644 --- a/src/ui/app/update.rs +++ b/src/ui/app/update.rs @@ -1,8 +1,11 @@ use relm4::{AppUpdate, Sender}; -use crate::ui::components::{ - toolbox_apps::messages::ToolboxAppDialogMsg, - toolbox_settings::messages::ToolboxSettingsDialogMsg, AppComponents, +use crate::{ + toolbx::ToolbxStatus, + ui::components::{ + toolbox_apps::messages::ToolboxAppDialogMsg, + toolbox_settings::messages::ToolboxSettingsDialogMsg, AppComponents, + }, }; use super::{messages::AppMsg, model::AppModel}; @@ -22,6 +25,19 @@ impl AppUpdate for AppModel { .send(ToolboxAppDialogMsg::Show) .unwrap(); } + AppMsg::ToolbxContainerToggleStartStop(index) => { + if let Some(toolbx_container) = self.toolboxes.get_mut(index.current_index()) { + match toolbx_container.status { + ToolbxStatus::Exited | ToolbxStatus::Configured => { + toolbx_container.start(); + } + ToolbxStatus::Running => { + toolbx_container.stop(); + } + } + // TODO: tell button to reactivate somehow + } + } } true }