use std::{borrow::BorrowMut, collections::BTreeMap, fmt::Display};
use super::{
block_id::BlockId,
diagnostics::Diagnostic,
types::{Type, Value},
ConstructDid, Did,
};
use serde::Serialize;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BlockEvent {
Action(Block),
Clear,
UpdateActionItems(Vec<NormalizedActionItemRequestUpdate>),
RunbookCompleted,
Exit,
ProgressBar(Block),
UpdateProgressBarStatus(ProgressBarStatusUpdate),
UpdateProgressBarVisibility(ProgressBarVisibilityUpdate),
Modal(Block),
Error(Block),
}
impl BlockEvent {
pub fn as_block(&self) -> Option<&Block> {
match &self {
BlockEvent::Action(ref block) => Some(block),
_ => None,
}
}
pub fn as_modal(&self) -> Option<&Block> {
match &self {
BlockEvent::Modal(ref block) => Some(block),
_ => None,
}
}
pub fn expect_block(&self) -> &Block {
match &self {
BlockEvent::Action(ref block) => block,
_ => unreachable!("block expected"),
}
}
pub fn expect_modal(&self) -> &Block {
match &self {
BlockEvent::Modal(ref block) => block,
_ => unreachable!("block expected"),
}
}
pub fn expect_updated_action_items(&self) -> &Vec<NormalizedActionItemRequestUpdate> {
match &self {
BlockEvent::UpdateActionItems(ref updates) => updates,
_ => unreachable!("block expected"),
}
}
pub fn expect_progress_bar_visibility_update(&self) -> &ProgressBarVisibilityUpdate {
match &self {
BlockEvent::UpdateProgressBarVisibility(ref update) => update,
_ => unreachable!("block expected"),
}
}
pub fn expect_runbook_completed(&self) {
match &self {
BlockEvent::RunbookCompleted => {}
_ => unreachable!("block expected"),
}
}
pub fn new_modal(title: &str, description: &str, groups: Vec<ActionGroup>) -> Block {
Block {
uuid: Uuid::new_v4(),
panel: Panel::new_modal_panel(title, description, groups),
visible: false,
}
}
}
pub enum RunbookExecutionState {
RunbookGenesis,
RunbookGlobalsUpdated,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Block {
pub uuid: Uuid,
#[serde(flatten)]
pub panel: Panel,
pub visible: bool,
}
impl Block {
pub fn new(uuid: &Uuid, panel: Panel) -> Self {
Block { uuid: uuid.clone(), panel, visible: true }
}
pub fn apply_action_item_updates(&mut self, update: NormalizedActionItemRequestUpdate) -> bool {
let mut did_update = false;
match self.panel.borrow_mut() {
Panel::ActionPanel(panel) => {
for group in panel.groups.iter_mut() {
let group_did_update = group.apply_action_item_updates(&update);
if group_did_update {
did_update = true;
}
}
}
Panel::ModalPanel(panel) => {
for group in panel.groups.iter_mut() {
let group_did_update = group.apply_action_item_updates(&update);
if group_did_update {
did_update = true;
}
}
}
_ => {}
};
did_update
}
pub fn update_progress_bar_status(
&mut self,
construct_did: &ConstructDid,
new_status: &ProgressBarStatus,
) {
match self.panel.borrow_mut() {
Panel::ProgressBar(progress_bar) => {
let mut construct_status_found = false;
for construct_statuses in progress_bar.iter_mut() {
if &construct_statuses.construct_did == construct_did {
construct_statuses.push_status(new_status);
construct_status_found = true;
}
}
if !construct_status_found {
progress_bar.push(ConstructProgressBarStatuses {
construct_did: construct_did.clone(),
statuses: vec![new_status.clone()],
})
}
}
_ => {}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NormalizedActionItemRequestUpdate {
pub id: BlockId,
pub action_status: Option<ActionItemStatus>,
pub action_type: Option<ActionItemRequestType>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ActionItemRequestUpdate {
pub id: ActionItemRequestUpdateIdentifier,
pub action_status: Option<ActionItemStatus>,
pub action_type: Option<ActionItemRequestType>,
}
#[derive(Debug, Clone, Serialize)]
pub enum ActionItemRequestUpdateIdentifier {
Id(BlockId),
ConstructDidWithKey((ConstructDid, String)),
}
impl ActionItemRequestUpdate {
pub fn from_id(id: &BlockId) -> Self {
ActionItemRequestUpdate {
id: ActionItemRequestUpdateIdentifier::Id(id.clone()),
action_status: None,
action_type: None,
}
}
pub fn from_context(construct_did: &ConstructDid, internal_key: &str) -> Self {
ActionItemRequestUpdate {
id: ActionItemRequestUpdateIdentifier::ConstructDidWithKey((
construct_did.clone(),
internal_key.to_string(),
)),
action_status: None,
action_type: None,
}
}
pub fn from_diff(
new_item: &ActionItemRequest,
existing_item: &ActionItemRequest,
) -> Option<Self> {
let id_match = new_item.id == existing_item.id;
let status_match = new_item.action_status == existing_item.action_status;
let type_diff = ActionItemRequestType::diff_mutable_properties(
&new_item.action_type,
&existing_item.action_type,
);
if !id_match || (status_match && type_diff.is_none()) {
return None;
}
let mut update = ActionItemRequestUpdate::from_id(&new_item.id);
if !status_match {
update.set_status(new_item.action_status.clone());
}
if let Some(new_type) = type_diff {
update.set_type(new_type);
}
Some(update)
}
pub fn set_status(&mut self, new_status: ActionItemStatus) -> Self {
self.action_status = Some(new_status);
self.clone()
}
pub fn set_type(&mut self, new_type: ActionItemRequestType) -> Self {
self.action_type = Some(new_type);
self.clone()
}
pub fn normalize(
&self,
action_item_requests: &BTreeMap<BlockId, ActionItemRequest>,
) -> Option<NormalizedActionItemRequestUpdate> {
for (_, action) in action_item_requests.iter() {
match &self.id {
ActionItemRequestUpdateIdentifier::Id(id) => {
if action.id.eq(id) {
return Some(NormalizedActionItemRequestUpdate {
id: id.clone(),
action_status: self.action_status.clone(),
action_type: self.action_type.clone(),
});
}
}
ActionItemRequestUpdateIdentifier::ConstructDidWithKey((
construct_did,
internal_key,
)) => {
let Some(ref action_construct_did) = action.construct_did else {
continue;
};
if action_construct_did.eq(construct_did)
&& action.internal_key.eq(internal_key)
{
return Some(NormalizedActionItemRequestUpdate {
id: action.id.clone(),
action_status: self.action_status.clone(),
action_type: self.action_type.clone(),
});
}
}
}
}
None
}
}
impl Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Block {} {{", self.uuid)?;
match &self.panel {
Panel::ActionPanel(panel) => {
writeln!(f, " title: {}", panel.title)?;
for group in panel.groups.iter() {
writeln!(f, " group: {} {{", group.title)?;
for sub_group in group.sub_groups.iter() {
writeln!(f, " sub_group: {{")?;
for item in sub_group.action_items.iter() {
writeln!(f, " title: {:?}", item.title)?;
writeln!(f, " consctruct: {:?}", item.construct_did)?;
writeln!(f, " status: {:?}", item.action_status)?;
writeln!(f, " action: {:?}", item.action_type)?;
writeln!(f, " }}")?;
}
writeln!(f, " }}")?;
}
writeln!(f, " }}")?;
}
writeln!(f, "}}")
}
Panel::ModalPanel(panel) => {
writeln!(f, " title: {}", panel.title)?;
for group in panel.groups.iter() {
writeln!(f, " group: {} {{", group.title)?;
for sub_group in group.sub_groups.iter() {
writeln!(f, " sub_group: {{")?;
for item in sub_group.action_items.iter() {
writeln!(f, " title: {:?}", item.title)?;
writeln!(f, " consctruct: {:?}", item.construct_did)?;
writeln!(f, " status: {:?}", item.action_status)?;
writeln!(f, " action: {:?}", item.action_type)?;
writeln!(f, " }}")?;
}
writeln!(f, " }}")?;
}
writeln!(f, " }}")?;
}
writeln!(f, "}}")
}
_ => {
writeln!(f, "?????")
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "panel")]
pub enum Panel {
ActionPanel(ActionPanelData),
ModalPanel(ModalPanelData),
ProgressBar(Vec<ConstructProgressBarStatuses>),
ErrorPanel(ErrorPanelData),
}
impl Panel {
pub fn new_action_panel(title: &str, description: &str, groups: Vec<ActionGroup>) -> Self {
Panel::ActionPanel(ActionPanelData {
title: title.to_string(),
description: description.to_string(),
groups,
})
}
pub fn new_modal_panel(title: &str, description: &str, groups: Vec<ActionGroup>) -> Self {
Panel::ModalPanel(ModalPanelData {
title: title.to_string(),
description: description.to_string(),
groups,
})
}
pub fn as_action_panel(&self) -> Option<&ActionPanelData> {
match &self {
Panel::ActionPanel(ref data) => Some(data),
_ => None,
}
}
pub fn as_modal_panel(&self) -> Option<&ModalPanelData> {
match &self {
Panel::ModalPanel(ref data) => Some(data),
_ => None,
}
}
pub fn expect_action_panel(&self) -> &ActionPanelData {
match &self {
Panel::ActionPanel(ref data) => data,
_ => panic!("expected action panel, got {:?}", self),
}
}
pub fn expect_modal_panel(&self) -> &ModalPanelData {
match &self {
Panel::ModalPanel(ref data) => data,
_ => panic!("expected action panel, got {:?}", self),
}
}
pub fn expect_modal_panel_mut(&mut self) -> &mut ModalPanelData {
match self {
Panel::ModalPanel(ref mut data) => data,
_ => panic!("expected action panel, got {:?}", self),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionPanelData {
pub title: String,
pub description: String,
pub groups: Vec<ActionGroup>,
}
impl ActionPanelData {
pub fn compile_actions_to_item_updates(
&self,
action_item_requests: &BTreeMap<BlockId, ActionItemRequest>,
) -> Vec<ActionItemRequestUpdate> {
let mut updates = vec![];
for group in self.groups.iter() {
let mut group_updates = group.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut group_updates);
}
updates
}
pub fn filter_existing_action_items(
&mut self,
existing_requests: &Vec<&mut ActionItemRequest>,
) -> &mut Self {
let mut group_idx_to_remove = vec![];
for (i, group) in self.groups.iter_mut().enumerate() {
group.filter_existing_action_items(&existing_requests);
if group.sub_groups.is_empty() {
group_idx_to_remove.push(i);
}
}
group_idx_to_remove.iter().rev().for_each(|i| {
self.groups.remove(*i);
});
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConstructProgressBarStatuses {
pub construct_did: ConstructDid,
pub statuses: Vec<ProgressBarStatus>,
}
impl ConstructProgressBarStatuses {
pub fn new(construct_did: &ConstructDid) -> Self {
ConstructProgressBarStatuses { construct_did: construct_did.clone(), statuses: vec![] }
}
pub fn push_status(&mut self, new_status: &ProgressBarStatus) {
self.statuses.push(new_status.clone());
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProgressBarStatus {
pub status: String,
pub status_color: ProgressBarStatusColor,
pub message: String,
pub diagnostic: Option<Diagnostic>,
}
impl ProgressBarStatus {
pub fn new_msg(status_color: ProgressBarStatusColor, status: &str, message: &str) -> Self {
ProgressBarStatus {
status: status.to_string(),
status_color,
message: message.to_string(),
diagnostic: None,
}
}
pub fn new_err(status: &str, message: &str, diag: &Diagnostic) -> Self {
ProgressBarStatus {
status: status.to_string(),
status_color: ProgressBarStatusColor::Red,
message: message.to_string(),
diagnostic: Some(diag.clone()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProgressBarStatusColor {
Green,
Yellow,
Red,
Purple,
}
impl ProgressBarStatusColor {
pub fn to_string(&self) -> String {
match &self {
ProgressBarStatusColor::Green => "Green".into(),
ProgressBarStatusColor::Yellow => "Yellow".into(),
ProgressBarStatusColor::Red => "Red".into(),
ProgressBarStatusColor::Purple => "Purple".into(),
}
}
}
const PROGRESS_SYMBOLS: [&str; 8] = ["|", "/", "-", "\\", "|", "/", "-", "\\"];
#[derive(Clone, Debug)]
pub struct ProgressSymbol {
current: usize,
}
impl ProgressSymbol {
pub fn new() -> Self {
ProgressSymbol { current: 0 }
}
pub fn new_with_default_index(default_index: usize) -> Self {
ProgressSymbol { current: default_index }
}
pub fn next(&mut self) -> &str {
self.current = (self.current + 1) % PROGRESS_SYMBOLS.len();
PROGRESS_SYMBOLS[self.current]
}
pub fn pending(&mut self) -> String {
format!("Pending {}", self.next())
}
}
#[derive(Debug, Clone)]
pub struct StatusUpdater {
status_update: ProgressBarStatusUpdate,
tx: channel::Sender<BlockEvent>,
progress: ProgressSymbol,
}
impl StatusUpdater {
fn _new(
background_tasks_uuid: &Uuid,
construct_did: &ConstructDid,
tx: &channel::Sender<BlockEvent>,
mut progress: ProgressSymbol,
) -> Self {
let initial_status = ProgressBarStatusUpdate::new(
&background_tasks_uuid,
&construct_did,
&&ProgressBarStatus::new_msg(ProgressBarStatusColor::Yellow, &progress.pending(), ""),
);
StatusUpdater { status_update: initial_status, tx: tx.clone(), progress }
}
pub fn new(
background_tasks_uuid: &Uuid,
construct_did: &ConstructDid,
tx: &channel::Sender<BlockEvent>,
) -> Self {
let progress = ProgressSymbol::new();
Self::_new(background_tasks_uuid, construct_did, tx, progress)
}
pub fn new_with_default_progress_index(
background_tasks_uuid: &Uuid,
construct_did: &ConstructDid,
tx: &channel::Sender<BlockEvent>,
default_progress_index: usize,
) -> Self {
let progress = ProgressSymbol::new_with_default_index(default_progress_index);
Self::_new(background_tasks_uuid, construct_did, tx, progress)
}
pub fn propagate_info(&mut self, info: &str) {
self.status_update.update_status(&ProgressBarStatus::new_msg(
ProgressBarStatusColor::Purple,
"Info",
info,
));
let _ = self.tx.send(BlockEvent::UpdateProgressBarStatus(self.status_update.clone()));
}
pub fn propagate_pending_status(&mut self, new_status_msg: &str) {
self.status_update.update_status(&ProgressBarStatus::new_msg(
ProgressBarStatusColor::Yellow,
&self.progress.pending(),
new_status_msg,
));
let _ = self.tx.send(BlockEvent::UpdateProgressBarStatus(self.status_update.clone()));
}
pub fn propagate_failed_status(&mut self, new_status_msg: &str, diag: &Diagnostic) {
self.status_update.update_status(&ProgressBarStatus::new_err(
"Failed",
new_status_msg,
diag,
));
let _ = self.tx.send(BlockEvent::UpdateProgressBarStatus(self.status_update.clone()));
}
pub fn propagate_success_status(&mut self, status: &str, msg: &str) {
self.status_update.update_status(&ProgressBarStatus::new_msg(
ProgressBarStatusColor::Green,
status,
msg,
));
let _ = self.tx.send(BlockEvent::UpdateProgressBarStatus(self.status_update.clone()));
}
pub fn propagate_status(&mut self, new_status: ProgressBarStatus) {
self.status_update.update_status(&new_status);
let _ = self.tx.send(BlockEvent::UpdateProgressBarStatus(self.status_update.clone()));
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProgressBarStatusUpdate {
pub progress_bar_uuid: Uuid,
pub construct_did: ConstructDid,
pub new_status: ProgressBarStatus,
}
impl ProgressBarStatusUpdate {
pub fn new(
progress_bar_uuid: &Uuid,
construct_did: &ConstructDid,
new_status: &ProgressBarStatus,
) -> Self {
ProgressBarStatusUpdate {
progress_bar_uuid: progress_bar_uuid.clone(),
construct_did: construct_did.clone(),
new_status: new_status.clone(),
}
}
pub fn update_status(&mut self, new_status: &ProgressBarStatus) {
self.new_status = new_status.clone();
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProgressBarVisibilityUpdate {
pub progress_bar_uuid: Uuid,
pub visible: bool,
}
impl ProgressBarVisibilityUpdate {
pub fn new(progress_bar_uuid: &Uuid, visible: bool) -> Self {
ProgressBarVisibilityUpdate { progress_bar_uuid: progress_bar_uuid.clone(), visible }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ModalPanelData {
pub title: String,
pub description: String,
pub groups: Vec<ActionGroup>,
}
impl ModalPanelData {
pub fn compile_actions_to_item_updates(
&self,
action_item_requests: &BTreeMap<BlockId, ActionItemRequest>,
) -> Vec<ActionItemRequestUpdate> {
let mut updates = vec![];
for group in self.groups.iter() {
let mut group_updates = group.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut group_updates);
}
updates
}
pub fn filter_existing_action_items(
&mut self,
existing_requests: &Vec<&mut ActionItemRequest>,
) -> &mut Self {
let mut group_idx_to_remove = vec![];
for (i, group) in self.groups.iter_mut().enumerate() {
group.filter_existing_action_items(&existing_requests);
if group.sub_groups.is_empty() {
group_idx_to_remove.push(i);
}
}
group_idx_to_remove.iter().rev().for_each(|i| {
self.groups.remove(*i);
});
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorPanelData {
pub title: String,
pub description: String,
pub groups: Vec<ActionGroup>,
}
impl ErrorPanelData {
pub fn from_diagnostics(diagnostics: &Vec<Diagnostic>) -> Self {
let mut diag_actions = vec![];
for (i, diag) in diagnostics.iter().enumerate() {
let mut action = ActionItemRequest::new(
&None,
"",
None,
ActionItemStatus::Error(diag.clone()),
ActionItemRequestType::DisplayErrorLog(DisplayErrorLogRequest {
diagnostic: diag.clone(),
}),
"diagnostic",
);
action.index = (i + 1) as u16;
diag_actions.push(action);
}
ErrorPanelData {
title: "EXECUTION ERROR".into(),
description: "Review the following execution errors and restart the runbook.".into(),
groups: vec![ActionGroup {
title: "".into(),
sub_groups: vec![ActionSubGroup {
title: None,
action_items: diag_actions,
allow_batch_completion: false,
}],
}],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionGroup {
pub title: String,
pub sub_groups: Vec<ActionSubGroup>,
}
impl ActionGroup {
pub fn new(title: &str, sub_groups: Vec<ActionSubGroup>) -> Self {
ActionGroup { title: title.to_string(), sub_groups }
}
pub fn contains_validate_modal_item(&self) -> bool {
for sub_group in self.sub_groups.iter() {
for item in sub_group.action_items.iter() {
if let ActionItemRequestType::ValidateModal = item.action_type {
return true;
}
}
}
false
}
pub fn apply_action_item_updates(
&mut self,
update: &NormalizedActionItemRequestUpdate,
) -> bool {
let mut did_update = false;
for sub_group in self.sub_groups.iter_mut() {
for action in sub_group.action_items.iter_mut() {
if action.id == update.id {
if let Some(action_status) = update.action_status.clone() {
if action.action_status != action_status {
action.action_status = action_status;
did_update = true;
}
}
if let Some(action_type) = update.action_type.clone() {
if action.action_type != action_type {
action.action_type = action_type;
did_update = true;
}
}
}
}
}
did_update
}
pub fn compile_actions_to_item_updates(
&self,
action_item_requests: &BTreeMap<BlockId, ActionItemRequest>,
) -> Vec<ActionItemRequestUpdate> {
let mut updates = vec![];
for sub_group in self.sub_groups.iter() {
let mut sub_group_updates =
sub_group.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut sub_group_updates);
}
updates
}
pub fn filter_existing_action_items(
&mut self,
existing_requests: &Vec<&mut ActionItemRequest>,
) -> &mut Self {
let mut sub_group_idx_to_remove = vec![];
for (i, sub_group) in self.sub_groups.iter_mut().enumerate() {
sub_group.filter_existing_action_items(&existing_requests);
if sub_group.action_items.is_empty() {
sub_group_idx_to_remove.push(i);
}
}
sub_group_idx_to_remove.iter().rev().for_each(|i| {
self.sub_groups.remove(*i);
});
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionSubGroup {
pub title: Option<String>,
pub action_items: Vec<ActionItemRequest>,
pub allow_batch_completion: bool,
}
impl ActionSubGroup {
pub fn new(
title: Option<String>,
action_items: Vec<ActionItemRequest>,
allow_batch_completion: bool,
) -> Self {
ActionSubGroup { title, action_items, allow_batch_completion }
}
pub fn contains_validate_modal_item(&self) -> bool {
for item in self.action_items.iter() {
if let ActionItemRequestType::ValidateModal = item.action_type {
return true;
}
}
false
}
pub fn compile_actions_to_item_updates(
&self,
action_item_requests: &BTreeMap<BlockId, ActionItemRequest>,
) -> Vec<ActionItemRequestUpdate> {
let mut updates = vec![];
for new_item in self.action_items.iter() {
if let Some(existing_item) = action_item_requests.get(&new_item.id) {
if let Some(update) = ActionItemRequestUpdate::from_diff(new_item, existing_item) {
updates.push(update);
};
};
}
updates
}
pub fn filter_existing_action_items(
&mut self,
existing_requests: &Vec<&mut ActionItemRequest>,
) -> &mut Self {
let mut action_item_idx_to_remove = vec![];
for (i, new_item) in self.action_items.iter().enumerate() {
for existing_item in existing_requests.iter() {
if existing_item.id.eq(&new_item.id) {
if let None = ActionItemRequestUpdate::from_diff(new_item, existing_item) {
action_item_idx_to_remove.push(i);
};
}
}
}
action_item_idx_to_remove.iter().rev().for_each(|i| {
self.action_items.remove(*i);
});
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionItemRequest {
pub id: BlockId,
pub construct_did: Option<ConstructDid>,
pub index: u16,
pub title: String,
pub description: Option<String>,
pub action_status: ActionItemStatus,
pub action_type: ActionItemRequestType,
pub internal_key: String,
}
impl ActionItemRequest {
pub fn new(
construct_did: &Option<ConstructDid>,
title: &str,
description: Option<String>,
action_status: ActionItemStatus,
action_type: ActionItemRequestType,
internal_key: &str,
) -> Self {
let data = format!(
"{}-{}-{}-{}-{}",
title,
description.clone().unwrap_or("".into()),
internal_key,
construct_did.as_ref().and_then(|did| Some(did.to_string())).unwrap_or("".into()),
action_type.get_block_id_string()
);
let id = BlockId::new(data.as_bytes());
ActionItemRequest {
id,
construct_did: construct_did.clone(),
index: 0,
title: title.to_string(),
description,
action_status,
action_type,
internal_key: internal_key.to_string(),
}
}
}
pub enum ChecklistActionResultProvider {
TermConsole,
LocalWebConsole,
RemoteWebConsole,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "status", content = "data")]
pub enum ActionItemStatus {
Blocked,
Todo,
Success(Option<String>),
InProgress(String),
Error(Diagnostic),
Warning(Diagnostic),
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateConstructData {
pub construct_did: ConstructDid,
pub action_item_update: ActionItemRequestUpdate,
pub internal_key: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OpenModalData {
pub modal_uuid: Uuid,
pub title: String,
}
#[derive(Clone, Debug)]
pub enum ActionType {
UpdateActionItemRequest(ActionItemRequestUpdate),
AppendSubGroup(ActionSubGroup),
AppendGroup(ActionGroup),
AppendItem(ActionItemRequest, Option<String>, Option<String>),
NewBlock(ActionPanelData),
NewModal(Block),
}
#[derive(Clone, Debug)]
pub struct Actions {
pub store: Vec<ActionType>,
}
impl Actions {
pub fn none() -> Actions {
Actions { store: vec![] }
}
pub fn has_pending_actions(&self) -> bool {
for item in self.store.iter() {
match item {
ActionType::AppendSubGroup(_)
| ActionType::AppendGroup(_)
| ActionType::AppendItem(_, _, _) => return true,
ActionType::NewBlock(_) => return true,
ActionType::NewModal(_) => return true,
ActionType::UpdateActionItemRequest(data) => {
match data.action_status.clone().unwrap() {
ActionItemStatus::Success(_) => continue,
_ => return true,
}
}
}
}
false
}
pub fn append(&mut self, actions: &mut Actions) {
self.store.append(&mut actions.store);
}
pub fn push_modal(&mut self, block: Block) {
self.store.push(ActionType::NewModal(block));
}
pub fn push_group(&mut self, title: &str, action_items: Vec<ActionItemRequest>) {
self.store.push(ActionType::AppendGroup(ActionGroup {
sub_groups: vec![ActionSubGroup {
title: None,
action_items,
allow_batch_completion: false,
}],
title: title.to_string(),
}));
}
pub fn push_sub_group(&mut self, title: Option<String>, action_items: Vec<ActionItemRequest>) {
if !action_items.is_empty() {
self.store.push(ActionType::AppendSubGroup(ActionSubGroup {
title,
action_items,
allow_batch_completion: false,
}));
}
}
pub fn push_action_item_update(&mut self, update: ActionItemRequestUpdate) {
self.store.push(ActionType::UpdateActionItemRequest(update))
}
pub fn push_panel(&mut self, title: &str, description: &str) {
self.store.push(ActionType::NewBlock(ActionPanelData {
title: title.to_string(),
description: description.to_string(), groups: vec![],
}))
}
pub fn new_panel(title: &str, description: &str) -> Actions {
let store = vec![ActionType::NewBlock(ActionPanelData {
title: title.to_string(),
description: description.to_string(), groups: vec![],
})];
Actions { store }
}
pub fn new_group_of_items(title: &str, action_items: Vec<ActionItemRequest>) -> Actions {
let store = vec![ActionType::AppendGroup(ActionGroup {
sub_groups: vec![ActionSubGroup {
title: None,
action_items,
allow_batch_completion: false,
}],
title: title.to_string(),
})];
Actions { store }
}
pub fn new_sub_group_of_items(
title: Option<String>,
action_items: Vec<ActionItemRequest>,
) -> Actions {
let store = vec![ActionType::AppendSubGroup(ActionSubGroup {
title,
action_items,
allow_batch_completion: false,
})];
Actions { store }
}
pub fn append_item(
item: ActionItemRequest,
group_title: Option<&str>,
panel_title: Option<&str>,
) -> Actions {
let store = vec![ActionType::AppendItem(
item,
group_title.map(|t| t.to_string()),
panel_title.map(|t| t.to_string()),
)];
Actions { store }
}
pub fn get_new_action_item_requests(&self) -> Vec<&ActionItemRequest> {
let mut new_action_item_requests = vec![];
for item in self.store.iter() {
match item {
ActionType::AppendSubGroup(data) => {
for item in data.action_items.iter() {
new_action_item_requests.push(item);
}
}
ActionType::AppendGroup(data) => {
for subgroup in data.sub_groups.iter() {
for item in subgroup.action_items.iter() {
new_action_item_requests.push(item);
}
}
}
ActionType::AppendItem(item, _, _) => {
new_action_item_requests.push(item);
}
ActionType::NewBlock(data) => {
for group in data.groups.iter() {
for subgroup in group.sub_groups.iter() {
for item in subgroup.action_items.iter() {
new_action_item_requests.push(item);
}
}
}
}
ActionType::NewModal(data) => {
for group in data.panel.expect_modal_panel().groups.iter() {
for subgroup in group.sub_groups.iter() {
for item in subgroup.action_items.iter() {
new_action_item_requests.push(item);
}
}
}
}
ActionType::UpdateActionItemRequest(_) => continue,
}
}
new_action_item_requests
}
pub fn compile_actions_to_block_events(
&mut self,
action_item_requests: &BTreeMap<BlockId, ActionItemRequest>,
) -> Vec<BlockEvent> {
let mut blocks = vec![];
let mut current_panel_data =
ActionPanelData { title: "".to_string(), description: "".to_string(), groups: vec![] };
let mut index = 0;
let mut current_modal: Option<Block> = None;
let mut updates = vec![];
for item in self.store.iter_mut() {
match item {
ActionType::AppendItem(item, group_title, panel_title) => {
item.index = index;
index += 1;
match current_modal {
None => {
if current_panel_data.groups.len() > 0 {
let Some(group) = current_panel_data.groups.last_mut() else {
continue;
};
if group.sub_groups.len() > 0 {
let Some(sub_group) = group.sub_groups.last_mut() else {
continue;
};
if sub_group.action_items.is_empty() {
*sub_group = ActionSubGroup {
title: None,
action_items: vec![item.clone()],
allow_batch_completion: true,
};
continue;
}
}
group
.sub_groups
.last_mut()
.unwrap()
.action_items
.push(item.clone());
} else {
current_panel_data.groups.push(ActionGroup {
title: group_title.as_ref().unwrap_or(&"".into()).into(),
sub_groups: vec![ActionSubGroup {
title: None,
action_items: vec![item.clone()],
allow_batch_completion: true,
}],
});
}
if let Some(panel_title) = panel_title {
current_panel_data.title = panel_title.to_string();
}
}
Some(ref mut modal) => {
if modal.panel.expect_modal_panel().groups.len() > 0 {
let Some(group) =
modal.panel.expect_modal_panel_mut().groups.last_mut()
else {
continue;
};
if group.sub_groups.len() > 0 {
let Some(sub_group) = group.sub_groups.last_mut() else {
continue;
};
if sub_group.action_items.is_empty() {
*sub_group = ActionSubGroup {
title: None,
action_items: vec![item.clone()],
allow_batch_completion: true,
};
continue;
}
}
group.sub_groups.push(ActionSubGroup {
title: None,
action_items: vec![item.clone()],
allow_batch_completion: true,
});
} else {
modal.panel.expect_modal_panel_mut().groups.push(ActionGroup {
title: group_title.as_ref().unwrap_or(&"".into()).into(),
sub_groups: vec![ActionSubGroup {
title: None,
action_items: vec![item.clone()],
allow_batch_completion: true,
}],
});
}
if let ActionItemRequestType::ValidateModal = item.action_type {
blocks.push(BlockEvent::Modal(modal.clone()));
current_modal = None;
}
}
}
}
ActionType::AppendSubGroup(data) => {
for item in data.action_items.iter_mut() {
item.index = index;
index += 1;
}
match current_modal {
None => {
if current_panel_data.groups.len() > 0 {
let Some(group) = current_panel_data.groups.last_mut() else {
continue;
};
if group.sub_groups.len() > 0 {
let Some(sub_group) = group.sub_groups.last_mut() else {
continue;
};
if sub_group.action_items.is_empty() {
*sub_group = data.clone();
continue;
}
}
group.sub_groups.push(data.clone());
} else {
current_panel_data.groups.push(ActionGroup {
title: "".to_string(),
sub_groups: vec![data.clone()],
});
}
}
Some(ref mut modal) => {
if modal.panel.expect_modal_panel().groups.len() > 0 {
let Some(group) =
modal.panel.expect_modal_panel_mut().groups.last_mut()
else {
continue;
};
if group.sub_groups.len() > 0 {
let Some(sub_group) = group.sub_groups.last_mut() else {
continue;
};
if sub_group.action_items.is_empty() {
*sub_group = data.clone();
continue;
}
}
group.sub_groups.push(data.clone());
} else {
modal.panel.expect_modal_panel_mut().groups.push(ActionGroup {
title: "".to_string(),
sub_groups: vec![data.clone()],
});
}
if data.contains_validate_modal_item() {
blocks.push(BlockEvent::Modal(modal.clone()));
current_modal = None;
}
}
}
}
ActionType::AppendGroup(data) => {
for subgroup in data.sub_groups.iter_mut() {
for item in subgroup.action_items.iter_mut() {
item.index = index;
index += 1;
}
}
match current_modal {
None => {
current_panel_data.groups.push(data.clone());
}
Some(ref mut modal) => {
modal.panel.expect_modal_panel_mut().groups.push(data.clone());
if data.contains_validate_modal_item() {
blocks.push(BlockEvent::Modal(modal.clone()));
current_modal = None;
}
}
}
}
ActionType::NewBlock(data) => {
if current_panel_data.groups.len() > 1 {
blocks.push(BlockEvent::Action(Block {
uuid: Uuid::new_v4(),
panel: Panel::ActionPanel(current_panel_data.clone()),
visible: true,
}));
}
current_panel_data = data.clone();
}
ActionType::NewModal(data) => {
current_modal = Some(data.clone());
}
ActionType::UpdateActionItemRequest(data) => {
if let Some(update) = data.normalize(&action_item_requests) {
updates.push(update);
}
}
}
}
if !updates.is_empty() {
blocks.push(BlockEvent::UpdateActionItems(updates));
}
blocks.push(BlockEvent::Action(Block {
uuid: Uuid::new_v4(),
panel: Panel::ActionPanel(current_panel_data.clone()),
visible: true,
}));
blocks
}
pub fn compile_actions_to_item_updates(
&self,
action_item_requests: &BTreeMap<BlockId, ActionItemRequest>,
) -> Vec<ActionItemRequestUpdate> {
let mut updates = vec![];
for item in self.store.iter() {
match item {
ActionType::AppendSubGroup(sub_group) => {
let mut sub_group_updates =
sub_group.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut sub_group_updates);
}
ActionType::AppendGroup(group) => {
let mut group_updates =
group.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut group_updates);
}
ActionType::AppendItem(new_item, _, _) => {
if let Some(existing_item) = action_item_requests.get(&new_item.id) {
if let Some(update) =
ActionItemRequestUpdate::from_diff(new_item, existing_item)
{
updates.push(update);
};
};
}
ActionType::NewBlock(action_panel_data) => {
let mut block_updates =
action_panel_data.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut block_updates);
}
ActionType::NewModal(modal) => match &modal.panel {
Panel::ActionPanel(action_panel_data) => {
let mut block_updates = action_panel_data
.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut block_updates);
}
Panel::ModalPanel(modal_panel_data) => {
let mut block_updates =
modal_panel_data.compile_actions_to_item_updates(&action_item_requests);
updates.append(&mut block_updates);
}
_ => {}
},
ActionType::UpdateActionItemRequest(update) => updates.push(update.clone()),
}
}
updates
}
pub fn filter_existing_action_items(
&mut self,
existing_requests: &Option<&Vec<&mut ActionItemRequest>>,
) -> &mut Self {
let Some(existing_requests) = existing_requests else {
return self;
};
let mut idx_to_remove = vec![];
for (i, item) in self.store.iter_mut().enumerate() {
match item {
ActionType::UpdateActionItemRequest(_) => {}
ActionType::AppendSubGroup(sub_group) => {
sub_group.filter_existing_action_items(&existing_requests);
if sub_group.action_items.is_empty() {
idx_to_remove.push(i);
}
}
ActionType::AppendGroup(group) => {
group.filter_existing_action_items(&existing_requests);
if group.sub_groups.is_empty() {
idx_to_remove.push(i);
}
}
ActionType::AppendItem(new_item, _, _) => {
for existing_item in existing_requests.iter() {
if existing_item.id.eq(&new_item.id) {
if let None =
ActionItemRequestUpdate::from_diff(new_item, existing_item)
{
idx_to_remove.push(i);
};
}
}
}
ActionType::NewBlock(block) => {
block.filter_existing_action_items(&existing_requests);
if block.groups.is_empty() {
idx_to_remove.push(i);
}
}
ActionType::NewModal(_) => {}
}
}
idx_to_remove.iter().rev().for_each(|i| {
self.store.remove(*i);
});
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", content = "data")]
pub enum ActionItemRequestType {
ReviewInput(ReviewInputRequest),
ProvideInput(ProvideInputRequest),
PickInputOption(PickInputOptionRequest),
ProvidePublicKey(ProvidePublicKeyRequest),
ProvideSignedTransaction(ProvideSignedTransactionRequest),
ProvideSignedMessage(ProvideSignedMessageRequest),
SendTransaction(SendTransactionRequest),
DisplayOutput(DisplayOutputRequest),
DisplayErrorLog(DisplayErrorLogRequest),
OpenModal(OpenModalData),
ValidateBlock(ValidateBlockData),
ValidateModal,
}
impl ActionItemRequestType {
pub fn as_review_input(&self) -> Option<&ReviewInputRequest> {
match &self {
ActionItemRequestType::ReviewInput(value) => Some(value),
_ => None,
}
}
pub fn as_provide_input(&self) -> Option<&ProvideInputRequest> {
match &self {
ActionItemRequestType::ProvideInput(value) => Some(value),
_ => None,
}
}
pub fn as_pick_input(&self) -> Option<&PickInputOptionRequest> {
match &self {
ActionItemRequestType::PickInputOption(value) => Some(value),
_ => None,
}
}
pub fn as_provide_public_key(&self) -> Option<&ProvidePublicKeyRequest> {
match &self {
ActionItemRequestType::ProvidePublicKey(value) => Some(value),
_ => None,
}
}
pub fn as_provide_signed_tx(&self) -> Option<&ProvideSignedTransactionRequest> {
match &self {
ActionItemRequestType::ProvideSignedTransaction(value) => Some(value),
_ => None,
}
}
pub fn as_sign_tx(&self) -> Option<&SendTransactionRequest> {
match &self {
ActionItemRequestType::SendTransaction(value) => Some(value),
_ => None,
}
}
pub fn as_provide_signed_msg(&self) -> Option<&ProvideSignedMessageRequest> {
match &self {
ActionItemRequestType::ProvideSignedMessage(value) => Some(value),
_ => None,
}
}
pub fn as_display_output(&self) -> Option<&DisplayOutputRequest> {
match &self {
ActionItemRequestType::DisplayOutput(value) => Some(value),
_ => None,
}
}
pub fn as_display_err(&self) -> Option<&DisplayErrorLogRequest> {
match &self {
ActionItemRequestType::DisplayErrorLog(value) => Some(value),
_ => None,
}
}
pub fn as_open_modal(&self) -> Option<&OpenModalData> {
match &self {
ActionItemRequestType::OpenModal(value) => Some(value),
_ => None,
}
}
pub fn get_block_id_string(&self) -> String {
match self {
ActionItemRequestType::ReviewInput(val) => {
format!("ReviewInput({}-{})", val.input_name, val.force_execution)
}
ActionItemRequestType::ProvideInput(val) => format!(
"ProvideInput({}-{})",
val.input_name,
serde_json::to_string(&val.typing).unwrap() ),
ActionItemRequestType::PickInputOption(_) => format!("PickInputOption"),
ActionItemRequestType::ProvidePublicKey(val) => format!(
"ProvidePublicKey({}-{}-{})",
val.check_expectation_action_uuid
.as_ref()
.and_then(|u| Some(u.to_string()))
.unwrap_or("None".to_string()),
val.namespace,
val.network_id
),
ActionItemRequestType::ProvideSignedTransaction(val) => {
format!(
"ProvideSignedTransaction({}-{}-{}-{})",
val.check_expectation_action_uuid
.as_ref()
.and_then(|u| Some(u.to_string()))
.unwrap_or("None".to_string()),
val.signer_uuid.to_string(),
val.namespace,
val.network_id
)
}
ActionItemRequestType::SendTransaction(val) => {
format!(
"SendTransaction({}-{}-{}-{})",
val.check_expectation_action_uuid
.as_ref()
.and_then(|u| Some(u.to_string()))
.unwrap_or("None".to_string()),
val.signer_uuid.to_string(),
val.namespace,
val.network_id
)
}
ActionItemRequestType::ProvideSignedMessage(val) => format!(
"ProvideSignedMessage({}-{}-{}-{})",
val.check_expectation_action_uuid
.as_ref()
.and_then(|u| Some(u.to_string()))
.unwrap_or("None".to_string()),
val.signer_uuid.to_string(),
val.namespace,
val.network_id
),
ActionItemRequestType::DisplayOutput(val) => format!(
"DisplayOutput({}-{}-{})",
val.name,
val.description.clone().unwrap_or("None".to_string()),
val.value.to_string()
),
ActionItemRequestType::DisplayErrorLog(val) => {
format!("DisplayErrorLog({})", val.diagnostic.to_string())
}
ActionItemRequestType::OpenModal(val) => {
format!("OpenModal({}-{})", val.modal_uuid, val.title)
}
ActionItemRequestType::ValidateBlock(val) => {
format!("ValidateBlock({})", val.internal_idx.to_string())
}
ActionItemRequestType::ValidateModal => format!("ValidateModal"),
}
}
pub fn diff_mutable_properties(
new_type: &ActionItemRequestType,
existing_item: &ActionItemRequestType,
) -> Option<ActionItemRequestType> {
match new_type {
ActionItemRequestType::ReviewInput(new) => {
let Some(existing) = existing_item.as_review_input() else {
unreachable!("cannot change action item request type")
};
if new.value != existing.value {
if new.input_name != existing.input_name {
unreachable!("cannot change review input request input_name")
}
if new.force_execution != existing.force_execution {
unreachable!("cannot change review input request force_execution")
}
Some(new_type.clone())
} else {
None
}
}
ActionItemRequestType::ProvideInput(new) => {
let Some(existing) = existing_item.as_provide_input() else {
unreachable!("cannot change action item request type")
};
if new.default_value != existing.default_value {
if new.input_name != existing.input_name {
unreachable!("cannot change provide input request input_name")
}
if new.typing != existing.typing {
unreachable!("cannot change provide input request typing")
}
Some(new_type.clone())
} else {
None
}
}
ActionItemRequestType::PickInputOption(_) => {
let Some(_) = existing_item.as_pick_input() else {
unreachable!("cannot change action item request type")
};
Some(new_type.clone())
}
ActionItemRequestType::ProvidePublicKey(new) => {
let Some(existing) = existing_item.as_provide_public_key() else {
unreachable!("cannot change action item request type")
};
if new.message != existing.message {
if new.check_expectation_action_uuid != existing.check_expectation_action_uuid {
unreachable!("cannot change provide public key request check_expectation_action_uuid");
}
if new.namespace != existing.namespace {
unreachable!("cannot change provide public key request namespace");
}
if new.network_id != existing.network_id {
unreachable!("cannot change provide public key request network_id");
}
Some(new_type.clone())
} else {
None
}
}
ActionItemRequestType::ProvideSignedTransaction(new) => {
let Some(existing) = existing_item.as_provide_signed_tx() else {
unreachable!("cannot change action item request type")
};
if new.payload != existing.payload || new.skippable != existing.skippable {
if new.check_expectation_action_uuid != existing.check_expectation_action_uuid {
unreachable!(
"cannot change provide signed tx request check_expectation_action_uuid"
);
}
if new.signer_uuid != existing.signer_uuid {
unreachable!("cannot change provide signed tx request signer_uuid");
}
if new.namespace != existing.namespace {
unreachable!("cannot change provide signed tx request namespace");
}
if new.network_id != existing.network_id {
unreachable!("cannot change provide signed tx request network_id");
}
if new.only_approval_needed != existing.only_approval_needed {
unreachable!(
"cannot change provide signed tx request only_approval_needed"
);
}
Some(new_type.clone())
} else {
None
}
}
ActionItemRequestType::SendTransaction(new) => {
let Some(existing) = existing_item.as_sign_tx() else {
unreachable!("cannot change action item request type")
};
if new.payload != existing.payload {
if new.check_expectation_action_uuid != existing.check_expectation_action_uuid {
unreachable!(
"cannot change provide signed tx request check_expectation_action_uuid"
);
}
if new.signer_uuid != existing.signer_uuid {
unreachable!("cannot change provide signed tx request signer_uuid");
}
if new.namespace != existing.namespace {
unreachable!("cannot change provide signed tx request namespace");
}
if new.network_id != existing.network_id {
unreachable!("cannot change provide signed tx request network_id");
}
Some(new_type.clone())
} else {
None
}
}
ActionItemRequestType::ProvideSignedMessage(new) => {
let Some(existing) = existing_item.as_provide_signed_msg() else {
unreachable!("cannot change action item request type")
};
if new.message != existing.message {
if new.check_expectation_action_uuid != existing.check_expectation_action_uuid {
unreachable!(
"cannot change provide signed msg request check_expectation_action_uuid"
);
}
if new.signer_uuid != existing.signer_uuid {
unreachable!("cannot change provide signed msg request signer_uuid");
}
if new.namespace != existing.namespace {
unreachable!("cannot change provide signed msg request namespace");
}
if new.network_id != existing.network_id {
unreachable!("cannot change provide signed msg request network_id");
}
Some(new_type.clone())
} else {
None
}
}
ActionItemRequestType::DisplayOutput(_) => None,
ActionItemRequestType::DisplayErrorLog(_) => None,
ActionItemRequestType::OpenModal(_) => None,
ActionItemRequestType::ValidateBlock(_) => None,
ActionItemRequestType::ValidateModal => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ReviewInputRequest {
pub input_name: String,
pub value: Value,
pub force_execution: bool,
}
impl ReviewInputRequest {
pub fn new(input_name: &str, value: &Value) -> Self {
ReviewInputRequest {
input_name: input_name.to_string(),
value: value.clone(),
force_execution: false,
}
}
pub fn force_execution(&mut self) -> &mut Self {
self.force_execution = true;
self
}
pub fn to_action_type(&self) -> ActionItemRequestType {
ActionItemRequestType::ReviewInput(self.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProvideInputRequest {
pub default_value: Option<Value>,
pub input_name: String,
pub typing: Type,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DisplayOutputRequest {
pub name: String,
pub description: Option<String>,
pub value: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DisplayErrorLogRequest {
pub diagnostic: Diagnostic,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ValidateBlockData {
internal_idx: u8,
}
impl ValidateBlockData {
pub fn new(internal_idx: u8) -> Self {
ValidateBlockData { internal_idx }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PickInputOptionRequest {
pub options: Vec<InputOption>,
pub selected: InputOption,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InputOption {
pub value: String,
pub displayed_value: String,
}
impl InputOption {
pub fn default() -> Self {
InputOption { value: String::new(), displayed_value: String::new() }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProvidePublicKeyRequest {
pub check_expectation_action_uuid: Option<ConstructDid>,
pub message: String,
pub namespace: String,
pub network_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProvideSignedTransactionRequest {
pub check_expectation_action_uuid: Option<ConstructDid>,
pub signer_uuid: ConstructDid,
pub expected_signer_address: Option<String>,
pub skippable: bool,
pub only_approval_needed: bool,
pub payload: Value,
pub formatted_payload: Option<Value>,
pub namespace: String,
pub network_id: String,
}
impl ProvideSignedTransactionRequest {
pub fn new(signer_uuid: &Did, payload: &Value, namespace: &str, network_id: &str) -> Self {
ProvideSignedTransactionRequest {
signer_uuid: ConstructDid(signer_uuid.clone()),
check_expectation_action_uuid: None,
expected_signer_address: None,
skippable: false,
payload: payload.clone(),
formatted_payload: None,
namespace: namespace.to_string(),
network_id: network_id.to_string(),
only_approval_needed: false,
}
}
pub fn skippable(&mut self, is_skippable: bool) -> &mut Self {
self.skippable = is_skippable;
self
}
pub fn only_approval_needed(&mut self) -> &mut Self {
self.only_approval_needed = true;
self
}
pub fn check_expectation_action_uuid(&mut self, uuid: &ConstructDid) -> &mut Self {
self.check_expectation_action_uuid = Some(uuid.clone());
self
}
pub fn expected_signer_address(&mut self, address: Option<&str>) -> &mut Self {
self.expected_signer_address = address.and_then(|a| Some(a.to_string()));
self
}
pub fn formatted_payload(&mut self, display_payload: Option<&Value>) -> &mut Self {
self.formatted_payload = display_payload.cloned();
self
}
pub fn to_action_type(&self) -> ActionItemRequestType {
ActionItemRequestType::ProvideSignedTransaction(self.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SendTransactionRequest {
pub check_expectation_action_uuid: Option<ConstructDid>,
pub signer_uuid: ConstructDid,
pub expected_signer_address: Option<String>,
pub payload: Value,
pub formatted_payload: Option<Value>,
pub namespace: String,
pub network_id: String,
}
impl SendTransactionRequest {
pub fn new(signer_uuid: &Did, payload: &Value, namespace: &str, network_id: &str) -> Self {
SendTransactionRequest {
signer_uuid: ConstructDid(signer_uuid.clone()),
check_expectation_action_uuid: None,
expected_signer_address: None,
payload: payload.clone(),
formatted_payload: None,
namespace: namespace.to_string(),
network_id: network_id.to_string(),
}
}
pub fn check_expectation_action_uuid(&mut self, uuid: &ConstructDid) -> &mut Self {
self.check_expectation_action_uuid = Some(uuid.clone());
self
}
pub fn expected_signer_address(&mut self, address: Option<&str>) -> &mut Self {
self.expected_signer_address = address.and_then(|a| Some(a.to_string()));
self
}
pub fn formatted_payload(&mut self, display_payload: Option<&Value>) -> &mut Self {
self.formatted_payload = display_payload.cloned();
self
}
pub fn to_action_type(&self) -> ActionItemRequestType {
ActionItemRequestType::SendTransaction(self.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProvideSignedMessageRequest {
pub check_expectation_action_uuid: Option<ConstructDid>,
pub signer_uuid: ConstructDid,
pub message: Value,
pub namespace: String,
pub network_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionItemResponse {
pub action_item_id: BlockId,
#[serde(flatten)]
pub payload: ActionItemResponseType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum ActionItemResponseType {
ReviewInput(ReviewedInputResponse),
ProvideInput(ProvidedInputResponse),
PickInputOption(String),
ProvidePublicKey(ProvidePublicKeyResponse),
ProvideSignedMessage(ProvideSignedMessageResponse),
ProvideSignedTransaction(ProvideSignedTransactionResponse),
SendTransaction(SendTransactionResponse),
ValidateBlock,
ValidateModal,
}
impl ActionItemResponseType {
pub fn is_validate_panel(&self) -> bool {
match &self {
ActionItemResponseType::ValidateBlock => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReviewedInputResponse {
pub input_name: String,
pub value_checked: bool,
pub force_execution: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProvidedInputResponse {
pub input_name: String,
pub updated_value: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProvidePublicKeyResponse {
pub public_key: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProvideSignedMessageResponse {
pub signed_message_bytes: String,
pub signer_uuid: ConstructDid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProvideSignedTransactionResponse {
pub signed_transaction_bytes: Option<String>,
pub signature_approved: Option<bool>,
pub signer_uuid: ConstructDid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SendTransactionResponse {
pub transaction_hash: String,
pub signer_uuid: ConstructDid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DiscoveryResponse {
pub needs_credentials: bool,
pub client_type: ClientType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientType {
Operator,
Participant,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChannelParticipantAuthRequest {
pub otp_code: Option<String>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChannelParticipantAuthResponse {
pub auth_token: String,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename = "runbook", rename_all = "camelCase")]
pub struct OpenChannelRequest {
pub runbook_name: String,
pub runbook_description: Option<String>,
pub registered_addons: Vec<String>,
pub block_store: BTreeMap<usize, Block>,
pub uuid: Uuid,
pub slug: String,
pub operating_token: String,
pub totp: String,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename = "runbook", rename_all = "camelCase")]
pub struct DeleteChannelRequest {
pub slug: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenChannelResponse {
pub http_endpoint_url: String,
pub ws_endpoint_url: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenChannelResponseBrowser {
pub totp: String,
pub http_endpoint_url: String,
pub ws_endpoint_url: String,
pub slug: String,
}