Skip to main content

coding_tools/cli/
ct_await.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Jonathan Shook
3
4//! The `ct-await` command grammar (see [`crate::cli`]); the `ct-await` bin is a
5//! thin parse-and-dispatch wrapper over this `Cli`.
6
7use clap::Parser;
8
9use crate::explain::Format;
10use crate::pattern;
11use crate::pulse::HeartbeatOpts;
12
13#[derive(Parser, Debug)]
14#[command(
15    name = "ct-await",
16    version,
17    about = "Poll a read-only probe until it succeeds, an abort pattern appears, or the bound expires.",
18    long_about = "ct-await runs a gated read-only probe every --every seconds until the condition \
19                  is established — probe exit 0, or a required --ok-match appearing in its output — \
20                  or until an --err-match appears (immediate ERROR) or the required --timeout \
21                  expires (ERROR). Observe an external process's effects without owning its \
22                  execution (also reachable as `ct await`). See `ct-await --explain` for \
23                  agent-oriented documentation."
24)]
25pub struct Cli {
26    /// Question this wait answers; printed as a "== ... ==" banner.
27    #[arg(long)]
28    pub question: Option<String>,
29
30    /// Seconds between probe runs (fractional allowed).
31    #[arg(long, value_name = "SECS", default_value_t = 5.0)]
32    pub every: f64,
33
34    /// Hard bound on the whole wait (fractional allowed). Required: a wait is bounded by design.
35    #[arg(long, value_name = "SECS")]
36    pub timeout: f64,
37
38    /// SUCCESS when this pattern (substring->glob->regex promoted) appears in the probe's output. When supplied it is the REQUIRED proof: a clean exit without it means "not yet".
39    #[arg(long, value_name = "PATTERN")]
40    pub ok_match: Option<String>,
41
42    /// End the wait immediately with ERROR when this pattern appears in the probe's output (decisive over --ok-match, exactly as in ct-test).
43    #[arg(long, value_name = "PATTERN")]
44    pub err_match: Option<String>,
45
46    /// Pin how matcher patterns are interpreted (promotion off): literal, glob, or regex.
47    #[arg(long, value_enum)]
48    pub mode: Option<pattern::Mode>,
49
50    #[command(flatten)]
51    pub heartbeat: HeartbeatOpts,
52
53    /// Template written to stdout when the wait ends. Tokens: {RESULT} {ELAPSED} {TICKS} {REASON} {QUESTION} {CMD}.
54    #[arg(long, alias = "emit-stdout")]
55    pub emit: Option<String>,
56
57    /// Template written to stderr when the wait ends (same tokens as --emit).
58    #[arg(long)]
59    pub emit_stderr: Option<String>,
60
61    /// Suppress the banner and the default outcome line.
62    #[arg(long)]
63    pub quiet: bool,
64
65    /// Print agent usage docs (md or json) and exit.
66    #[arg(long, value_enum, num_args = 0..=1, default_missing_value = "md")]
67    pub explain: Option<Format>,
68
69    /// The probe (after `--`): an argv run directly each tick, never through a shell. Exit 0 ends the wait with SUCCESS; any other exit means "not yet".
70    #[arg(last = true, value_name = "PROBE...")]
71    pub probe: Vec<String>,
72}