mando-cli v2
Polymorphic IoC-based CLI for Alpiq BESS project status, dependency checking, and developer tooling. Built on a custom dependency injection framework (polym_ioc) with async providers, broadcast-based event hooks, and a composable CLI output system.
Status: WIP (2026-03-18)
Core IoC framework, provider system, and event hooks functional. Two contexts implemented (filesystem, binary dependencies). Static CLI output framework being extended with Table, Step, and Panel elements. Ratatui live TUI scaffolded but not yet integrated with provider data.
Architecture
MandoApp (builder)
├── .ioc(MandoPolyContainer) # type-erased HashMap<TypeId, Arc<dyn Any>>
├── .with::<MandoFsCtx>() # filesystem context (project root, structure, state)
├── .with::<BinaryDepStateProvider>() # binary dependency context (glab, etc.)
├── .with::<AggregateContext>() # composite context resolver
├── .hook::<AutoHook<_, MandoProjectRootPath>>() # reactive value watcher
└── .start() # execute all initializers
Core Traits
| Trait | Purpose |
|---|---|
DependencyContainer | Type-erased storage (resolve<T>, insert<T>) |
MandoCliContext<D> | Context with init/get/set/resolve against container D |
MandoContextualValueProvider<D, O> | Async value provider with event emission |
ProvidedContextValue | Marker with OutEventType + ErrEventType |
CacheSafeProvidedContextValue | Clone + ProvidedContextValue, enables caching |
ProviderValueHook<D> | Hook trait for reacting to value changes |
ContextAggregate<D> | Builds composite values from multiple contexts |
CliDisplay | Renders provider values as Vec<CliElement> for terminal output |
Data Flow
Provider resolves value
→ emits to broadcast::Sender<Option<OutEventType>>
→ AutoHook subscribes, deduplicates, forwards on mpsc
→ (future) ratatui TUI reacts to mpsc updates
Provider value implements CliDisplay
→ produces Vec<CliElement>
→ Page builder composes elements
→ render_to_term() outputs styled text via console crate
CLI Output Framework
Four-layer rendering pipeline:
CliDisplaytrait — provider values implement this to produce elementsCliElementenum — typed renderable elementsPagebuilder — fluent composition APIrender_to_term()— renders elements to stdout viaconsolecrate
Element Types
| Variant | Purpose |
|---|---|
KeyValue | label: value with style |
StatusRow | Icon + name + detail (dependency status) |
Header | Section header with ─ decoration |
Spacer | Blank line |
Block | Indented group of child elements |
Raw | Pre-formatted string |
Table | Columnar data with auto-aligned columns |
Step | Sequential check with Pass/Fail/Pending/Skipped state |
Panel | Bordered box with box-drawing chars, contains children |
Page Builder
Page::new()
.header("Mando Status")
.panel_display(Some("Project"), &status.project_root)
.spacer()
.header("Dependencies")
.steps(vec![
(StepState::Pass, "glab".into(), Some("ready".into())),
(StepState::Fail, "docker".into(), Some("not installed".into())),
])
.render();Styling
ElementStyle enum: Default, Success (green), Warning (yellow), Error (red), Muted (dim), Accent (cyan bold). Applied via console::style().
StepState enum: Pass (✓ green), Fail (✗ red), Pending (⠋ yellow), Skipped (- dim). Controls both icon and color.
Contexts
MandoFsCtx
Filesystem context providing:
MandoProjectRootPath— walks up from cwd looking for project markersMandoProjectStructure— project directory layoutCliDirState— persisted CLI state from.mando-state.json
BinaryDepStateProvider
Binary dependency checker providing BinaryDepState<B> for any B: BinaryDependency:
- Runs
which/whereto check installation - Optional auth check (
B::auth_check_args()) - Optional config check (
B::config_check_args()) - Currently implements:
Glab(GitLab CLI)
MandoCliStatus (Aggregate)
Composite context that resolves from both MandoFsCtx and BinaryDepStateProvider:
project_root: MandoProjectRootPathglab: BinaryDepState<Glab>
Event System
AutoHook<D, Target> watches ProvidedContextValue broadcast channels:
- Creates broadcast channels if not present in container
- Spawns tokio tasks for value and error channels
- Deduplicates value events (only forwards on change)
- Designed for future ratatui reactive updates
Dual Output Modes
- Static (current focus):
Pagebuilder →render_to_term()for one-shot command output (mando status) - Live (future): ratatui alternate screen with event loop, fed by
AutoHookchannels (mandowith no subcommand)
Both share the CliDisplay trait — same provider implementations feed both output paths.
Project Structure
src/
├── main.rs # tokio entry, MandoApp bootstrap, CLI/TUI routing
├── cli/
│ ├── commands.rs # clap Command definition
│ └── router.rs # subcommand dispatch
├── polym_ioc/
│ ├── container.rs # DependencyContainer trait + MandoPolyContainer
│ ├── ctx.rs # MandoCliContext trait
│ ├── provider.rs # MandoContextualValueProvider, ProvidedContextValue
│ ├── app.rs # MandoApp builder
│ └── aggregate.rs # ContextResolver, AggregateContext
├── events/
│ └── hook.rs # ProviderValueHook, AutoHook
├── contexts/
│ ├── status.rs # MandoCliStatus aggregate
│ ├── fs/ # filesystem context + providers
│ │ ├── ctx.rs
│ │ └── providers/
│ │ ├── project_rootpath_provider.rs
│ │ ├── mando_project_structure_provider.rs
│ │ └── cli_dir_state_provider.rs
│ └── deps/ # dependency context + providers
│ ├── ctx.rs
│ └── providers/
│ ├── generic.rs # BinaryDependency trait, BinaryDepState<B>
│ └── glab_ctx.rs # Glab impl
└── tui/
├── mod.rs # MandoTui (ratatui shell)
├── draw.rs # ratatui frame drawing
├── display.rs # CliDisplay, CliElement, ElementStyle, StatusIcon, StepState
├── render.rs # render_to_term, element rendering
├── page.rs # Page fluent builder
└── impls.rs # CliDisplay impls for provider types
Dependencies
| Crate | Purpose |
|---|---|
| tokio | Async runtime (full features) |
| tracing / tracing-subscriber | Structured logging |
| thiserror | Error derives |
| async-trait | Async trait support |
| serde / serde_json | Serialization |
| anyhow | Error handling |
| clap | CLI argument parsing |
| console | Styled terminal output (static) |
| indicatif | Progress bars |
| ratatui / crossterm | Live TUI (future) |
| boxen | Boxed text rendering |
Related
- mando-cli — v1 YAML recipe runner (separate binary, same directory)
- Mando — the service being managed
- Alpiq BESS — project overview