#![allow(
clippy::todo,
clippy::unimplemented,
clippy::panic,
clippy::unwrap_used,
clippy::expect_used,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::doc_markdown,
clippy::needless_pass_by_value,
clippy::too_many_arguments,
clippy::unused_async,
clippy::diverging_sub_expression,
clippy::no_effect_underscore_binding,
clippy::let_unit_value,
clippy::used_underscore_binding,
clippy::let_underscore_untyped,
clippy::struct_field_names,
clippy::manual_let_else,
clippy::map_unwrap_or,
clippy::redundant_pub_crate,
dead_code,
unreachable_code,
unused_assignments,
unused_mut,
unused_imports,
unused_variables
)]
mod steps;
use std::env;
use std::process;
use arcp::error::ARCPError;
use arcp::transport::MemoryTransport;
use arcp::{ARCPClient, ErrorCode};
use serde_json::{json, Value};
use crate::steps::run_step;
type Client = ARCPClient<MemoryTransport>;
const STEPS: &[&str] = &["plan", "gather", "synthesize", "critique", "finalize"];
fn step_key(_job_id: &str, _step: &str, _salt: &str) -> String {
todo!()
}
async fn emit_progress(_client: &Client, _job_id: &str, _step: &str) -> Result<(), ARCPError> {
todo!()
}
async fn emit_checkpoint(_client: &Client, _job_id: &str, step: &str) -> Result<String, ARCPError> {
let chk = format!("chk_{step}_<job_suffix>");
Ok(chk)
}
async fn execute_steps(
client: &Client,
job_id: &str,
request: Value,
starting_at: &str,
crash_after: Option<&str>,
) -> Result<Value, ARCPError> {
let start_idx = STEPS.iter().position(|s| *s == starting_at).unwrap_or(0);
let mut output = request;
for (i, step) in STEPS.iter().enumerate().skip(start_idx) {
let _key = step_key(job_id, step, &output.to_string());
emit_progress(client, job_id, step).await?;
output = json!({"step": step, "i": i});
let _chk = emit_checkpoint(client, job_id, step).await?;
if crash_after == Some(*step) {
println!(
"[crash after {step}; resume with RESUME_JOB_ID={job_id} \
RESUME_CHECKPOINT_ID=chk_{step}_<job_suffix> \
RESUME_AFTER_MSG_ID=<last id from your event log>]"
);
process::exit(137);
}
}
Ok(output)
}
async fn issue_resume(
_client: &Client,
_job_id: &str,
_after_message_id: &str,
_checkpoint_id: Option<&str>,
) -> Result<Option<String>, ARCPError> {
todo!()
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client: Client = todo!();
let resume_job = env::var("RESUME_JOB_ID").ok();
let resume_after = env::var("RESUME_AFTER_MSG_ID").ok();
if let (Some(rj_id), Some(rj_after)) = (resume_job, resume_after) {
let last = issue_resume(
&client,
&rj_id,
&rj_after,
env::var("RESUME_CHECKPOINT_ID").ok().as_deref(),
)
.await?;
match last {
None => println!("already terminal during replay"),
Some(label) => {
let next = STEPS.iter().position(|s| **s == label).unwrap_or(0) + 1;
if next >= STEPS.len() {
println!("nothing to resume");
} else {
println!("[resuming at {}]", STEPS[next]);
let final_ =
execute_steps(&client, &rj_id, json!("<replayed>"), STEPS[next], None)
.await?;
println!("done: {final_}");
}
}
}
} else {
let job_id = "job_<uuid>";
let request = "Survey CRDT-based collaborative editing in 2026.";
let final_ = execute_steps(
&client,
job_id,
json!(request),
STEPS[0],
env::var("CRASH_AFTER_STEP").ok().as_deref(),
)
.await?;
println!("job_id={job_id}\n{final_}");
}
Ok(())
}