mod-cli 0.6.4

A fully customizable, feature-rich CLI framework for Rust. Define commands, prefixes, styled output, and more—built for flexibility and speed.
Documentation

Add the library to your Cargo.toml:

[dependencies]
mod-cli = "0.6.4"

Add the library to your Cargo.toml with features:

[dependencies]
mod-cli = { version = "0.6.4", features = ["gradients", "table-presets"] }
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.

Basic Usage

use modcli::ModCli;

fn main() {
    let args: Vec<String> = std::env::args().skip(1).collect();
    let mut cli = ModCli::new();
    cli.run(args);
}

Set a custom prefix

use modcli::ModCli;

fn main() {
    let args: Vec<String> = 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

 let teal = colors::get("teal"); // always returns a Color (or fallback)
 let demo = build()
     .part("Color Demo:").space()
     .part("Teal").color(teal).bold().get();

 print::line(&demo, 0);


Using Gradients

Two-color gradient:

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:

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:

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:

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);


Output Styles

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:

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:

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:

use modcli::output::{
    progress::{
        show_progress_bar, 
    }
};

show_progress_bar("Testing", 45, 1500);

Displays

Label [#############################################] 100% Done!

Manual control:

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):

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):

use modcli::output::{
    progress::{
        show_spinner
    }
};
show_spinner("Loading", 20, 100);

Animated Percentage Loader:

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

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::Flex, TableStyle::Heavy);

Outputs

┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
┃Name    ┃Age     ┃Role    ┃
┣━━━━━━━━╋━━━━━━━━╋━━━━━━━━┫
┃Alice   ┃29      ┃Engineer┃
┃Bob     ┃35      ┃Manager ┃
┃Charlie ┃41      ┃CTO     ┃
┗━━━━━━━━┻━━━━━━━━┻━━━━━━━━┛

Table Example: Fixed Width, Rounded Borders

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

╭───────────────┬───────────────┬───────────────╮
│Name           │Age            │Role           │
├───────────────┼───────────────┼───────────────┤
│Alice          │29             │Engineer       │
│Bob            │35             │Manager        │
│Charlie        │41             │CTO            │
╰───────────────┴───────────────┴───────────────╯

Table Example: Fixed Width, Ascii Borders

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

+---------------+---------------+---------------+
|Name           |Age            |Role           |
+---------------+---------------+---------------+
|Alice          |29             |Engineer       |
|Bob            |35             |Manager        |
|Charlie        |41             |CTO            |
+---------------+---------------+---------------+

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

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.

mod commands;
use modcli::ModCli;
use commands::greet::GreetCommand;

fn main() {
    let args: Vec<String> = std::env::args().skip(1).collect();
    let mut cli = ModCli::new();

    // Register function
    cli.registry.register(Box::new(GreetCommand));


    cli.run(args);
}

Test Command

$ myCLI greet
Greetings!

$ myCLI help
List of available commands...

ModCLI supports an interactive console mode (like a REPL):

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:

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)

{
  "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)

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.