clish 0.1.0-beta.3

Elegant CLI framework for Rust.
Documentation

clish

The most elegant CLI framework for Rust.

clish (/klɪʃ/ "KEL-ish")

Crates.io Docs.rs License Docs Rust edition Repository

use clish::prelude::*;

#[command]
fn deploy(target: Pos<String>, env: Named<String>, force: bool) {
    println!("Deploying {target} to {env} (force={force})");
}

fn main() {
    app!().run();
}

Inspired by Typer

Quick Start

[dependencies]
clish = "0.1.0-beta.3"
use clish::prelude::*;

#[command]
fn hello(name: Named<Option<String>>) {
    println!("Hello, {}!", name.unwrap_or("world"));
}

fn main() {
    app!().run();
}
cargo run -- hello --name Alice
# Hello, Alice!

Usage

app -h                      # Short help
app --help                  # Long help (includes details)
app --version               # Show version
app --verbose --version     # Version + platform info
app <cmd> --help            # Per-command help
app <cmd> --help            # Also works: app --help <cmd>
app <cmd> -- arg1 --arg2    # -- separator: everything after is positional

Argument Types

Type CLI Form
Pos<T> <arg> required positional
Pos<Option<T>> [arg] optional positional
Pos<Vec<T>> <arg>... variadic (zero or more)
Named<T> --name <val> required option
Named<Option<T>> [--name <val>] optional option
Named<Vec<T>> --name <val> --name <val> repeatable
bool --flag presence flag

Named options support --name=value, --name value, -n value, and -nvalue forms. Flags support bundling: -abc is equivalent to -a -b -c.

Command Options

#[command(
    help = "Deploy the app",
    details = "Long description shown on --help.",
    aliases = ["ship", "push"],
    hidden = false,
    deprecated = true,
    deprecation_note = "use 'ship' instead",
    param(host, short = 'h', help = "Target host", env = "HOST"),
    param(port, short = 'p', default = "8080"),
    param(level, choices = ["debug", "info"]),
    param(verbose, conflicts_with = ["quiet"]),
    param(output, requires = ["format"]),
)]
fn deploy(host: Pos<String>, port: Named<u16>, level: Named<String>, verbose: bool, quiet: bool, output: Named<Option<String>>, format: Named<Option<String>>) { ... }

Parameter Options

Key Type Description
help string Short description
details string Long description (shown only in --help)
name string Override the CLI flag name
short char Single-character alias (-d)
placeholder string Custom help token
hide bool Omit from help listings
default string Default value
env string Environment variable fallback
choices array Allowed values
conflicts_with array Mutual exclusion
requires array Prerequisites
value_hint string Shell completion hint (reserved)

Resolution order: CLI argument > $ENV_VAR > default > error.

Oneshot Mode

Pass a command function to app!() for single-command CLIs without subcommand dispatch:

use clish::prelude::*;

#[command]
fn greet(name: Pos<String>) {
    println!("Hello, {name}!");
}

fn main() {
    app!(greet).run();
}

Oneshot mode enforces that the command has no custom name, aliases, hidden, or deprecated attributes, and that no other commands are registered.

Styling

use clish::prelude::*;
use clish::help::{AppStyles, AppStyle};

app!()
    .styles(AppStyles {
        header: AppStyle::Markup("[bold cyan underline]"),
        command: AppStyle::Markup("[bold cyan]"),
        ..Default::default()
    })
    .run();

Or use anstyle::Style values directly:

use anstyle::{Style, AnsiColor};

app!()
    .styles(AppStyles {
        header: AppStyle::Anstyle(Style::new().fg_color(Some(AnsiColor::Cyan.into()))),
        ..Default::default()
    })
    .run();

Error Handling

Errors are printed to stderr with structured formatting:

error: unknown command 'deplyo'
  |
1 | myapp deplyo
  |        ^^^^^^
  |
  = hint: run 'myapp --help' for available commands

Command functions can return Result<(), String> for custom error handling.

Documentation

Full docs: https://razkar.codeberg.page/clish API documentation: https://docs.rs/clish

License

MIT or Apache-2.0

Cheers, RazkarStudio.

Copyright (c) 2026 RazkarStudio. All rights reserved.