use crate::reply::start::Prepared;
use crate::ui::Model;
#[derive(Debug, Clone, PartialEq)]
pub struct Start {
pub address: String,
pub goal: String,
pub phase: Phase,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Phase {
Staging,
Firing,
Started(String),
Refused(String),
}
impl Start {
pub fn line(&self) -> String {
match &self.phase {
Phase::Started(name) => format!("started «{name}» in {}", self.address),
Phase::Refused(why) => format!("not started in {}: {why}", self.address),
Phase::Staging | Phase::Firing => {
format!("starting in {}: {}…", self.address, self.goal)
}
}
}
pub fn outstanding(&self) -> bool {
!matches!(self.phase, Phase::Started(_) | Phase::Refused(_))
}
}
impl Model {
pub fn stage(&mut self, address: &str) {
if self.draft.trim().is_empty() {
return;
}
let goal = std::mem::take(&mut self.draft);
self.outbox.push(super::Posted::act(crate::verbs::prepare(
address.to_owned(),
)));
self.start = Some(Start {
address: address.to_owned(),
goal,
phase: Phase::Staging,
});
}
pub(super) fn fire(&mut self, prepared: &Prepared) {
let held = self
.start
.clone()
.filter(|start| matches!(start.phase, Phase::Staging));
let (address, goal) = held.map_or_else(
|| (prepared.workspace.clone(), prepared.goal.clone()),
|start| (start.address, start.goal),
);
if goal.trim().is_empty() {
self.start = None;
return;
}
self.outbox.push(super::Posted::act(crate::verbs::prompt(
prepared,
address.clone(),
goal.clone(),
)));
self.start = Some(Start {
address,
goal,
phase: Phase::Firing,
});
}
pub(super) fn starting(&self, op: &str) -> bool {
(op == crate::verbs::PREPARE || op == crate::verbs::PROMPT)
&& self.start.as_ref().is_some_and(Start::outstanding)
}
pub(super) fn refuse_start(&mut self, why: String) {
let Some(mut start) = self.start.take() else {
return;
};
self.refund(&start.goal);
start.phase = Phase::Refused(why);
self.start = Some(start);
}
pub(super) fn take_back_start(&mut self) {
if let Some(start) = self.start.take() {
self.refund(&start.goal);
}
}
fn refund(&mut self, goal: &str) {
if self.draft.trim().is_empty() {
goal.clone_into(&mut self.draft);
}
}
pub(super) fn started(&mut self, conversation: String) {
let mut start = self.start.clone().unwrap_or_else(|| Start {
address: self
.aim
.as_ref()
.map_or_else(String::new, |aim| aim.address.clone()),
goal: String::new(),
phase: Phase::Firing,
});
start.phase = Phase::Started(conversation.clone());
let here = self
.aim
.as_ref()
.is_some_and(|aim| aim.address == start.address);
self.start = Some(start);
if here {
self.select(&conversation);
}
}
}
#[cfg(test)]
mod tests;