use anyhow::Result;
use chrono::Local;
use std::io::{IsTerminal, Write};
use crate::background;
use crate::store::Store;
use crate::types::{EventKind, TaskEvent, TaskId};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ForegroundSignal {
Int,
Term,
Hup,
}
impl ForegroundSignal {
pub(super) fn name(self) -> &'static str {
match self {
Self::Int => "SIGINT",
Self::Term => "SIGTERM",
Self::Hup => "SIGHUP",
}
}
fn exit_code(self) -> i32 {
match self {
Self::Int => 130,
Self::Term => 143,
Self::Hup => 129,
}
}
}
pub(super) fn should_detach(signal: ForegroundSignal) -> bool {
should_detach_for_terminal(signal, std::io::stdin().is_terminal())
}
pub(super) fn should_detach_for_terminal(
signal: ForegroundSignal,
stdin_is_terminal: bool,
) -> bool {
matches!(signal, ForegroundSignal::Term | ForegroundSignal::Hup) && !stdin_is_terminal
}
pub(super) fn handle_foreground_detach(
store: &Store,
task_id: &TaskId,
signal: ForegroundSignal,
) -> ! {
let detail = format!(
"foreground aid detached after {} without signaling the agent",
signal.name()
);
if let Some(mut spec) = background::load_spec_if_exists(task_id.as_str())
.ok()
.flatten()
{
spec.detached = true;
let _ = background::save_spec(&spec);
}
let insert_result = store.insert_event(&TaskEvent {
task_id: task_id.clone(),
timestamp: Local::now(),
event_kind: EventKind::Milestone,
detail,
metadata: None,
});
let mut stderr = std::io::stderr().lock();
if let Err(err) = insert_result {
let _ = writeln!(stderr, "[aid] Failed to record detach milestone for {task_id}: {err}");
}
let _ = writeln!(
stderr,
"\n\u{26a0} Received {} (non-interactive) \u{2014} aid detached from task {task_id}.\n\
Aid did not send a stop signal. Reattach: aid watch --wait {task_id}",
signal.name()
);
std::process::exit(signal.exit_code());
}
#[cfg(unix)]
pub(super) async fn wait_for_foreground_signal() -> Result<ForegroundSignal> {
use tokio::signal::unix::{SignalKind, signal};
let mut sigint = signal(SignalKind::interrupt())?;
let mut sigterm = signal(SignalKind::terminate())?;
let mut sighup = signal(SignalKind::hangup())?;
tokio::select! {
_ = sigint.recv() => Ok(ForegroundSignal::Int),
_ = sigterm.recv() => Ok(ForegroundSignal::Term),
_ = sighup.recv() => Ok(ForegroundSignal::Hup),
}
}
#[cfg(not(unix))]
pub(super) async fn wait_for_foreground_signal() -> Result<ForegroundSignal> {
std::future::pending::<Result<ForegroundSignal>>().await
}