use std::num::NonZeroUsize;
use anyhow::{Context, Result, ensure};
use super::{DraftKind, WorktreeKind, builtin};
use crate::delivery::{Opening, OpeningSession};
use crate::failed_run::FailedRun;
use crate::github::Ticket;
use crate::issue::IssueUrl;
use crate::prompt;
use crate::run::Reached;
use crate::spec_run::{Outside, account_tickets};
use builtin::Step;
pub(crate) struct Sequence {
pub process: String,
pub delivery: Vec<Step>,
}
pub(crate) fn sequence() -> Result<Sequence> {
let mut steps = builtin::steps("spec-run", WorktreeKind::SpecBranch)?.into_iter();
let land = steps.next().context("spec-run needs land-tickets first")?;
let DraftKind::LandTickets { line } = land.kind else {
anyhow::bail!("spec-run needs land-tickets first");
};
let process = land.child_process(line).0.to_string();
Ok(Sequence {
process,
delivery: steps
.map(|step| {
ensure!(
!matches!(step.kind, DraftKind::Implement { .. }),
"spec-run cannot implement a Ticket"
);
builtin::delivery_step(step)
})
.collect::<Result<_>>()?,
})
}
pub(crate) fn run_through(
outside: &mut impl Outside,
spec: &IssueUrl,
base: &str,
branch: &str,
tickets: Vec<Ticket>,
parallel: NonZeroUsize,
steps: Vec<Step>,
) -> Result<Reached, FailedRun> {
let landed = account_tickets(outside, spec, tickets, parallel)?;
match review_and_deliver(
outside,
spec,
base,
branch,
&landed.checklist,
&landed.pr_url,
steps,
) {
Ok(reached) => Ok(Reached {
ticket_lines: landed.lines,
..reached
}),
Err(mut failed) => {
landed.record_failure(&mut failed);
Err(failed)
}
}
}
fn review_and_deliver(
outside: &mut impl Outside,
spec: &IssueUrl,
base: &str,
branch: &str,
checklist: &str,
spec_pr_url: &str,
steps: Vec<Step>,
) -> Result<Reached, FailedRun> {
if outside.interrupted() {
return Err(FailedRun {
interrupted: true,
pr_url: Some(spec_pr_url.to_string()),
..FailedRun::from(anyhow::anyhow!("interrupted"))
});
}
let opening = Opening {
session: OpeningSession::SpecReview,
prompt: prompt::spec_review(spec, base, branch, spec_pr_url),
};
let delivered = outside.deliver(opening, checklist, steps);
if delivered.is_err() {
let _ = outside.show(checklist);
}
delivered
}