|
| 1 | +# `scream-cli` Developer Documentation |
| 2 | + |
| 3 | +This document provides a comprehensive technical breakdown of the `scream-cli` crate. It details the flow of execution, configuration handling, data management, and the user interface system. It is intended for developers contributing to or extending the CLI's functionality. |
| 4 | + |
| 5 | +For user-facing instructions on how to _use_ the CLI, see the [User Manual (`/docs/cli/USAGE.md`)](/docs/cli/USAGE.md). |
| 6 | + |
| 7 | +**Table of Contents** |
| 8 | + |
| 9 | +- [`scream-cli` Developer Documentation](#scream-cli-developer-documentation) |
| 10 | + - [1. Component Overview](#1-component-overview) |
| 11 | + - [2. Execution Flow of a `scream place` Command](#2-execution-flow-of-a-scream-place-command) |
| 12 | + - [3. The Layered Configuration System](#3-the-layered-configuration-system) |
| 13 | + - [3.1. The Three Layers of Configuration](#31-the-three-layers-of-configuration) |
| 14 | + - [3.2. The Build Process in `config::builder`](#32-the-build-process-in-configbuilder) |
| 15 | + - [4. Data Management with `DataManager`](#4-data-management-with-datamanager) |
| 16 | + - [4.1. Data Path Resolution](#41-data-path-resolution) |
| 17 | + - [4.2. Logical Name Resolution](#42-logical-name-resolution) |
| 18 | + - [4.3. Data Downloading](#43-data-downloading) |
| 19 | + - [5. User Interface (UI) and Logging](#5-user-interface-ui-and-logging) |
| 20 | + - [5.1. Decoupled Architecture with `tokio::mpsc`](#51-decoupled-architecture-with-tokiompsc) |
| 21 | + - [5.2. Progress Reporting](#52-progress-reporting) |
| 22 | + - [5.3. Logging Integration](#53-logging-integration) |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## 1. Component Overview |
| 27 | + |
| 28 | +The `scream-cli` crate is structured into several modules, each with a distinct responsibility, to create a maintainable and extensible command-line application. |
| 29 | + |
| 30 | +- **`main.rs`**: The application entry point. It is responsible for setting up the `tokio` asynchronous runtime, initializing global error handling (`color-eyre`), parsing command-line arguments, and spawning the main UI manager task. |
| 31 | + |
| 32 | +- **`cli.rs`**: Defines the entire command-line interface structure using `clap`'s derive macros. This file is the single source of truth for all commands (`place`, `data`), subcommands, arguments, and help messages. |
| 33 | + |
| 34 | +- **`commands/`**: Contains the primary business logic for each subcommand. |
| 35 | + |
| 36 | + - `place.rs`: Orchestrates the entire side-chain placement process, from configuration building to calling `scream-core` and processing the results. |
| 37 | + - `data.rs`: Implements the logic for the `scream data` subcommands (`download`, `path`, etc.). |
| 38 | + |
| 39 | +- **`config/`**: The configuration engine. |
| 40 | + |
| 41 | + - `file.rs`: Defines the Rust structs that map directly to the `config.toml` file format using `serde`. |
| 42 | + - `defaults.rs`: Provides hardcoded, sensible fallback values for all configurable parameters. |
| 43 | + - `builder.rs`: Implements the core logic for merging the three configuration layers (defaults, file, and CLI arguments) into a final, validated `PlacementConfig` struct for `scream-core`. |
| 44 | + |
| 45 | +- **`data.rs`**: Implements the `DataManager`, a crucial abstraction that handles the physical location, resolution of logical names, and downloading of external data files (forcefields, rotamer libraries). |
| 46 | + |
| 47 | +- **`ui.rs`**: Manages all terminal output. It uses the `indicatif` crate for progress bars and runs in a separate `tokio` task. It receives events to display from both the core library and the logging system via a shared channel. |
| 48 | + |
| 49 | +- **`logging.rs`**: Configures the `tracing` subscriber framework. Its key feature is a custom `tracing::Layer` (`ChannelLayer`) that intercepts log messages and forwards them to the `UiManager` for display, ensuring that logging does not interfere with progress bar rendering. |
| 50 | + |
| 51 | +- **`error.rs`**: Defines the CLI-specific error enum (`CliError`), which centralizes error handling for the application. |
| 52 | + |
| 53 | +## 2. Execution Flow of a `scream place` Command |
| 54 | + |
| 55 | +A typical `scream place` command follows a well-defined sequence of operations, translating user input into a scientific result. |
| 56 | + |
| 57 | +**Figure 1: `scream place` Execution Flow** |
| 58 | + |
| 59 | +```mermaid |
| 60 | +sequenceDiagram |
| 61 | + participant User |
| 62 | + participant CLI (main.rs) |
| 63 | + participant ConfigBuilder as Config Builder (config::builder) |
| 64 | + participant DataManager (data.rs) |
| 65 | + participant Core Library (scream-core) |
| 66 | + participant UI Manager (ui.rs) |
| 67 | +
|
| 68 | + User->>+CLI: executes `scream place ...` |
| 69 | + CLI->>CLI: `clap::parse()` command-line args |
| 70 | + CLI->>+UI Manager: Spawn UiManager task |
| 71 | + CLI->>+Config Builder: `build_config(args)` |
| 72 | + Config Builder->>+DataManager: Resolve logical names (e.g., 'charmm@rmsd-1.0') |
| 73 | + DataManager-->>-Config Builder: Return concrete file paths |
| 74 | + Config Builder-->>-CLI: Return final `PlacementConfig` |
| 75 | + CLI->>+Core Library: `workflows::place::run(system, config, reporter)` |
| 76 | + Note right of Core Library: Emits `Progress` events to <br/>UI Manager via reporter callback |
| 77 | + Core Library-->>-CLI: Return `PlacementResult` |
| 78 | + CLI->>CLI: Process results & write output file(s) |
| 79 | + CLI->>-UI Manager: Send shutdown signal |
| 80 | +``` |
| 81 | + |
| 82 | +1. **Parsing**: `main.rs` uses `clap` to parse all command-line arguments into the `Cli` struct. |
| 83 | +2. **UI Initialization**: The `UiManager` is spawned as a separate asynchronous `tokio` task to handle all terminal rendering independently of the main computation. |
| 84 | +3. **Configuration Building**: `commands::place::run` calls `config::builder::build_config`, passing the parsed arguments. |
| 85 | +4. **Path Resolution**: The `Config Builder` uses the `DataManager` to resolve any "logical names" for data files (like `'charmm@rmsd-1.0'`) into absolute file paths. |
| 86 | +5. **Core Invocation**: A final, validated `PlacementConfig` is constructed and passed to the `scream_core::workflows::place::run` function. The computationally intensive work happens here, executed within a `tokio::task::spawn_blocking` call to avoid blocking the async runtime. |
| 87 | +6. **Progress Reporting**: During execution, the core library sends `Progress` events back to the `UiManager` via a callback, which updates the progress bars in the terminal. |
| 88 | +7. **Result Handling**: Once `place::run` returns a `PlacementResult`, the `commands::place` module formats the summary, prints it to the console, and writes the resulting molecular structure(s) to the output file(s) specified by the user. |
| 89 | + |
| 90 | +## 3. The Layered Configuration System |
| 91 | + |
| 92 | +The CLI's configuration system is designed to be flexible and predictable, merging settings from three distinct sources with a clear order of precedence. |
| 93 | + |
| 94 | +### 3.1. The Three Layers of Configuration |
| 95 | + |
| 96 | +Settings are determined by the first layer in which they are found, following this hierarchy: |
| 97 | + |
| 98 | +**Figure 2: Configuration Priority** |
| 99 | + |
| 100 | +```mermaid |
| 101 | +graph TD |
| 102 | + subgraph Priority |
| 103 | + direction LR |
| 104 | + A["<strong>Layer 1:</strong><br/>CLI Arguments<br/>(Highest Priority)"] --> B["<strong>Layer 2:</strong><br/>`config.toml` File"]; |
| 105 | + B --> C["<strong>Layer 3:</strong><br/>Built-in Defaults<br/>(Lowest Priority)"]; |
| 106 | + end |
| 107 | +``` |
| 108 | + |
| 109 | +1. **Layer 1: Command-Line Arguments**: Any argument provided directly on the command line (e.g., `--s-factor 1.2`, `-n 5`) always overrides settings from other layers. This is ideal for quick experiments and scripting. |
| 110 | +2. **Layer 2: TOML Configuration File**: The `config.toml` file is the primary method for specifying complex or persistent settings, such as the `[residues-to-optimize]` table. |
| 111 | +3. **Layer 3: Built-in Defaults (`config::defaults`)**: These are hardcoded fallback values that ensure the program can run with minimal user input. |
| 112 | + |
| 113 | +### 3.2. The Build Process in `config::builder` |
| 114 | + |
| 115 | +The `config::builder::build_config` function is the orchestrator of this merging logic. Its process is as follows: |
| 116 | + |
| 117 | +1. It starts with the `DefaultsConfig` struct. |
| 118 | +2. It loads the `FileConfig` struct by parsing the TOML file specified via `--config`. If no file is given, an empty `FileConfig` is used. |
| 119 | +3. It applies any `--set KEY=VALUE` arguments, directly modifying the in-memory `FileConfig` struct. |
| 120 | +4. It iterates through every parameter required by `scream-core`'s `PlacementConfig`. For each parameter, it checks for a value in this order: command-line argument, `FileConfig` struct, and finally the `DefaultsConfig`. |
| 121 | +5. During this process, it uses the `DataManager` to translate any string-based "logical names" into verified `PathBuf`s. |
| 122 | +6. Finally, it constructs and returns the fully validated `PlacementConfig` object. |
| 123 | + |
| 124 | +## 4. Data Management with `DataManager` |
| 125 | + |
| 126 | +The `DataManager` (`data.rs`) is a critical abstraction that decouples the application logic from the physical storage of data files like forcefields and rotamer libraries. |
| 127 | + |
| 128 | +### 4.1. Data Path Resolution |
| 129 | + |
| 130 | +The `DataManager` determines the root directory for all data files using a two-step process: |
| 131 | + |
| 132 | +1. It first looks for a configuration file in an OS-specific config location (e.g., `~/.config/screampp/path.conf` on Linux). This file can be created and modified by the user via `scream data set-path` and `reset-path`. |
| 133 | +2. If this file does not exist, it falls back to an OS-specific default data directory (e.g., `~/.local/share/screampp` on Linux). This logic is handled by the `directories-rs` crate. |
| 134 | + |
| 135 | +This ensures a predictable and platform-idiomatic location for data while still allowing user customization. |
| 136 | + |
| 137 | +### 4.2. Logical Name Resolution |
| 138 | + |
| 139 | +To provide a user-friendly experience, the CLI uses "logical names" instead of requiring full file paths for common data files. |
| 140 | + |
| 141 | +- **The Problem**: A user should not have to type `--rotamer-library /path/to/data/rotamers/charmm/rmsd-1.0.toml`. |
| 142 | +- **The Solution**: |
| 143 | + 1. The `utils::parser` module contains functions that parse user-friendly strings like `'charmm@rmsd-1.0'` into a structured representation (e.g., `RotamerLibraryName { scheme: "charmm", diversity: "rmsd-1.0" }`). |
| 144 | + 2. The `DataManager::resolve_logical_name` method takes this structured data and constructs the full, platform-correct file path within the resolved data directory. It also performs an existence check on the final path. |
| 145 | + |
| 146 | +### 4.3. Data Downloading |
| 147 | + |
| 148 | +The `scream data download` command automates the acquisition of required data. |
| 149 | + |
| 150 | +- **Process**: It uses the `reqwest` library to fetch a version-matched `.tar.zst` archive from the official SCREAM++ GitHub Releases page. The downloaded data is streamed into memory, decompressed on-the-fly using the `zstd` crate, and unpacked into the data directory using the `tar` crate. This entire process is visualized with an `indicatif` progress bar. |
| 151 | + |
| 152 | +## 5. User Interface (UI) and Logging |
| 153 | + |
| 154 | +The CLI's user interface is designed to be responsive and non-blocking, providing clear feedback without slowing down the core computation. |
| 155 | + |
| 156 | +### 5.1. Decoupled Architecture with `tokio::mpsc` |
| 157 | + |
| 158 | +The UI is architected around an asynchronous, event-driven model. |
| 159 | + |
| 160 | +**Figure 3: UI and Logging Event Flow** |
| 161 | + |
| 162 | +```mermaid |
| 163 | +graph TD |
| 164 | + subgraph "Core Computation (Blocking Thread)" |
| 165 | + A["scream-core Reporter"]; |
| 166 | + end |
| 167 | +
|
| 168 | + subgraph "CLI Main Thread" |
| 169 | + B["tracing::Layer"]; |
| 170 | + end |
| 171 | +
|
| 172 | + subgraph "UI Task (Async)" |
| 173 | + E["UiManager"]; |
| 174 | + F["indicatif MultiProgress"]; |
| 175 | + E --> F; |
| 176 | + end |
| 177 | +
|
| 178 | + C["tokio::mpsc::channel"]; |
| 179 | +
|
| 180 | + A -- "Progress Event" --> C; |
| 181 | + B -- "Log Event" --> C; |
| 182 | + C -- "UiEvent" --> E; |
| 183 | + F --> G[Terminal Output]; |
| 184 | +``` |
| 185 | + |
| 186 | +- **Decoupling**: The main computational logic (in `scream-core`) runs in a blocking thread pool managed by `tokio::task::spawn_blocking`. The `UiManager` runs in a separate, non-blocking asynchronous task. |
| 187 | +- **Communication**: They communicate via a `tokio::mpsc` (multi-producer, single-consumer) channel. This allows the computationally-heavy core library and the logging system to send `UiEvent` messages to the `UiManager` without waiting for the terminal to render. |
| 188 | + |
| 189 | +### 5.2. Progress Reporting |
| 190 | + |
| 191 | +- A `CliProgressHandler` is created in the CLI. It holds a sender handle to the mpsc channel. |
| 192 | +- It generates a `ProgressCallback` closure (a `Box<dyn Fn(...)>`), which captures the channel sender. |
| 193 | +- This callback is passed down into `scream-core` as part of the `ProgressReporter`. |
| 194 | +- When the core library needs to report progress (e.g., `Progress::TaskIncrement`), it calls the callback. The callback's only job is to send the `Progress` event into the channel asynchronously. |
| 195 | +- The `UiManager`'s main loop receives the event and updates the `indicatif` progress bars accordingly. |
| 196 | + |
| 197 | +### 5.3. Logging Integration |
| 198 | + |
| 199 | +To prevent log messages from interfering with the dynamically rendered progress bars, the logging system is integrated into the same UI event loop. |
| 200 | + |
| 201 | +- **`logging::ChannelLayer`**: A custom `tracing::Layer` is implemented. |
| 202 | +- **Mechanism**: Instead of writing formatted log messages directly to `stdout` or `stderr`, this layer intercepts each log event from the `tracing` framework. It formats the message and sends it as a `UiEvent::Log` over the _same_ mpsc channel used for progress events. |
| 203 | +- **Benefit**: The `UiManager` receives both log messages and progress updates through a single stream. It can then use `indicatif`'s `MultiProgress::println` method to print the log message _above_ the active progress bars, ensuring a clean, non-flickering, and readable terminal output. |
0 commit comments