Skip to content

ddn.util.docopt

The ddn.util.docopt module provides an idiomatic D implementation of the docopt command-line interface description language parser. It allows you to define your program's CLI by simply writing a help message.

Overview

Docopt is a language for describing command-line interfaces. Instead of writing code to parse arguments, you write a help message that describes your interface, and docopt parses the arguments accordingly. This approach ensures your help text and argument parsing are always in sync.

This module is based on the Python docopt package and provides full support for commands, options, arguments, and complex usage patterns.

Key Features

  • Help Message as Interface Definition: Define your CLI by writing a human-readable help message
  • CTFE Pre-Parsing: Parse the docstring at compile time for zero runtime overhead
  • Short and Long Options: Support for -v, --verbose, and combined forms like -v --verbose
  • Option Arguments: Options with values like --speed=<kn> or -s <kn>
  • Default Values: Specify defaults in the options section with [default: value]
  • Environment Variable Defaults: Use [default: $VAR or fallback] syntax
  • Commands and Subcommands: Support for command-based interfaces like git commit
  • Positional Arguments: Required arguments like <file> and optional ones like [<file>]
  • Repeating Elements: Support for ... to indicate repeating arguments
  • Mutually Exclusive Groups: Use (a | b) for alternatives
  • Optional Groups: Use [options] for optional elements

Basic Usage

import ddn.util.docopt;
import std.stdio;

enum DOC = `
Naval Fate.

Usage:
  naval_fate ship new <name>...
  naval_fate ship <name> move <x> <y> [--speed=<kn>]
  naval_fate ship shoot <x> <y>
  naval_fate mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate (-h | --help)
  naval_fate --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.
`;

void main(string[] args) {
    auto arguments = docopt(DOC, args[1..$], true, "Naval Fate 2.0");

    if (arguments["ship"].isTrue) {
        if (arguments["new"].isTrue) {
            writeln("Creating new ship(s): ", arguments["<name>"]);
        } else if (arguments["move"].isTrue) {
            writefln("Moving %s to (%s, %s) at %s knots",
                arguments["<name>"], arguments["<x>"], arguments["<y>"],
                arguments["--speed"]);
        }
    }
}

CTFE Pre-Parsing

For better performance, you can pre-parse the docstring at compile time:

import ddn.util.docopt;

enum DOC = `
Usage:
  prog greet <name> [--times=<n>] [--shout]
  prog sum <nums>...
  prog (-h | --help)

Options:
  -h --help        Show this help.
  -t --times=<n>   Repeat times [default: 1]
  -s --shout       Uppercase output.
`;

// Pre-parse at compile time
enum PRE = parseDocopt!DOC;

void main(string[] args) {
    // Use pre-parsed descriptor at runtime
    auto arguments = docopt(PRE, args[1..$], true, "1.0.0", false);

    if (arguments["greet"].isTrue) {
        auto name = arguments["<name>"].asString;
        auto times = arguments["--times"].as!long;
        // ...
    }
}

Environment Variable Defaults

You can specify environment variables as default values:

enum DOC = `
Usage:
  prog [--config=<file>]

Options:
  --config=<file>  Config file [default: $CONFIG_FILE or config.json]
`;

If CONFIG_FILE environment variable is set, its value is used; otherwise, config.json is the default.

Working with Parsed Arguments

The docopt function returns an ArgMap containing all parsed values:

auto args = docopt(DOC, argv);

// Check if a flag/command is set
if (args["--verbose"].isTrue) { ... }

// Get string value
string filename = args["<file>"].asString;

// Get typed value
long count = args["--count"].as!long;

// Get array of values (for repeating arguments)
const(string[]) files = args["<files>"].as!(const(string[]));

// Check if value is null/not provided
if (args["--optional"].isNull) { ... }

Exception Handling

The module provides three exception types:

  • DocoptLanguageError: Thrown when the docstring itself is malformed
  • DocoptArgumentError: Thrown when user provides invalid arguments
  • DocoptExitHelp: Thrown when --help or --version is requested
try {
    auto args = docopt(DOC, argv, true, "1.0.0");
} catch (DocoptExitHelp e) {
    writeln(e.msg);  // Print help or version
} catch (DocoptArgumentError e) {
    writeln("Error: ", e.msg);
}

Usage Pattern Syntax

Pattern Description
<argument> Positional argument
--option Long option (flag)
-o Short option
--option=<value> Option with required value
[optional] Optional element
(required) Required element (grouping)
element... Repeating element
a \| b Mutually exclusive alternatives
[options] All defined options
command Command/subcommand

When to Use

Use this module when you need to:

  • Build command-line applications with complex argument patterns
  • Keep help text and argument parsing synchronized
  • Support subcommands, options with defaults, and repeating arguments
  • Provide user-friendly error messages for invalid input