Install seahorse via Cargo
masterTo use seahorse in your Rust project, add it to your Cargo.toml dependencies file.
[dependencies]
seahorse = "2.2"repository·master·Indexed 43 days ago
https://github.com/ksk001100/seahorseA minimal CLI framework written in Rust (v2.2.0) for building command-line applications with typed flags, sub-commands, and structured error handling. It provides a builder-style interface via the App and Command structs to define application metadata, nested command hierarchies, and actions using closures.
To use seahorse in your Rust project, add it to your Cargo.toml dependencies file.
[dependencies]
seahorse = "2.2"The Context struct is passed to Action closures and serves as the primary interface for retrieving parsed command-line data. It separates positional arguments from flags.
args: A Vec<String> containing the remaining positional arguments after flags and their values have been removed.flags: Internal storage for parsed flag names and their corresponding FlagValue or FlagError.In seahorse, commands are organized in a tree structure. A top-level App contains Command objects. Each Command can optionally contain a list of subcommands.
When a command is executed, the system attempts to match the provided arguments against registered subcommands first. If a match is found (either by name or by an alias), the execution descends into that subcommand. If no subcommand matches, the command's own action or action_with_result is executed. This hierarchy allows for both leaf-node actions and branching command structures.
// Example of a hierarchical structure
let sub = Command::new("list").action(|_| println!("listing..."));
let parent = Command::new("db").command(sub);
// Resulting CLI usage: `app db list`The seahorse::App acts as the central orchestrator for a CLI. The execution flow follows this hierarchy:
run_with_result normalizes the provided Vec<String>.Command (by name or alias).action is executed.action or action_with_result.-h or --help is present in the arguments, the application automatically displays its help text instead of executing the action.This structure allows you to build complex CLIs with nested subcommands or simple single-purpose tools using just the top-level action.
A Command can perform an operation using either an Action or an ActionWithResult.
action: A closure that takes a &Context and returns nothing (()).action_with_result: A closure that takes a &Context and returns a Result<(), Box<dyn Error>>.Note: You cannot set both. Attempting to do so will cause a panic.
use seahorse::{Command, Context, Action, ActionWithResult};
// Using .action()
let cmd_action = Command::new("greet")
.action(|c: &Context| println!("Hello!"));
// Using .action_with_result()
let cmd_result = Command::new("save")
.action_with_result(|c: &Context| {
// Perform logic that might fail
Ok(())
});The App struct provides a builder-style interface to set metadata for your CLI. Use the following methods:
.author(author: T): Sets the application author..description(description: T): Sets the application description..usage(usage: T): Sets the application usage string..version(version: T): Sets the application version.All these methods accept types that implement Into<String> and return Self to allow chaining.
use seahorse::App;
let app = App::new("cli")
.author("John Doe")
.description("A useful tool")
.usage("cli [command] [args]")
.version("1.0.0");Add subcommands to your application using the .command(command: Command) method.
Note: You cannot register two commands with the same name; doing so will cause the application to panic at runtime.
use seahorse::{App, Command};
let command = Command::new("hello")
.usage("cli hello [arg]")
.action(|c| println!("{:?}", c.args));
let app = App::new("cli")
.command(command);Once a Flag is defined, you can use it to parse a slice of strings (typically std::env::args()):
option_index(&[String]): Returns the index of the flag in the argument vector. It checks both the primary --name and any configured aliases.value(Option<String>): Parses the string value into a FlagValue enum based on the flag's FlagType.Returns Err(FlagError::ArgumentError) if a required value is missing (for non-Bool types) or Err(FlagError::ValueTypeError) if parsing fails.
use seahorse::{Flag, FlagType, FlagValue};
let flag = Flag::new("age", FlagType::Int).alias("a");
let args = vec!["cli".to_string(), "-a".to_string(), "25".to_string()];
if let Some(idx) = flag.option_index(&args) {
// Note: value() usually takes the string following the flag index
// In this implementation, you pass the actual value string
let val_str = args.get(idx + 1).cloned();
match flag.value(val_str) {
Ok(FlagValue::Int(v)) => println!("Age is: {}", v),
Err(e) => println!("Error: {:?}", e),
_ => {}
}
}Use int_flag to get the value of an integer flag as an isize. It returns Result<isize, FlagError>.
Use int_flag_vec to get all values for a repeated integer flag. It returns Vec<Result<isize, FlagError>>.
use seahorse::Context;
fn action(c: &Context) {
match c.int_flag("int") {
Ok(i) => println!("{}", i),
Err(e) => println!("{}", e),
}
}
// For repeated flags
fn action_repeated(c: &Context) {
for i in c.int_flag_vec("int") {
match i {
Ok(val) => println!("{}", val),
Err(e) => println!("{}", e),
}
}
}Use string_flag to get the value of a string flag. It returns Result<String, FlagError>.
Use string_flag_vec to get all values for a repeated string flag. It returns Vec<Result<String, FlagError>>.
use seahorse::Context;
fn action(c: &Context) {
match c.string_flag("string") {
Ok(s) => println!("{}", s),
Err(e) => println!("{}", e),
}
}
// For repeated flags
fn action_repeated(c: &Context) {
for s in c.string_flag_vec("string") {
match s {
Ok(val) => println!("{}", val),
Err(e) => println!("{}", e),
}
}
}You can attach flags to a command using the .flag() method. Flags are defined using the Flag struct and a FlagType (e.g., Bool, Int, Float, String).
use seahorse::{Command, Flag, FlagType};
let command = Command::new("config")
.flag(Flag::new("port", FlagType::Int))
.flag(Flag::new("debug", FlagType::Bool));Commands can act as containers for other commands using the .command() method. This allows you to build complex CLI structures like git remote add.
use seahorse::{Command, App};
let sub_command = Command::new("world")
.action(|_| println!("Hello world!"));
let parent_command = Command::new("hello")
.command(sub_command);
let app = App::new("my_cli")
.command(parent_command);