https://github.com/jamesgober/mod-cli
A modular, high-performance CLI framework for Rust — built for customization, speed, and control.
https://github.com/jamesgober/mod-cli
cli cli-framework rust terminal
Last synced: 3 months ago
JSON representation
A modular, high-performance CLI framework for Rust — built for customization, speed, and control.
- Host: GitHub
- URL: https://github.com/jamesgober/mod-cli
- Owner: jamesgober
- License: apache-2.0
- Created: 2025-04-03T12:40:22.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2025-05-01T02:57:14.000Z (9 months ago)
- Last Synced: 2025-06-30T14:07:09.423Z (7 months ago)
- Topics: cli, cli-framework, rust, terminal
- Language: Rust
- Homepage:
- Size: 240 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
MOD-CLI
CLI FRAMEWORK
MOD-CLI is a lightweight, modular CLI framework for Rust.
Register commands, style output, and build interactive flows with a clean, zero-bloat core.
Focus on your app, not boilerplate.
Documentation
Full API docs are on docs.rs.
Docs are built with a minimal, stable feature set (internal-commands, custom-commands) to ensure MSRV compatibility.
Key Features
Custom Commands - Define your own commands with execution logic.
Colors and Gradients – Full-spectrum foreground and background colors with multi-color gradient support.
Styled Output – Compose bold, italic, underlined, and colorized output using a chainable builder. Includes table rendering and ANSI-safe formatting.
Animated Loaders – Built-in progress bars, spinners, and percent indicators with customizable characters, labels, and themes.
Interactive Console – Launch an interactive shell with command routing, custom input handlers, prompt styling, and exit messages.
Modular Architecture – Drop-in components for printing, styling, theming, command sources, and shell extensions. Easily replace or extend any layer.
Zero-Bloat Core – Minimal dependencies, clean structure, and fully optional features through Cargo flags.
Installation
Add the library to your `Cargo.toml`:
```toml
[dependencies]
mod-cli = "0.6.4"
```
Error Handling
Commands and selected APIs return structured errors via `ModCliError`.
Key updates:
- `Command::validate(&self, args) -> Result<(), modcli::error::ModCliError>`
- `set_startup_banner_from_file(path) -> Result<(), ModCliError>`
- `output::messages::load_messages_from_json(path) -> Result<(), ModCliError>` (feature: `theme-config`)
Minimal example:
```rust
use modcli::command::Command;
use modcli::error::ModCliError;
struct Hello;
impl Command for Hello {
fn name(&self) -> &str { "hello" }
fn help(&self) -> Option<&str> { Some("Greets the user") }
fn validate(&self, args: &[String]) -> Result<(), ModCliError> {
if args.len() > 1 {
return Err(ModCliError::InvalidUsage("hello takes at most one argument".into()));
}
Ok(())
}
fn execute(&self, args: &[String]) {
if let Some(name) = args.first() { println!("Hello, {name}!"); } else { println!("Hello!"); }
}
}
```
Programmatic dispatch:
```rust
use modcli::{loader::CommandRegistry, error::ModCliError};
let reg = CommandRegistry::new();
match reg.try_execute("unknown", &[]) {
Err(ModCliError::UnknownCommand(name)) => eprintln!("unknown: {name}"),
Err(other) => eprintln!("error: {other}"),
Ok(()) => {}
}
```
Add the library with features:
```toml
[dependencies]
mod-cli = { version = "0.6.4", features = ["gradients", "table-presets"] }
```
Feature Flags
| Feature | Description |
|----------------------|-------------|
| `internal-commands` | Built-in helper commands like `help`, `ping`, etc. |
| `custom-commands` | Ergonomic helpers for user-defined commands. |
| `tracing-logs` | Emit `tracing` events via `output::hook` alongside console output. |
| `dispatch-cache` | Single-entry dispatch cache to speed repeated invocations. |
| `gradients` | Named gradient helpers (24‑bit RGB) with zero extra deps. |
| `layouts` | Lightweight layout engine for terminal rows/columns. |
| `table-presets` | Convenience presets for `TableStyle` (ASCII, Rounded, Heavy). |
| `progress-presets` | Convenience constructors for `ProgressStyle` (compact, heavy). |
| `theme-config` | Enable theme config serialization (serde/serde_json). |
| `images` | Optional image support (png/jpeg) via the `image` crate. |
Usage
### Basic Usage
```rust
use modcli::ModCli;
fn main() {
let args: Vec = std::env::args().skip(1).collect();
let mut cli = ModCli::new();
cli.run(args);
}
```
### Set a custom prefix
```rust
use modcli::ModCli;
fn main() {
let args: Vec = std::env::args().skip(1).collect();
let mut cli = ModCli::new();
// Set a custom prefix
cli.set_prefix("myCLI");
cli.run(args);
}
```
#### Using named colors
```rust
use modcli::output::{build, colors, print};
let teal = colors::get("teal"); // returns a Color (or fallback)
let demo = build()
.part("Color Demo:").space()
.part("Teal").color(teal).bold().get();
print::line(&demo);
```
---
### Using Gradients
#### Two-color gradient:
```rust
use modcli::output::{
gradient,
print,
RED, ORANGE
};
let gradient_text = gradient::two_color("Two color gradient", RED, ORANGE);
print::line(&gradient_text);
```
#### Three-color gradient:
```rust
use modcli::output::{
gradient,
print,
BLUE, GREEN, YELLOW
};
let gradient_text = gradient::three_color("Three color gradient", BLUE, GREEN, YELLOW);
print::line(&gradient_text);
```
#### Multi-color gradient:
```rust
use modcli::output::{
gradient,
print,
RED, ORANGE, YELLOW, GREEN, BLUE
};
let gradient_text = gradient::multi_color("Multi-color gradient", vec![RED, ORANGE, YELLOW, GREEN, BLUE]);
print::line(&gradient_text);
```
#### Using RGB with gradients:
```rust
use modcli::output::{
gradient,
print
};
let gradient_text = gradient::two_color(
"Gradient Output",
Color::Rgb { r: 255, g: 0, b: 0 },
Color::Rgb { r: 0, g: 0, b: 255 },
);
print::line(&gradient_text);
```
### Themes
```rust
use modcli::output::{print, themes};
// Apply a theme (changes terminal fg/bg until reset)
themes::apply_theme("blue");
print::line("Applied theme: blue");
// Read colors for log categories from current theme
let t = themes::current_theme();
let categories = ["error","warn","success","info","debug","trace","notice","status"];
for key in categories {
let color = t.get_log_color(key);
println!("{key}: {:?}", color);
}
// Reset at the end
themes::Theme::reset();
```
---
### Output Styles
```rust
use modcli::output::{
print,
build,
BLUE
};
// 📦 Progress Bar Demo
let testing = build()
.part("Testing")
.color(BLUE)
.bold()
.get();
print::line(&testing);
// Outputs "Testing" in bold/blue.
```
#### Multiple Styles:
```rust
use modcli::output::{
gradient,
print,
build,
BLUE, LIGHT_BLUE
};
// 📦 Progress Bar Demo
let testing = build()
.part("Label:").color(BLUE).bold().space()
.part("This content has").space()
.part("multiple").color(LIGHT_BLUE).bold().space()
.part("styles").underline().space()
.part("and").italic().space()
.part("colors").underline().space()
.part("!")
.get();
print::line(&testing);
```
#### Style + Gradients:
```rust
use modcli::output::{
print,
build,
BLUE, GREEN
};
let gradient_text = gradient::two_color("Gradient Output", BLUE, GREEN);
let testing = build()
.part(&gradient_text).bold().space()
.part("+ Styled!")
.get();
print::line(&testing);
```
---
### Progress Bar & Animated Loaders
#### Auto Progress:
```rust
use modcli::output::{
progress::{
show_progress_bar,
}
};
show_progress_bar("Testing", 45, 1500);
```
> Displays
```bash
Label [#############################################] 100% Done!
```
#### Manual control:
```rust
use modcli::output::{
build,
progress::{
ProgressBar,
ProgressStyle,
},
LIGHT_BLUE
};
// Progress Bar Demo
let label = build()
.part("Loading")
.color(LIGHT_BLUE)
.bold()
.get();
let mut bar = ProgressBar::new(30, ProgressStyle {
fill: '■',
done_label: "Complete!",
color: Some(LIGHT_BLUE),
..Default::default()
});
bar.set_label(&label);
bar.start_auto(2000); // auto-fill in 2 seconds
```
#### Manual .tick() control (like during a loop):
```rust
use std::time::Duration;
use modcli::output::{
progress::{
ProgressBar,
ProgressStyle
},
ORANGE
};
use modcli::console::run_shell;
let mut bar = ProgressBar::new(10, ProgressStyle {
fill: '■',
done_label: "Done!",
color: Some(ORANGE),
..Default::default()
});
bar.set_label("Syncing");
for _ in 0..10 {
bar.tick();
std::thread::sleep(Duration::from_millis(200));
}
println!(" {}", bar.style.done_label);
```
#### Animated Spinner (Loading/Waiting):
```rust
use modcli::output::{
progress::{
show_spinner
}
};
show_spinner("Loading", 20, 100);
```
#### Animated Percentage Loader:
```rust
use std::thread::sleep;
use std::time::Duration;
use modcli::output::{
progress::{
show_percent_progress
}
};
for i in (0..=100).step_by(10) {
show_percent_progress("Loading", i);
sleep(Duration::from_millis(100));
}
println!();
```
---
### Tables
#### Table Example: Flex Width, Heavy Borders
```rust
use modcli::output::table::{render_table, TableMode, TableStyle};
let headers = ["Name", "Age", "Role"];
let rows = vec![
vec!["Alice", "29", "Engineer"],
vec!["Bob", "35", "Manager"],
vec!["Charlie", "41", "CTO"],
];
render_table(&headers, &rows, TableMode::Flex, TableStyle::Heavy);
```
> Outputs
```bash
┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
┃Name ┃Age ┃Role ┃
┣━━━━━━━━╋━━━━━━━━╋━━━━━━━━┫
┃Alice ┃29 ┃Engineer┃
┃Bob ┃35 ┃Manager ┃
┃Charlie ┃41 ┃CTO ┃
┗━━━━━━━━┻━━━━━━━━┻━━━━━━━━┛
```
#### Table Example: Fixed Width, Rounded Borders
```rust
use crate::output::table::{render_table, TableMode, TableStyle};
let headers = ["Name", "Age", "Role"];
let rows = vec![
vec!["Alice", "29", "Engineer"],
vec!["Bob", "35", "Manager"],
vec!["Charlie", "41", "CTO"],
];
render_table(&headers, &rows, TableMode::Fixed(15), TableStyle::Rounded);
```
> Outputs
```bash
╭───────────────┬───────────────┬───────────────╮
│Name │Age │Role │
├───────────────┼───────────────┼───────────────┤
│Alice │29 │Engineer │
│Bob │35 │Manager │
│Charlie │41 │CTO │
╰───────────────┴───────────────┴───────────────╯
```
#### Table Example: Fixed Width, Ascii Borders
```rust
use crate::output::table::{render_table, TableMode, TableStyle};
let headers = ["Name", "Age", "Role"];
let rows = vec![
vec!["Alice", "29", "Engineer"],
vec!["Bob", "35", "Manager"],
vec!["Charlie", "41", "CTO"],
];
render_table(&headers, &rows, TableMode::Fixed(15), TableStyle::Ascii);
```
> Outputs
```bash
+---------------+---------------+---------------+
|Name |Age |Role |
+---------------+---------------+---------------+
|Alice |29 |Engineer |
|Bob |35 |Manager |
|Charlie |41 |CTO |
+---------------+---------------+---------------+
```
Creating Custom Commands
### File Structure
```
my_project/
├── src/
│ ├── commands/
│ │ └── greet.rs ← define `GreetCommand` here
```
> Create a commands folder in src/, then put the command in its own file:
### Custom Command File
```rust
use modcli::command::Command;
pub struct GreetCommand;
impl Command for GreetCommand {
fn name(&self) -> &str {
"greet"
}
fn aliases(&self) -> &[&str] {
&["hi"]
}
fn help(&self) -> Option<&str> {
Some("Greets the user.")
}
fn validate(&self, _args: &[String]) -> Result<(), String> {
Ok(())
}
fn execute(&self, _args: &[String]) {
println!("Greetings!");
}
}
```
> greet.rs
### Register your command in `main.rs`, `tool.rs`, etc.
```rust
mod commands;
use modcli::ModCli;
use commands::greet::GreetCommand;
fn main() {
let args: Vec = std::env::args().skip(1).collect();
let mut cli = ModCli::new();
// Register function
cli.registry.register(Box::new(GreetCommand));
cli.run(args);
}
```
#### Test Command
```sh
$ myCLI greet
Greetings!
$ myCLI help
List of available commands...
```
Interactive Shell
### ModCLI supports an interactive console mode (like a REPL):
```rust
use modcli::config::CliConfig;
use modcli::console::run_shell;
fn main() {
let config = CliConfig::load(None);
run_shell(&config);
}
```
### Adding a custom console command for `shell`:
```rust
use modcli::ModCli;
use modcli::shell_commands::{register, ShellCommand};
fn greet_handler(_input: &str) -> bool {
println!("👋 Hello from shell command!");
true
}
fn main() {
register(ShellCommand {
name: "greet",
aliases: &["hi", "wave"],
help: "Greets the user with a friendly hello",
handler: greet_handler,
});
}
```
#### Config File Example (config.json)
```json
{
"modcli": {
"name" : "mod-cli",
"prefix": "mod",
"banner": "Welcome to ModCLI",
"delay" : 0,
"theme" : "default",
"strict": false,
"force_shell": false,
"shell": {
"prompt": "Tool >",
"welcome": ["Welcome to the console."],
"goodbye": ["Bye!"]
},
"messages": {
"no_command": "No command provided.",
"not_found": "Command not found."
}
}
}
```
> Default location: `project_root/config.json`
#### Manually set the config path (if not project root)
```rust
use modcli::config;
fn main() {
config::set_path(("my/custom/config.json");
```
> [!WARNING]
> **Pre-release**: This project is in active development.
> The core is stable but features are evolving. Production use is possible,
> but interfaces may still evolve until 1.0.
📌 License
Licensed under the Apache License, version 2.0 (the "License"); you may not use this software, including, but not limited to the source code, media files, ideas, techniques, or any other associated property or concept belonging to, associated with, or otherwise packaged with this software except in compliance with the License.
You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the LICENSE file included with this project for the specific language governing permissions and limitations under the License.
COPYRIGHT © 2025 JAMES GOBER.