#framework #terminal-command #cli-framework #cli #command-line-tool #terminal

bin+lib mod-cli

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

6 releases (breaking)

new 0.5.0 May 1, 2025
0.4.0 Apr 30, 2025
0.3.8 Apr 30, 2025
0.2.1 Apr 10, 2025
0.1.0 Apr 10, 2025

#302 in Asynchronous

Download history 297/week @ 2025-04-05 77/week @ 2025-04-12 11/week @ 2025-04-19 287/week @ 2025-04-26

672 downloads per month

Apache-2.0

82KB
1.5K SLoC

Triple Hexagon

ModCLI
RUST CLI FRAMEWORK

Crates.io   Crates.io Downloads   docs.rs   GitHub last commit

ModCLI is a lightweight, modular CLI framework for Rust. A fully customizable and feature-rich system for registering commands, styling output, and running interactive shells with zero bloat. Built to speed up CLI development and get powerful tools running fast—without rewriting the basics.

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:

    [dependencies]
    mod-cli = "0.5.0"
    

    Add the library to your Cargo.toml with features:

    [dependencies]
    mod-cli = { version = "0.5.0", features = ["plugins"] }
    

    Feature Flags

    Feature Description
    internal-commands Enables built-in test/dev commands like ping, hello
    custom-commands Enables CLI custom command creation.
    json-loader Enables external command loading from JSON config
    plugins Enables plugin support for dynamic runtime command injection


    Usage

    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::output::input::console::run_interactive_console;
    
    
     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            |
    +---------------+---------------+---------------+
    



    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

    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...
    



    Interactive Shell

    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.



    📌 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.

    Dependencies

    ~6–16MB
    ~180K SLoC