feat: runner status communicates exit code; build window shows color coded build status

This commit is contained in:
Gabriele Musco 2023-06-17 15:43:55 +02:00
commit 234f499d2f
No known key found for this signature in database
GPG key ID: 1068D795C80E51DE
4 changed files with 116 additions and 27 deletions

View file

@ -30,7 +30,7 @@ pub struct Runner {
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub enum RunnerStatus { pub enum RunnerStatus {
Running, Running,
Stopped, Stopped(Option<i32>),
} }
macro_rules! logger_thread { macro_rules! logger_thread {
@ -154,10 +154,10 @@ impl Runner {
pub fn status(&mut self) -> RunnerStatus { pub fn status(&mut self) -> RunnerStatus {
match &mut self.process { match &mut self.process {
None => RunnerStatus::Stopped, None => RunnerStatus::Stopped(None),
Some(proc) => match proc.try_wait() { Some(proc) => match proc.try_wait() {
Err(_) => RunnerStatus::Running, Err(_) => RunnerStatus::Running,
Ok(Some(_)) => RunnerStatus::Stopped, Ok(Some(code)) => RunnerStatus::Stopped(code.code()),
Ok(None) => RunnerStatus::Running, Ok(None) => RunnerStatus::Running,
}, },
} }
@ -217,7 +217,7 @@ mod tests {
runner.start(); runner.start();
sleep(time::Duration::from_millis(10)); sleep(time::Duration::from_millis(10));
runner.terminate(); runner.terminate();
assert_eq!(runner.status(), RunnerStatus::Stopped); assert_eq!(runner.status(), RunnerStatus::Stopped(Some(0)));
let out = runner.consume_output(); let out = runner.consume_output();
assert_eq!(out, "REX2TEST: Lorem ipsum dolor\n"); assert_eq!(out, "REX2TEST: Lorem ipsum dolor\n");
} }

View file

@ -5,6 +5,7 @@ use crate::runner::{Runner, RunnerStatus};
pub struct RunnerPipeline { pub struct RunnerPipeline {
runners: Vec<RefCell<Runner>>, runners: Vec<RefCell<Runner>>,
current_index: usize, current_index: usize,
last_exit_status: Option<i32>,
has_started: bool, has_started: bool,
pub log: Vec<String>, pub log: Vec<String>,
} }
@ -19,6 +20,7 @@ impl RunnerPipeline {
runners: c_runners, runners: c_runners,
current_index: 0, current_index: 0,
has_started: false, has_started: false,
last_exit_status: None,
log: vec![], log: vec![],
} }
} }
@ -55,7 +57,11 @@ impl RunnerPipeline {
self.log.extend(log); self.log.extend(log);
match status { match status {
RunnerStatus::Running => {}, RunnerStatus::Running => {},
RunnerStatus::Stopped => { RunnerStatus::Stopped(ecode) => {
match ecode {
None => {} // should never get here
Some(0) => {
self.last_exit_status = Some(0);
self.current_index += 1; self.current_index += 1;
match self.get_current_runner() { match self.get_current_runner() {
None => {}, None => {},
@ -64,6 +70,13 @@ impl RunnerPipeline {
} }
} }
} }
Some(nonzero) => {
self.last_exit_status = Some(nonzero);
// interrupting pipeline by going past last runner
self.current_index = self.runners.len();
}
}
}
} }
} }
} }
@ -73,7 +86,10 @@ impl RunnerPipeline {
self.log.concat() self.log.concat()
} }
pub fn is_running(&self) -> bool { pub fn status(&self) -> RunnerStatus {
self.get_current_runner().is_some() match self.get_current_runner() {
None => RunnerStatus::Stopped(self.last_exit_status),
Some(_) => RunnerStatus::Running
}
} }
} }

View file

