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//! - `config` ([`ConfigCommand`]): the read-only view of the operator file
42//! (ADR-062 D6) — the values, the winning layer per value, and the file the
43//! mutating arms write.
44
45pub mod aave;
46pub mod block;
47pub mod cancel;
48pub mod command;
49pub mod config;
50pub mod context;
51pub mod database;
52pub mod error;
53pub mod exchange;
54pub mod fleet;
55pub mod operator;
56pub mod path;
57pub mod pool;
58pub mod prompt;
59pub mod registrations;
60pub mod report;
61pub mod strategy;
62
63pub use aave::{resolve_aave_deployment, AaveCommand, AaveDeployment, AAVE_DEPLOYMENTS};
64pub use block::{
65 parse_to_block, resolve_chain_selector, resolve_to_block, BlockTag, ToBlockSpec,
66 DEFAULT_CHUNK_SIZE, DEFAULT_TO_BLOCK, DEFAULT_VERIFY_ALL_INTERVAL,
67};
68pub use cancel::CancelHandle;
69pub use command::{Command, DatabaseCommand};
70pub use config::ConfigCommand;
71pub use context::CliContext;
72pub use database::database_backup_path;
73pub use error::{CliError, ExitCode};
74pub use exchange::{
75 resolve_deployment, ExchangeCommand, ExchangeDeployment, PoolManagerDeployment,
76 RETIRED_EXCHANGES,
77};
78pub use fleet::FleetCommand;
79pub use operator::{
80 parse_hop_token, parse_sim_intake_floor, render_json_sorted, resolve_socket,
81 validate_posture_patch, PathDirection, PathFamily, PathStep, PosturePatchEntry,
82 PosturePatchValue, WireRequest, WireResponse, FLEET_POSTURE_THRESHOLD_KEYS,
83 SIM_INTAKE_FLOOR_RESTORE, SOCKET_DEFAULT, SOCKET_ENV,
84};
85pub use path::PathCommand;
86pub use pool::{PoolCommand, PoolFamily};
87pub use prompt::{PromptPlan, Prompter};
88pub use registrations::{ensure_supported_registrations, RegistrationReport};
89pub use report::{
90 schema_state_label, AavePositionLine, AaveReport, AaveUpdateEntry, AaveUpdateOutcome,
91 ActivateOutcome, CommandOutcome, CommandReport, ConfigReport, ConfigValue, CutoverOutcome,
92 DatabaseReport, DeactivateOutcome, DryRunKind, ExchangeActiveState, ExchangeListRow,
93 ExchangeReport, FleetReport, PathReport, PoolReport, StrategyReport,
94};
95pub use strategy::{
96 descriptor, descriptors, EndpointSummary, MutationOutcome, StrategyCommand, StrategyFacet,
97 StrategyFacetDescriptor,
98};
99
100/// The ONE execution entry: run `command` against `ctx`, asking `prompter` when
101/// the command's [`PromptPlan`] requires it.
102///
103/// Returns a [`CommandOutcome`] carrying the typed [`CommandReport`] (on
104/// success) and the [`ExitCode`] derived at the single `From<&CliError>` site.
105/// A fresh [`CancelHandle`] is created (no SIGINT wiring); use
106/// [`run_with_cancel`] to share the facade's handle.
107#[must_use]
108pub fn run(command: &Command, ctx: &CliContext<'_>, prompter: &dyn Prompter) -> CommandOutcome {
109 run_with_cancel(command, ctx, prompter, &CancelHandle::new())
110}
111
112/// The cancellable execution entry (ADR-051 D7).
113#[must_use]
114pub fn run_with_cancel(
115 command: &Command,
116 ctx: &CliContext<'_>,
117 prompter: &dyn Prompter,
118 cancel: &CancelHandle,
119) -> CommandOutcome {
120 let result = command.execute_with_cancel(ctx, prompter, cancel);
121 let exit_code = match &result {
122 Ok(_) => ExitCode::Success,
123 Err(err) => ExitCode::from(err),
124 };
125 CommandOutcome::new(result, exit_code)
126}