Skip to content

ddn.util.docopt

This page provides runnable, everyday examples for parsing command lines with the ddn.util.docopt module.

All examples are designed to be copied into a single .d file and run via:

dub run --single example.d -- [args...]

1) Parse flags and arguments

This example shows basic parsing of positional arguments and options.

#!/usr/bin/env dub
/+ dub.sdl:
    name "docopt-basic"
    dependency "ddn" version="*"
+/
module examples.docopt_basic;

import std.stdio : writeln, writefln;
import ddn.util.docopt;

enum DOC = q{
Basic demo.

Usage:
  basic greet <name> [--shout]
  basic (-h | --help)

Options:
  -h --help    Show this help.
  --shout      Uppercase the greeting.
};

void main(string[] args) {
   // Drop program name.
   auto argv = args.length > 1 ? args[1 .. $] : cast(string[])[];

   try {
      auto m = docopt(DOC, argv);

      auto name = m["<name>"].asString;
      auto shout = m["--shout"].isTrue;

      auto msg = "Hello, " ~ name ~ "!";
      if (shout) {
         import std.string : toUpper;

         msg = msg.toUpper;
      }
      writeln(msg);
   } catch (DocoptExitHelp e) {
      // Triggered by -h/--help.
      writeln(e.msg);
   } catch (DocoptArgumentError e) {
      // Friendly user-facing parse errors.
      writefln("Error: %s", e.msg);
   }
}

Run:

dub run --single example.d -- greet Alice
dub run --single example.d -- greet Alice --shout
dub run --single example.d -- --help

2) Subcommands + defaults (including env var defaults)

This example demonstrates:

  • Multiple subcommands
  • A numeric option with a default
  • An option defaulted from an environment variable
#!/usr/bin/env dub
/+ dub.sdl:
    name "docopt-subcommands"
    dependency "ddn" version="*"
+/
module examples.docopt_subcommands;

import std.stdio : writeln, writefln;
import std.conv : to;
import ddn.util.docopt;

enum VERSION = "subcommands-demo 1.0.0";

enum DOC = q{
subcommands-demo.

Usage:
  tool greet <name> [--times=<n>] [--config=<file>]
  tool sum <n>...
  tool (--help | --version)

Options:
  --help               Show this help.
  --version            Show version.
  -t --times=<n>       Repeat times [default: 1]
  -c --config=<file>   Config file [default: $CONFIG_FILE or config.json]
};

void main(string[] args) {
   auto argv = args.length > 1 ? args[1 .. $] : cast(string[])[];

   try {
      auto m = docopt(DOC, argv, true, VERSION, false);

      if (m["greet"].isTrue) {
         const name = m["<name>"].asString;
         const times = m["--times"].as!long;
         const config = m["--config"].asString;

         foreach (_; 0 .. times)
            writeln("Hello, " ~ name ~ "!");
         writefln("(config: %s)", config);
      } else if (m["sum"].isTrue) {
         const(string[]) nums = m["<n>"].as!(const(string[]));
         long total = 0;
         foreach (s; nums)
            total += s.to!long;
         writefln("sum = %s", total);
      }
   } catch (DocoptExitHelp e) {
      writeln(e.msg);
   } catch (DocoptArgumentError e) {
      writefln("Error: %s", e.msg);
   }
}

Run:

dub run --single example.d -- greet Bob
CONFIG_FILE=custom.json dub run --single example.d -- greet Bob --times=2
dub run --single example.d -- sum 10 20 30
dub run --single example.d -- --version

3) CTFE pre-parsing of the doc string

If your tool parses the same doc string many times, you can pre-parse at compile time.

#!/usr/bin/env dub
/+ dub.sdl:
    name "docopt-ctfe"
    dependency "ddn" version="*"
+/
module examples.docopt_ctfe;

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

enum DOC = q{
ctfe-demo.

Usage:
  ctfe <name>
};

// Parse at compile-time.
enum PRE = parseDocopt!DOC;

void main(string[] args) {
   auto argv = args.length > 1 ? args[1 .. $] : cast(string[])[];
   auto m = docopt(PRE, argv);
   writeln("name = ", m["<name>"].asString);
}

Run:

dub run --single example.d -- Alice