mod activity;
mod command_log;
mod effects;
mod recording;
use std::fmt::{self, Display};
use std::path::{Path, PathBuf};
use std::sync::{LazyLock, Mutex, MutexGuard, PoisonError};
use crate::config::UserConfig;
use crate::harness::Choice;
use crate::issue::{IssueUrl, Repo};
use activity::Kind;
use effects::OnMachine;
use recording::Record;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommandKind {
Issue,
Architect,
Pickup,
Secure,
}
impl CommandKind {
pub fn folder(self) -> &'static str {
match self {
Self::Issue => "issue",
Self::Architect => "architect",
Self::Pickup => "pickup",
Self::Secure => "secure",
}
}
pub fn named(name: &str) -> Option<Self> {
match name {
"issue" => Some(Self::Issue),
"architect" => Some(Self::Architect),
"pickup" => Some(Self::Pickup),
"secure" => Some(Self::Secure),
_ => None,
}
}
}
pub enum Begin<'a> {
Run(&'a IssueUrl),
ChildRun(&'a str, CommandKind),
ArchitectRun,
PickupRun,
SecurityRun,
}
#[derive(Clone, Copy)]
pub enum Work<'a> {
Run(&'a IssueUrl),
SpecRun(&'a IssueUrl),
PickupRun(&'a IssueUrl),
ArchitectRun(&'a Repo),
SecurityRun(&'a Repo),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Pass {
Architect,
Pickup,
Security,
}
impl Display for Pass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
Pass::Architect => "Architect run",
Pass::Pickup => "Pickup run",
Pass::Security => "Security run",
})
}
}
static RECORD: LazyLock<Mutex<Record<OnMachine>>> =
LazyLock::new(|| Mutex::new(Record::new(OnMachine)));
pub fn begin(begin: Begin) {
record().begin(begin);
}
pub fn configured(config: &UserConfig) {
record().configured(config);
}
pub fn started(work: Work, harness: &Choice) {
record().started(work, harness);
}
pub fn skipped(pass: Pass, repo: &Repo, reason: impl Display) {
record().skipped(pass, repo, reason);
}
pub fn ended(outcome: impl Display) {
record().ended(outcome);
}
pub fn show_held() {
record().show_held();
}
pub fn root(repo: &Repo) -> PathBuf {
record().root(repo)
}
pub fn command_kind() -> CommandKind {
record().command_kind()
}
pub fn session_dir(repo: &Repo) -> PathBuf {
let record = record();
record
.root(repo)
.join("commands")
.join(record.command_kind().folder())
}
pub fn stamp() -> String {
record().stamp()
}
pub fn command_log_path() -> Option<PathBuf> {
record().command_log_path()
}
pub fn eprint(line: &str) {
record().eprint(line);
}
pub fn print(line: &str) {
record().print(line);
}
fn root_under(logs_dir: &Path, repo: &Repo) -> PathBuf {
logs_dir.join(&repo.owner).join(&repo.name)
}
impl Work<'_> {
fn repo(self) -> Repo {
match self {
Work::Run(issue) | Work::SpecRun(issue) | Work::PickupRun(issue) => issue.repo(),
Work::ArchitectRun(repo) | Work::SecurityRun(repo) => repo.clone(),
}
}
fn kind(self) -> Kind {
match self {
Work::Run(_) => Kind::Run,
Work::SpecRun(_) => Kind::SpecRun,
Work::PickupRun(_) => Kind::Pass(Pass::Pickup),
Work::ArchitectRun(_) => Kind::Pass(Pass::Architect),
Work::SecurityRun(_) => Kind::Pass(Pass::Security),
}
}
fn issue(self) -> Option<u64> {
match self {
Work::Run(issue) | Work::SpecRun(issue) | Work::PickupRun(issue) => Some(issue.number),
Work::ArchitectRun(_) | Work::SecurityRun(_) => None,
}
}
fn command_log(self, root: &Path, stamp: &str) -> PathBuf {
let (folder, prefix) = match self {
Work::Run(issue) | Work::SpecRun(issue) => {
(CommandKind::Issue, format!("{}-", issue.number))
}
Work::PickupRun(issue) => (CommandKind::Pickup, format!("{}-", issue.number)),
Work::ArchitectRun(_) => (CommandKind::Architect, String::new()),
Work::SecurityRun(_) => (CommandKind::Secure, String::new()),
};
root.join("commands")
.join(folder.folder())
.join(format!("{prefix}{stamp}.log"))
}
}
fn record() -> MutexGuard<'static, Record<OnMachine>> {
RECORD.lock().unwrap_or_else(PoisonError::into_inner)
}