@ -1,5 +1,5 @@
use super::about_dialog::AboutDialog; use super::about_dialog::AboutDialog;
use super::build_window::BuildWindow; use super::build_window::{BuildStatus, BuildWindow};
use super::debug_view::{DebugView, DebugViewMsg}; use super::debug_view::{DebugView, DebugViewMsg};
use super::libsurvive_setup_window::LibsurviveSetupWindow; use super::libsurvive_setup_window::LibsurviveSetupWindow;
use super::main_view::MainViewMsg; use super::main_view::MainViewMsg;
@ -161,7 +161,7 @@ impl SimpleComponent for App {
} }
match runner.status() { match runner.status() {
RunnerStatus::Running => {} RunnerStatus::Running => {}
RunnerStatus::Stopped => { RunnerStatus::Stopped(_) => {
self.main_view self.main_view
.sender() .sender()
.emit(MainViewMsg::MonadoActiveChanged(false)); .emit(MainViewMsg::MonadoActiveChanged(false));
@ -176,12 +176,30 @@ impl SimpleComponent for App {
self.build_window self.build_window
.sender() .sender()
.emit(BuildWindowMsg::UpdateContent(pipeline.get_log())); .emit(BuildWindowMsg::UpdateContent(pipeline.get_log()));
if !pipeline.is_running() { match pipeline.status() {
self.setcap_confirm_dialog.present(); RunnerStatus::Running | RunnerStatus::Stopped(None) => {}
RunnerStatus::Stopped(Some(code)) => {
self.build_window self.build_window
.sender() .sender()
.emit(BuildWindowMsg::UpdateCanClose(true)); .emit(BuildWindowMsg::UpdateCanClose(true));
self.build_pipeline.take(); self.build_pipeline.take();
match code {
0 => {
self.build_window.sender().emit(
BuildWindowMsg::UpdateBuildStatus(BuildStatus::Done)
);
self.setcap_confirm_dialog.present();
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateCanClose(true));
}
errcode => self.build_window.sender().emit(
BuildWindowMsg::UpdateBuildStatus(BuildStatus::Error(
format!("Exit status {}", errcode),
)),
),
}
}
} }
} }
}; };
@ -219,7 +237,7 @@ impl SimpleComponent for App {
.sender() .sender()
.emit(MainViewMsg::MonadoActiveChanged(false)); .emit(MainViewMsg::MonadoActiveChanged(false));
} }
RunnerStatus::Stopped => { RunnerStatus::Stopped(_) => {
self.debug_view self.debug_view
.sender() .sender()
.emit(DebugViewMsg::LogUpdated(vec![])); .emit(DebugViewMsg::LogUpdated(vec![]));
@ -301,7 +319,8 @@ impl SimpleComponent for App {
.sender() .sender()
.send(LibsurviveSetupMsg::Present( .send(LibsurviveSetupMsg::Present(
self.get_selected_profile().clone(), self.get_selected_profile().clone(),
)); ))
.expect_dialog("Failed to present Libsurvive Setup Window");
} }
} }
} }

View file

