Skip to content

A fast, low-level, and configurable command-line processor

License

Notifications You must be signed in to change notification settings

chaseruskin/cliproc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

95 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cliproc

Pipeline License: MIT Crates.io

This library provides support for fast, low-level, and configurable command-line processing.

[dependencies]
cliproc = "2.1.1"

Example

use cliproc::{cli, proc, stage::Memory};
use cliproc::{Arg, Cli, Command, ExitCode, Help};
use std::env;

// 1. Define the struct and the data required to perform its task
struct Demo {
    name: String,
    count: Option<u8>,
}

// 2. Implement the `Command` trait to allow a struct to function as a command
impl Command for Demo {
    // 2a. Map the command-line data to the struct's data
    fn interpret(cli: &mut Cli<Memory>) -> cli::Result<Self> {
        cli.help(Help::with(HELP))?;
        Ok(Demo {
            name: cli.require(Arg::option("name").switch('n'))?,
            count: cli.get(Arg::option("count").switch('c'))?,
        })
    }

    // 2b. Process the struct's data to perform its task
    fn execute(self) -> proc::Result {
        for _ in 0..self.count.unwrap_or(1) {
            println!("Hello {}!", self.name);
        }
        Ok(())
    }
}

// 3. Build the command-line processor and run the command
fn main() -> ExitCode {
    Cli::default().parse(env::args()).go::<Demo>()
}

const HELP: &str = "\
A fast, low-level, and configurable command-line processor.

Usage:
    demo [options] --name <name>
    
Options:
    --name, -n <name>       Name of the person to greet              
    --count, -c <count>     Number of times to greet (default: 1)
    --help, -h              Print this help information and exit
";

See the examples/ folder for more demonstrations.

Details

The command-line processor is divided into 3 stages: build, ready, and memory. It is implemented using the typestate pattern to enforce valid state operations and state transitions at compile-time.

  1. Build Stage: The build stage provides methods to configure the command-line processor. This stage uses the builder pattern to set options.

  2. Ready Stage: The ready stage provides methods to determine how to run the command-line processor.

  3. Memory Stage: The memory stage provides methods to handle requests for data that is available from the command-line. This stage is the final stage of the command-line processor.

Transitions

Parsing a set of arguments using parse(...) transitions the command-line processor from the build stage to the ready stage.

At the ready stage, the processor has two choices: go or save:

  • go() runs the processor to completion by transitioning to the memory stage and then handling calling the specified struct as a command with its implementation of the Command trait. This is the recommended choice for running the processor.

  • save() puts off command interpretations and execution by only transitioning the processor to the memory stage. This allows the programmer to expliticly handle calling the specified struct as a command.

Commands

The raw vector of strings received from the command-line is processed by translating the strings into tokens to be interpreted by a struct implementing the Command trait.

Any struct can function as a command/subcommand as along as:

  1. Each field's type in the struct implements the standard library's std::str::FromStr trait.
  2. The struct implements cliproc's Command (or Subcommand) trait.

Arguments

There are 4 supported types of arguments recognized by cliproc:

  • Flags: boolean conditions (ex: --verbose)
  • Options: arbitrary types for values specified with as a key/value pair (ex: --output <file>)
  • Positionals: arbitrary types for values specified based upon position in the argument list (ex: <name>)
  • Subcommands: arbitrary types for nesting commands that contain their own set of arguments (ex: <command>)

The command-line processor interprets arguments as they are provided. For this reason, there is a specific order in which a struct must handle its attributes according to which one of the argument types it requests data from.

Upon interpreting a command or subcommand, the argument discovery order must follow:

  1. Flags
  2. Options
  3. Positionals
  4. Subcommands

Failure to specify the struct initialization in this order is a programmer's error and will result in a panic!.

Features

The command-line processor has the ability to:

  • Accept long options for flags and options

    • --verbose, --output a.out
  • Accept switches (short options) for flags and options

    • -v, -o a.out
  • Accept positional arguments

    • main.rs
  • Nest commands within commands using subcommands

    • calc add 10 20, calc mult 3 9
  • Accept attached value placement

    • --output=a.out, -o=a.out
  • Aggregate switches

    • -v -f, -vf
  • Capture variable instances of a flag with an optional maximum limit

    • --verbose --verbose --verbose
  • Capture variable instances of an option (order-preserving) with an optional maximum limit

    • --num 2 --num 17 --num 5
  • Capture variable positional calls for a single argument (order-preserving):

    • 10 20 30
  • Aggregate switches and assign a value to the final switch:

    • -vfo=a.out
  • Prioritize flag for help information over any other parsing error

  • Enable/disable a help flag with custom text entry WYSIWYG

  • Retain parsing and argument handling knowledge to share arguments of a command with nested subcommands

  • Detect misspelled words using dynamic programming approach for sequence alignment algorithm with configurable threshold for similarity comparison

  • Verify there is no unused/unrecognized arguments before completing parsing

  • Preserve unprocessed arguments that follow an empty flag --

About

A fast, low-level, and configurable command-line processor

Topics

Resources

License

Stars

Watchers

Forks

Languages