degenbot_cli_core/lib.rs
1//! `degenbot-cli-core` — the clap-free semantics home for the degenbot console
2//! (ADR-051 D1/D2).
3//!
4//! The console has two first-class front ends: the pure-Rust argv facade
5//! (`degenbot-cli`, clap) and the Python passthrough (`src/degenbot/_cli.py`).
6//! Both map argv into the SAME command model declared here, so command
7//! semantics — prompt policy, dry-run text, exit-code mapping — exist exactly
8//! once. This crate is deliberately **pyo3-free** (so the pure-Rust consumer can
9//! depend on it) and **clap-free/indicatif-free** (argv spelling and progress
10//! rendering are the facade's job; asserted by `just check-cli-core-purity`).
11//!
12//! # Shape
13//!
14//! - [`Command`] + the per-group enums: the constructor-parsed command model.
15//! - [`Command::execute`] / [`run_with_cancel`]: the execution entries,
16//! returning a typed [`CommandReport`].
17//! - [`CliError`] → [`ExitCode`]: declared at exactly one `From` site; the
18//! workspace `exit = "deny"` lint stands, so `run` returns codes and never
19//! aborts the process. The typed fleet boot refusal (FF-T1) maps to
20//! `EX_CONFIG` 78 (lifted out of `DegenbotCLI.invoke`).
21//! - [`PromptPlan`] + [`Prompter`]: interactive policy is declared data, ported
22//! verbatim from the click handlers and audited, never redesigned (D4).
23//! - [`CancelHandle`]: the cooperative cancel carrier the updater arms thread
24//! into `run_pool_update` / `run_aave_update` (the facade owns the SIGINT
25//! policy, ADR-051 D7).
26//!
27//! # Groups
28//!
29//! - `database` ([`DatabaseCommand`]): the template group; ports
30//! `src/degenbot/cli/database.py` arm for arm.
31//! - `exchange` ([`ExchangeCommand`]): the 34 Python click verbs collapse to
32//! one data-driven command resolving `(chain, name)` through the
33//! `degenbot-uniswap` deployments registry (ADR-051 D5).
34//! - `pool` ([`PoolCommand`]): the `degenbot-pool-updater` chunk loop +
35//! on-chain-truth verify.
36//! - `aave` ([`AaveCommand`]): the `degenbot-aave` market run + row flips.
37//! - `fleet` ([`FleetCommand`]): the live cordon posture over the
38//! operator command channel (ADR-051 D6).
39//! - `path` ([`PathCommand`]): live add-path / bounded discovery over
40//! the same operator command channel.
41
42pub mod aave;
43pub mod block;
44pub mod cancel;
45pub mod command;
46pub mod context;
47pub mod database;
48pub mod error;
49pub mod exchange;
50pub mod fleet;
51pub mod operator;
52pub mod path;
53pub mod pool;
54pub mod prompt;
55pub mod registrations;
56pub mod report;
57pub mod strategy;
58
59pub use aave::{resolve_aave_deployment, AaveCommand, AaveDeployment, AAVE_DEPLOYMENTS};
60pub use block::{
61 parse_to_block, resolve_chain_selector, resolve_to_block, BlockTag, ToBlockSpec,
62 DEFAULT_CHUNK_SIZE, DEFAULT_TO_BLOCK, DEFAULT_VERIFY_ALL_INTERVAL,
63};
64pub use cancel::CancelHandle;
65pub use command::{Command, DatabaseCommand};
66pub use context::CliContext;
67pub use database::database_backup_path;
68pub use error::{CliError, ExitCode};
69pub use exchange::{
70 resolve_deployment, ExchangeCommand, ExchangeDeployment, PoolManagerDeployment,
71 RETIRED_EXCHANGES,
72};
73pub use fleet::FleetCommand;
74pub use operator::{
75 parse_hop_token, parse_sim_intake_floor, render_json_sorted, resolve_socket,
76 validate_posture_patch, PathDirection, PathFamily, PathStep, PosturePatchEntry,
77 PosturePatchValue, WireRequest, WireResponse, FLEET_POSTURE_THRESHOLD_KEYS,
78 SIM_INTAKE_FLOOR_RESTORE, SOCKET_DEFAULT, SOCKET_ENV,
79};
80pub use path::PathCommand;
81pub use pool::{PoolCommand, PoolFamily};
82pub use prompt::{PromptPlan, Prompter};
83pub use registrations::{ensure_supported_registrations, RegistrationReport};
84pub use report::{
85 schema_state_label, AavePositionLine, AaveReport, AaveUpdateEntry, AaveUpdateOutcome,
86 ActivateOutcome, CommandOutcome, CommandReport, CutoverOutcome, DatabaseReport,
87 DeactivateOutcome, DryRunKind, ExchangeActiveState, ExchangeListRow, ExchangeReport,
88 FleetReport, PathReport, PoolReport, StrategyReport,
89};
90pub use strategy::{
91 descriptor, descriptors, EndpointSummary, MutationOutcome, StrategyCommand, StrategyFacet,
92 StrategyFacetDescriptor,
93};
94
95/// The ONE execution entry: run `command` against `ctx`, asking `prompter` when
96/// the command's [`PromptPlan`] requires it.
97///
98/// Returns a [`CommandOutcome`] carrying the typed [`CommandReport`] (on
99/// success) and the [`ExitCode`] derived at the single `From<&CliError>` site.
100/// A fresh [`CancelHandle`] is created (no SIGINT wiring); use
101/// [`run_with_cancel`] to share the facade's handle.
102#[must_use]
103pub fn run(command: &Command, ctx: &CliContext<'_>, prompter: &dyn Prompter) -> CommandOutcome {
104 run_with_cancel(command, ctx, prompter, &CancelHandle::new())
105}
106
107/// The cancellable execution entry (ADR-051 D7).
108#[must_use]
109pub fn run_with_cancel(
110 command: &Command,
111 ctx: &CliContext<'_>,
112 prompter: &dyn Prompter,
113 cancel: &CancelHandle,
114) -> CommandOutcome {
115 let result = command.execute_with_cancel(ctx, prompter, cancel);
116 let exit_code = match &result {
117 Ok(_) => ExitCode::Success,
118 Err(err) => ExitCode::from(err),
119 };
120 CommandOutcome::new(result, exit_code)
121}