Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

modpm/cmd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

71 Commits

Repository files navigation

cmd

Documentation GitHub CI Version Licence Score Downloads

A simple, intuitive library for building CLI applications in D.

API Reference Documentation

Features

  • Nested subcommands — Supports hierarchical command structures, allowing commands to contain other commands.
  • Flexible arguments — Required (<>) and optional ([]) arguments with variadic support (...).
  • Rich option handling — Supports --option=value, --option value, and grouped short flags such as -abc (equivalent to -a -b -c).
  • Automatic help generation — Built-in help and usage text generation.
  • Array collection — Repeated options are automatically collected as arrays.
  • Type-safe parsing — Clean API for accessing parsed arguments and options.

Quick Start

Here’s a minimal example that splits a string:

import std.stdio;
import std.string;
import cmd.program;
void main(string[] argv)
{
 auto args = new Program("split")
 .description("Split a string")
 .versionString("1.0.0")
 .versionOption("--version", "Show version information")
 .helpOption("-h, --help", "Show help for command")
 .argument("<string>", "String to split")
 .option("-s, --separator <char>", "Separator character")
 .option("--first", "Return only the first element")
 .parse(argv);
 auto parts = args.argument("string").split(args.option("separator"));
 if (args.hasFlag("first"))
 writeln(parts[0]);
 else
 writeln(parts);
}

Usage

$ ./split "hello,world,foo" --separator ","
["hello", "world", "foo"]
$ ./split "hello,world,foo" --separator "," --first
hello

Note: versionOption() enables a version flag for your program (e.g., --version), and helpOption() enables a help flag (e.g., -h or --help). When specified, these flags are handled automatically: the library will print the version or help message and exit.

Options and Flags

Use option() to add named parameters to your commands:

Option Types

ParsedArgs args = new Program("example")
 // Required option - user must provide a value
 .option("-t, --target <name>", "Target name")
 // Optional with default value
 .option("-p, --port [number]", "Port number", "8080")
 // Optional without default
 .option("-c, --config [file]", "Configuration file")
 // Boolean flag - without a parameter
 .option("--verbose", "Enable verbose output")
 .option("-q, --quiet", "Suppress output")
 .parse(argv);

Accessing Values

Please see the API reference page for ParsedArgs.

// Get option value (first occurrence)
const string target = args.option("target");
const string port = args.option("port");
// Get all values for repeated options
// const string[] targets = args.optionList("target");
// Check if optional option is present
if (args.hasOption("--config")) {
 const string config = args.option("--config");
 // ...
}
// Check boolean flags
if (args.hasFlag("verbose"))
 writeln("Verbose mode enabled");
const bool quiet = args.hasFlag("-q");

You can access options using:

  • option("target") - access by long name (explicit for long names)
  • option("t") - access by short name (implicit, could be ambiguous)
  • option("-t") - access by short name (explicit)
  • option("--target") - access by long name (explicit, stylistic)

(also applies for hasOption(), hasFlag(), etc.)

Arguments

Use argument() to add positional arguments that users provide in order:

new Program("file-processor")
 .argument("<input>", "Input file path") // Required
 .argument("[output]", "Output file path") // Optional

Variadic Arguments

Variadic (rest) arguments can be added as the last argument in your command to accept multiple values. Optional variadic [files...] accepts ≥ 0 values, required variadic <files...> needs ≥ 1.

new Program("search")
 // ...
 .argument("<files...>", "Files to search")

Accessing Arguments

const string inputFile = args.argument("input");
// Check if optional argument was provided
if (args.hasArgument("output")) {
 const string outputFile = args.argument("output");
}
// Get all values of variadic
const string[] files = args.argumentList("files");

Note: Required arguments after optional ones are technically allowed, but behave counter-intuitively and ambiguously for the user. It is strongly recommended not to use optional positional arguments before required ones—consider re-ordering the arguments, or using named parameters (options).

Working with Subcommands

Build complex CLI applications with nested subcommands using command(). Here’s an example with basic subcommands.

import std.stdio;
import std.string;
import cmd.program;
import cmd.command;
void main(string[] args)
{
 new Program("subcommands")
 .description("A CLI application with subcommands")
 .versionString("1.0.0")
 .helpOption("-h, --help", "Show help for command")
 .command(new Command("split")
 .description("Split a string")
 .argument("<string>", "String to split")
 .option("-s, --separator <char>", "Separator character")
 .action((args) {
 auto parts = args.argument("string").split(args.option("separator"));
 writeln(parts);
 return 0;
 })
 )
 .command(new Command("greet")
 .description("Greet someone")
 .argument("<name>", "Name of the person to greet")
 .option("--excited", "Add excitement to the greeting")
 .action((args) {
 auto msg = "Hello, " ~ args.argument("name");
 if (args.hasFlag("excited"))
 msg ~= "!!!";
 writeln(msg);
 return 0;
 })
 )
 .run(args);
}

Usage

$ ./subcommands split "hello,world" --separator ","
$ ./subcommands greet Alice --excited
$ ./subcommands split --help # Shows help for the split subcommand

Important: A command cannot have both subcommands and arguments. Choose one pattern per command level.

Commands can be nested endlessly.

Custom Command Classes

Create modular command implementations by extending Command (or Program):

module modular.commands.greet;
import std.stdio;
import cmd.command;
import cmd.parsed_args;
class GreetCommand : Command
{
 public this()
 {
 super("greet")
 .description("Greet someone")
 .argument("<name>", "Name of the person to greet")
 .option("--excited", "Add excitement to the greeting")
 .action(&execute);
 }
 private int execute(ParsedArgs args)
 {
 auto msg = "Hello, " ~ args.argument("name");
 if (args.hasFlag("excited"))
 msg ~= "!!!";
 writeln(msg);
 return 0;
 }
}

Integration

// ...
import modular.commands.greet;
import cmd.program;
void main(string[] args)
{
 new Program("modular")
 .command(new GreetCommand())
 .run(args);
}

Built-in Help Command

Enable help as a subcommand using HelpCommand:

// ...
import cmd.help_command;
new Program("myapp")
 .description("My CLI application")
 .command(new Command("deploy").description("Deploy the application"))
 .command(new Command("status").description("Check application status"))
 .command(new HelpCommand()) // Adds the built-in ‘help’ subcommand
 .run(args);

Usage

$ myapp help # Show help for program
$ myapp help deploy # Show help for deploy command

Error Handling

The library provides clear error messages for common mistakes during args parsing and exits with status code 2:

error: unknown command 'unknown-command'
error: missing value for option 'target'
error: unknown option 'invalid-option'
error: unexpected argument 'extra'
error: missing required option '-t, --target <name>'
error: missing required argument 'input'

You can also trigger custom errors in your command actions using the error(string) and noreturn error(string, int) methods on Command:

.action((args) {
 if (someCondition) {
 args.command.error("error message"); // Does not exit (only prints error)
 // or
 args.command.error("error message", 1); // Exits with status
 }
 return 0;
});

The error methods can be overridden to customise the error format.

Licence

Copyright © 2025 Zefir Kirilov.

This project is licenced under the GPL-3.0 licence.

AltStyle によって変換されたページ (->オリジナル) /