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

View file

@ -5,6 +5,7 @@ use crate::runner::{Runner, RunnerStatus};
pub struct RunnerPipeline {
runners: Vec<RefCell<Runner>>,
current_index: usize,
last_exit_status: Option<i32>,
has_started: bool,
pub log: Vec<String>,
}
@ -19,6 +20,7 @@ impl RunnerPipeline {
runners: c_runners,
current_index: 0,
has_started: false,
last_exit_status: None,
log: vec![],
}
}
@ -55,12 +57,23 @@ impl RunnerPipeline {
self.log.extend(log);
match status {
RunnerStatus::Running => {},
RunnerStatus::Stopped => {
self.current_index += 1;
match self.get_current_runner() {
None => {},
Some(c_runner) => {
c_runner.borrow_mut().start();
RunnerStatus::Stopped(ecode) => {
match ecode {
None => {} // should never get here
Some(0) => {
self.last_exit_status = Some(0);
self.current_index += 1;
match self.get_current_runner() {
None => {},
Some(c_runner) => {
c_runner.borrow_mut().start();
}
}
}
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()
}
pub fn is_running(&self) -> bool {
self.get_current_runner().is_some()
pub fn status(&self) -> RunnerStatus {
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::build_window::BuildWindow;
use super::build_window::{BuildStatus, BuildWindow};
use super::debug_view::{DebugView, DebugViewMsg};
use super::libsurvive_setup_window::LibsurviveSetupWindow;
use super::main_view::MainViewMsg;
@ -161,7 +161,7 @@ impl SimpleComponent for App {
}
match runner.status() {
RunnerStatus::Running => {}
RunnerStatus::Stopped => {
RunnerStatus::Stopped(_) => {
self.main_view
.sender()
.emit(MainViewMsg::MonadoActiveChanged(false));
@ -176,12 +176,30 @@ impl SimpleComponent for App {
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateContent(pipeline.get_log()));
if !pipeline.is_running() {
self.setcap_confirm_dialog.present();
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateCanClose(true));
self.build_pipeline.take();
match pipeline.status() {
RunnerStatus::Running | RunnerStatus::Stopped(None) => {}
RunnerStatus::Stopped(Some(code)) => {
self.build_window
.sender()
.emit(BuildWindowMsg::UpdateCanClose(true));
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()
.emit(MainViewMsg::MonadoActiveChanged(false));
}
RunnerStatus::Stopped => {
RunnerStatus::Stopped(_) => {
self.debug_view
.sender()
.emit(DebugViewMsg::LogUpdated(vec![]));
@ -301,7 +319,8 @@ impl SimpleComponent for App {
.sender()
.send(LibsurviveSetupMsg::Present(
self.get_selected_profile().clone(),
));
))
.expect_dialog("Failed to present Libsurvive Setup Window");
}
}
}

View file

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