use serde::{Deserialize, Serialize};
use std::io::{self, Read, Write};
use std::path::Path;
use thiserror::Error;
use super::super::{ENV_CONV_BRANCH, ENV_CONV_REPO};
use super::dispatch::EnvLookup;
use crate::prompt::compactor::tools;
use crate::template::{GitRunner, RealGit};
use crate::workspace;
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct WriteSummaryInput {
content: String,
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct MarkInput {
path: String,
}
#[derive(Debug, Serialize, PartialEq, Eq)]
struct Output<'a> {
status: &'a str,
path: String,
}
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid input JSON: {0}")]
InvalidJson(#[source] serde_json::Error),
#[error("read input from stdin: {0}")]
StdinRead(#[source] io::Error),
#[error("missing env var {0:?} (set by the harness per ARCH §3.3)")]
MissingEnv(&'static str),
#[error("write summary: {0}")]
WriteSummary(#[source] io::Error),
#[error("mark for deletion: {0}")]
Mark(#[source] crate::prompt::Error),
#[error("write to stdout: {0}")]
Write(#[source] io::Error),
}
pub fn run_write_summary<R: Read, W: Write>(
stdin: &mut R,
stdout: &mut W,
env: &dyn EnvLookup,
) -> Result<(), Error> {
let input: WriteSummaryInput = read_input(stdin)?;
let worktree = resolve_worktree(env)?;
let rel = tools::write_summary(&worktree, &input.content).map_err(Error::WriteSummary)?;
emit(stdout, "written", rel)
}
pub fn run_mark_for_deletion<R: Read, W: Write>(
stdin: &mut R,
stdout: &mut W,
env: &dyn EnvLookup,
) -> Result<(), Error> {
run_mark_for_deletion_with(stdin, stdout, env, &RealGit::new())
}
pub fn run_mark_for_deletion_with<R: Read, W: Write>(
stdin: &mut R,
stdout: &mut W,
env: &dyn EnvLookup,
git: &dyn GitRunner,
) -> Result<(), Error> {
let input: MarkInput = read_input(stdin)?;
let worktree = resolve_worktree(env)?;
tools::mark_for_deletion(&worktree, &input.path, git).map_err(Error::Mark)?;
emit(stdout, "marked", input.path)
}
fn read_input<R: Read, T: for<'de> Deserialize<'de>>(stdin: &mut R) -> Result<T, Error> {
let mut buf = Vec::new();
stdin.read_to_end(&mut buf).map_err(Error::StdinRead)?;
serde_json::from_slice(&buf).map_err(Error::InvalidJson)
}
fn resolve_worktree(env: &dyn EnvLookup) -> Result<std::path::PathBuf, Error> {
let repo = env
.get(ENV_CONV_REPO)
.ok_or(Error::MissingEnv(ENV_CONV_REPO))?;
let branch = env
.get(ENV_CONV_BRANCH)
.ok_or(Error::MissingEnv(ENV_CONV_BRANCH))?
.into_string()
.map_err(|_| Error::MissingEnv(ENV_CONV_BRANCH))?;
Ok(workspace::agent_worktree(Path::new(&repo), &branch))
}
fn emit<W: Write>(stdout: &mut W, status: &str, path: String) -> Result<(), Error> {
let bytes = serde_json::to_vec(&Output { status, path }).expect("Output serializes");
stdout.write_all(&bytes).map_err(Error::Write)
}
#[cfg(test)]
mod tests;