use std::collections::{HashMap, VecDeque};
use chrono::{DateTime, Local, Timelike};
use rite_model::{Prompt, RoleId, StepId};
use rite_runtime::{Environment, ExecEvent, Icon, MaterialOverview, PromptId, SystemInfo};
pub(crate) const DEVIATION_INPUT_MAX: usize = 280;
pub(crate) const LOG_CAPACITY: usize = 200;
#[derive(Debug)]
pub struct Model {
pub ceremony_name: Option<String>,
pub ceremony_description: Option<String>,
pub ceremony_materials: Vec<MaterialOverview>,
pub ceremony_step_count: Option<usize>,
pub role_names: HashMap<RoleId, String>,
pub system_info: Option<SystemInfo>,
pub environment: Option<Environment>,
pub screen: Screen,
pub return_to: Option<Screen>,
pub current_step: Option<StepView>,
pub log: VecDeque<LogLine>,
pub deviations: Vec<DeviationView>,
pub pending_prompt: Option<PendingPrompt>,
pub running: RunningState,
pub tick: u64,
pub now: DateTime<Local>,
pub log_scroll: usize,
pub(crate) pending_events: VecDeque<ExecEvent>,
}
impl Default for Model {
fn default() -> Self {
Self {
ceremony_name: None,
ceremony_description: None,
ceremony_materials: Vec::new(),
ceremony_step_count: None,
role_names: HashMap::new(),
system_info: None,
environment: None,
screen: Screen::Step {
tab: StepTab::Overview,
},
return_to: None,
current_step: None,
log: VecDeque::with_capacity(LOG_CAPACITY),
deviations: Vec::new(),
pending_prompt: None,
running: RunningState::Active,
tick: 0,
now: Local::now(),
log_scroll: 0,
pending_events: VecDeque::new(),
}
}
}
impl Model {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub(crate) fn push_log(&mut self, line: LogLine) {
if let Some(LogLine::Entry {
icon: icon @ Icon::Spinner,
..
}) = self.log.back_mut()
{
*icon = Icon::Checkmark;
}
if self.log.len() == LOG_CAPACITY {
self.log.pop_front();
}
self.log.push_back(line);
if self.log_scroll > self.log.len() {
self.log_scroll = self.log.len();
}
}
pub(crate) fn push_entry(&mut self, icon: Icon, text: String) {
self.push_entry_at(icon, text, self.now);
}
pub(crate) fn push_entry_at(&mut self, icon: Icon, text: String, at: DateTime<Local>) {
let step = self.current_step.as_ref().map(|s| s.label.clone());
self.push_log(LogLine::Entry {
icon,
text,
step,
at,
});
}
pub(crate) fn open_modal(&mut self, modal: Screen) {
if self.return_to.is_none() {
self.return_to = Some(self.screen.clone());
}
self.screen = modal;
}
pub(crate) fn close_modal(&mut self) {
if let Some(prev) = self.return_to.take() {
self.screen = prev;
}
}
#[must_use]
pub fn has_active_spinner(&self) -> bool {
matches!(
self.log.back(),
Some(LogLine::Entry {
icon: Icon::Spinner,
..
})
)
}
#[must_use]
pub fn clock_display_key(&self) -> (u32, u32) {
(self.now.minute(), self.now.second() % 2)
}
}
#[derive(Debug, Clone)]
pub enum Screen {
Step {
tab: StepTab,
},
DeviationModal {
input: String,
},
AbortConfirm,
Completed {
fingerprint: Option<String>,
},
Failed {
reason: String,
},
Aborted,
}
impl Screen {
#[must_use]
pub fn is_terminal(&self) -> bool {
matches!(
self,
Screen::Completed { .. } | Screen::Failed { .. } | Screen::Aborted
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StepTab {
Overview,
Ceremony,
Deviations,
System,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunningState {
Active,
Aborting,
Done,
}
#[derive(Debug, Clone)]
pub struct StepView {
pub id: StepId,
pub label: String,
pub role_name: String,
}
#[derive(Debug, Clone)]
pub enum LogLine {
Entry {
icon: Icon,
text: String,
step: Option<String>,
at: DateTime<Local>,
},
StepDivider {
label: String,
role_name: String,
},
ActDivider {
label: String,
},
}
#[derive(Debug, Clone)]
pub struct DeviationView {
pub step: Option<StepId>,
pub text: String,
pub at: DateTime<Local>,
}
#[derive(Debug, Clone)]
pub struct PendingPrompt {
pub prompt_id: PromptId,
pub prompt: Prompt,
pub input: String,
pub rejection: Option<String>,
pub reveal: RevealPhase,
pub entry: RowEntry,
}
#[derive(Debug, Clone, Default)]
pub struct RowEntry {
pub rows: Vec<EnteredRow>,
pub notice: Option<RowNotice>,
}
#[derive(Debug, Clone)]
pub struct EnteredRow {
pub typed: zeroize::Zeroizing<String>,
pub reads: zeroize::Zeroizing<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RowNotice {
Repaired(String),
Refused(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RevealPhase {
#[default]
Ready,
Shown,
Confirm,
}