Skip to content

Commit

Permalink
feat: added action editor
Browse files Browse the repository at this point in the history
  • Loading branch information
lilopkins committed Oct 26, 2023
1 parent 07c465e commit 2a06dd1
Show file tree
Hide file tree
Showing 16 changed files with 2,185 additions and 606 deletions.
2 changes: 1 addition & 1 deletion testangel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ rfd = { version = "0.12.0", optional = true, default-features = false, features
octocrab = "0.31.2"
semver = "1.0.19"
relm4 = { version = "0.6", optional = true, features = [ "libadwaita", "gnome_44" ] }
relm4-icons = { version = "0.6", optional = true, features = [ "hourglass", "paper", "play", "menu", "lightbulb", "papyrus-vertical", "puzzle-piece", "question-round", "edit", "plus", "x-circular", "up", "down" ] }
relm4-icons = { version = "0.6", optional = true, features = [ "paper", "play", "menu", "lightbulb", "papyrus-vertical", "puzzle-piece", "question-round", "edit", "plus", "x-circular", "up", "down" ] }
fluent = { version = "0.16.0", optional = true }
fluent-templates = { version = "0.8.0", optional = true }
fuzzy-matcher = { version = "0.3.7", optional = true }
Expand Down
32 changes: 30 additions & 2 deletions testangel/locales/en/main.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ source-from-step = From Step { $step }: { $name }

filetype-all = All files
filetype-flow = { app-name } Flow file
filetype-action = { app-name } Action file
filetype-pdf = PDF files
# Flows
Expand All @@ -52,9 +53,9 @@ flow-action-changed-message = The parameters in { $stepCount ->
flow-save-before = Save this Flow?
flow-save-before-message = This flow has been modified since it was last saved. Would you like to save it before continuing?
flow-saved = Flow saved.
flow-saved = Flow saved
flow-error-saving = Error saving flow
flow-error-opening = Error Opening Flow
flow-error-opening = Error opening flow
flow-save-open-error-io-error = I/O error: { $error }
flow-save-open-error-parsing-error = The flow file is corrupted: { $error }
Expand All @@ -68,6 +69,33 @@ flow-execution-failed-message = Flow failed at step { $step }: { $reason }
flow-step-label = Step { $step }: { $name }
# Actions

action-header-add = Add step
action-header-run = Test action
action-header-more = More...
action-header-new = New action
action-header-open = Open action...
action-header-save = Save action
action-header-save-as = Save action as...
action-header-close = Close action
action-header-about = About { app-name }
action-nothing-open-description = Open an action or add a step to get started
action-save-before = Save this action?
action-save-before-message = This action has been modified since it was last saved. Would you like to save it before continuing?
action-saved = Action saved
action-error-saving = Error saving action
action-error-opening = Error opening action
action-save-open-error-io-error = I/O error: { $error }
action-save-open-error-parsing-error = The action file is corrupted: { $error }
action-save-open-error-serializing-error = The action could not be saved due to an internal serialisation error: { $error }
action-save-open-error-action-not-version-compatible = The action you tried to load is not compatible with this version of { app-name }.
action-save-open-error-missing-instruction = The instruction for step { $step } (with internal ID: { $error }) in this action is missing.
action-step-label = Step { $step }: { $name }
# Execution

report-failed = Failed to produce report
Expand Down
38 changes: 0 additions & 38 deletions testangel/src/next_ui/actions.rs

This file was deleted.

227 changes: 227 additions & 0 deletions testangel/src/next_ui/actions/execution_dialog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
use std::{collections::HashMap, sync::Arc};

use adw::prelude::*;
use relm4::{adw, gtk, Component, ComponentParts, RelmWidgetExt};
use testangel::{
action_loader::ActionMap,
ipc::EngineList,
report_generation::{self, ReportGenerationError},
types::{FlowError, Action},
};
use testangel_ipc::prelude::{Evidence, EvidenceContent, ParameterValue};

use crate::next_ui::{file_filters, lang};

#[derive(Debug)]
pub enum ExecutionDialogCommandOutput {
/// Execution completed with the resulting evidence
Complete(Vec<Evidence>),

/// Execution failed at the given step and for the given reason
Failed(usize, FlowError),
}

#[derive(Debug)]
pub struct ExecutionDialogInit {
pub action: Action,
pub engine_list: Arc<EngineList>,
pub action_map: Arc<ActionMap>,
}

#[derive(Debug)]
pub enum ExecutionDialogInput {
Close,
FailedToGenerateReport(ReportGenerationError),
}

#[derive(Debug)]
pub struct ExecutionDialog;

impl ExecutionDialog {
/// Create the absolute barebones of a message dialog, allowing for custom button and response mapping.
fn create_message_dialog<S>(&self, title: S, message: S) -> adw::MessageDialog
where
S: AsRef<str>,
{
adw::MessageDialog::builder()
.title(title.as_ref())
.heading(title.as_ref())
.body(message.as_ref())
.modal(true)
.build()
}
}

#[relm4::component(pub)]
impl Component for ExecutionDialog {
type Init = ExecutionDialogInit;
type Input = ExecutionDialogInput;
type Output = ();
type CommandOutput = ExecutionDialogCommandOutput;

view! {
#[root]
adw::Window {
set_modal: true,
set_resizable: false,

gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
set_margin_all: 50,

gtk::Spinner {
set_spinning: true,
},
gtk::Label {
set_label: &lang::lookup("flow-execution-running"),
},
},
},
}

fn init(
init: Self::Init,
root: &Self::Root,
sender: relm4::ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = ExecutionDialog;
let widgets = view_output!();
let action = init.action;
let engine_list = init.engine_list.clone();
let action_map = init.action_map.clone();

// TODO: ask for input, then build `ActionConfiguration` and trigger
// sender.spawn_oneshot_command(move || {
// let mut outputs: Vec<HashMap<usize, ParameterValue>> = Vec::new();
// let mut evidence = Vec::new();

// for engine in engine_list.inner() {
// if engine.reset_state().is_err() {
// evidence.push(Evidence {
// label: String::from("WARNING: State Warning"),
// content: EvidenceContent::Textual(String::from("For this test execution, the state couldn't be correctly reset. Some results may not be accurate."))
// });
// }
// }

// for (step, action_config) in action.actions.iter().enumerate() {
// match action_config.execute(
// action_map.clone(),
// engine_list.clone(),
// outputs.clone(),
// ) {
// Ok((output, ev)) => {
// outputs.push(output);
// evidence = [evidence, ev].concat();
// }
// Err(e) => {
// return ExecutionDialogCommandOutput::Failed(step + 1, e);
// }
// }
// }

// ExecutionDialogCommandOutput::Complete(evidence)
// });

ComponentParts { model, widgets }
}

fn update(
&mut self,
message: Self::Input,
sender: relm4::ComponentSender<Self>,
root: &Self::Root,
) {
match message {
ExecutionDialogInput::Close => root.destroy(),
ExecutionDialogInput::FailedToGenerateReport(reason) => {
let dialog = self.create_message_dialog(
lang::lookup("report-failed"),
lang::lookup_with_args("report-failed-message", {
let mut map = HashMap::new();
map.insert("reason", reason.to_string().into());
map
}),
);
dialog.set_transient_for(Some(root));
dialog.add_response("ok", &lang::lookup("ok"));
dialog.set_default_response(Some("ok"));
let sender_c = sender.clone();
dialog.connect_response(None, move |dlg, _response| {
sender_c.input(ExecutionDialogInput::Close);
dlg.close();
});
dialog.set_visible(true);
}
}
}

fn update_cmd(
&mut self,
message: Self::CommandOutput,
sender: relm4::ComponentSender<Self>,
root: &Self::Root,
) {
match message {
ExecutionDialogCommandOutput::Complete(evidence) => {
log::info!("Execution complete.");

// Present save dialog
let dialog = gtk::FileDialog::builder()
.modal(true)
.title(lang::lookup("report-save-title"))
.initial_name(lang::lookup("report-default-name"))
.filters(&file_filters::filter_list(vec![
file_filters::pdfs(),
file_filters::all(),
]))
.build();

let sender_c = sender.clone();
dialog.save(
Some(root),
Some(&relm4::gtk::gio::Cancellable::new()),
move |res| {
if let Ok(file) = res {
let path = file.path().unwrap();
if let Err(e) = report_generation::save_report(
path.with_extension("pdf"),
evidence.clone(),
) {
// Failed to generate report
sender_c.input(ExecutionDialogInput::FailedToGenerateReport(e));
return;
} else if let Err(e) = opener::open(path.with_extension("pdf")) {
log::warn!("Failed to open evidence: {e}");
}
}
sender_c.input(ExecutionDialogInput::Close);
},
);
}

ExecutionDialogCommandOutput::Failed(step, reason) => {
log::warn!("Execution failed");
let dialog = self.create_message_dialog(
lang::lookup("flow-execution-failed"),
lang::lookup_with_args("flow-execution-failed-message", {
let mut map = HashMap::new();
map.insert("step", step.into());
map.insert("reason", reason.to_string().into());
map
}),
);
dialog.set_transient_for(Some(root));
dialog.add_response("ok", &lang::lookup("ok"));
dialog.set_default_response(Some("ok"));
let sender_c = sender.clone();
dialog.connect_response(None, move |dlg, _response| {
sender_c.input(ExecutionDialogInput::Close);
dlg.close();
});
dialog.set_visible(true);
}
}
}
}
Loading

0 comments on commit 2a06dd1

Please sign in to comment.