@ -1,17 +1,27 @@
use gtk::prelude::*; use gtk::prelude::*;
use relm4::prelude::*; use relm4::prelude::*;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BuildStatus {
Building,
Done,
Error(String),
}
#[tracker::track] #[tracker::track]
pub struct BuildWindow { pub struct BuildWindow {
title: String, title: String,
content: String, content: String,
can_close: bool, can_close: bool,
build_status: BuildStatus,
#[tracker::do_not_track] #[tracker::do_not_track]
pub textbuf: gtk::TextBuffer, pub textbuf: gtk::TextBuffer,
#[tracker::do_not_track] #[tracker::do_not_track]
pub win: Option<adw::Window>, pub win: Option<adw::Window>,
#[tracker::do_not_track] #[tracker::do_not_track]
build_status_label: Option<gtk::Label>,
#[tracker::do_not_track]
pub scrolled_win: Option<gtk::ScrolledWindow>, pub scrolled_win: Option<gtk::ScrolledWindow>,
} }
@ -19,6 +29,7 @@ pub struct BuildWindow {
pub enum BuildWindowMsg { pub enum BuildWindowMsg {
Present, Present,
UpdateTitle(String), UpdateTitle(String),
UpdateBuildStatus(BuildStatus),
UpdateContent(String), UpdateContent(String),
UpdateCanClose(bool), UpdateCanClose(bool),
} }
@ -34,10 +45,12 @@ impl SimpleComponent for BuildWindow {
adw::Window { adw::Window {
set_modal: true, set_modal: true,
set_default_size: (520, 400), set_default_size: (520, 400),
set_hide_on_close: true,
gtk::Box { gtk::Box {
set_vexpand: true, set_vexpand: true,
set_hexpand: true, set_hexpand: true,
set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
set_spacing: 12,
gtk::WindowHandle { gtk::WindowHandle {
set_vexpand: false, set_vexpand: false,
set_hexpand: true, set_hexpand: true,
@ -48,11 +61,32 @@ impl SimpleComponent for BuildWindow {
#[wrap(Some)] #[wrap(Some)]
set_title_widget: title_label = &gtk::Label { set_title_widget: title_label = &gtk::Label {
#[track = "model.changed(BuildWindow::title())"] #[track = "model.changed(BuildWindow::title())"]
set_label: &model.title, set_markup: &model.title,
add_css_class: "title", add_css_class: "title",
}, },
} }
}, },
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true,
set_halign: gtk::Align::Center,
set_spacing: 12,
#[name(build_status_label)]
gtk::Label {
#[track = "model.changed(BuildWindow::build_status())"]
set_markup: match &model.build_status {
BuildStatus::Building => "Build in progress...".to_string(),
BuildStatus::Done => "Build done, you can close this window".to_string(),
BuildStatus::Error(code) => {
format!("Build failed: \"{c}\"", c = code)
}
}.as_str(),
add_css_class: "title-2",
set_wrap: true,
set_wrap_mode: gtk::pango::WrapMode::Word,
set_justify: gtk::Justification::Center,
}
},
#[name(scrolled_win)] #[name(scrolled_win)]
gtk::ScrolledWindow { gtk::ScrolledWindow {
set_hexpand: true, set_hexpand: true,
@ -65,6 +99,10 @@ impl SimpleComponent for BuildWindow {
set_vexpand: true, set_vexpand: true,
set_editable: false, set_editable: false,
set_monospace: true, set_monospace: true,
set_left_margin: 6,
set_right_margin: 6,
set_top_margin: 6,
set_bottom_margin: 6,
set_buffer: Some(&model.textbuf), set_buffer: Some(&model.textbuf),
}, },
}, },
@ -76,7 +114,8 @@ impl SimpleComponent for BuildWindow {
#[track = "model.changed(BuildWindow::can_close())"] #[track = "model.changed(BuildWindow::can_close())"]
set_sensitive: model.can_close, set_sensitive: model.can_close,
connect_clicked[win] => move |_| { connect_clicked[win] => move |_| {
win.hide();
win.close();
}, },
} }
} }
@ -89,10 +128,11 @@ impl SimpleComponent for BuildWindow {
match message { match message {
BuildWindowMsg::Present => { BuildWindowMsg::Present => {
self.win.as_ref().unwrap().present(); self.win.as_ref().unwrap().present();
}, sender.input(BuildWindowMsg::UpdateBuildStatus(BuildStatus::Building));
}
BuildWindowMsg::UpdateTitle(t) => { BuildWindowMsg::UpdateTitle(t) => {
self.set_title(t); self.set_title(t);
}, }
BuildWindowMsg::UpdateContent(c) => { BuildWindowMsg::UpdateContent(c) => {
if self.content != c { if self.content != c {
self.set_content(c); self.set_content(c);
@ -103,7 +143,18 @@ impl SimpleComponent for BuildWindow {
adj.set_value(adj.upper()); adj.set_value(adj.upper());
sw.set_vadjustment(Some(&adj)); sw.set_vadjustment(Some(&adj));
} }
}, }
BuildWindowMsg::UpdateBuildStatus(status) => {
let label = self.build_status_label.as_ref().unwrap();
label.remove_css_class("success");
label.remove_css_class("error");
match status {
BuildStatus::Done => label.add_css_class("success"),
BuildStatus::Error(_) => label.add_css_class("error"),
_ => {}
}
self.set_build_status(status);
}
BuildWindowMsg::UpdateCanClose(val) => { BuildWindowMsg::UpdateCanClose(val) => {
self.set_can_close(val); self.set_can_close(val);
} }
@ -121,12 +172,15 @@ impl SimpleComponent for BuildWindow {
content: "".into(), content: "".into(),
can_close: false, can_close: false,
textbuf: gtk::TextBuffer::builder().build(), textbuf: gtk::TextBuffer::builder().build(),
build_status: BuildStatus::Building,
win: None, win: None,
build_status_label: None,
scrolled_win: None, scrolled_win: None,
}; };
let widgets = view_output!(); let widgets = view_output!();
model.scrolled_win = Some(widgets.scrolled_win.clone()); model.scrolled_win = Some(widgets.scrolled_win.clone());
model.win = Some(widgets.win.clone()); model.win = Some(widgets.win.clone());
model.build_status_label = Some(widgets.build_status_label.clone());
ComponentParts { model, widgets } ComponentParts { model, widgets }
} }
} }