Home

Awesome

Argon

A processor for command-line arguments, an alternative to Getopt, written in D

#!/usr/bin/rdmd --shebang -unittest -g -debug -w

import argon;

import std.stdio;

// Imagine a program that creates widgets of some kind.

enum Colours {black, blue, green, cyan, red, magenta, yellow, white}

// Write a class that inherits from argon.Handler:

class MyHandler: argon.Handler {

    // Inside your class, define a set of data members.
    // Argon will copy user input into these variables.

    uint size;
    Colours colour;
    bool winged;
    uint nr_windows;
    string name;
    argon.Indicator got_name;

    // In your constructor, make a series of calls to Named(),
    // Pos() and (not shown here) Incremental().  These calls tell Argon
    // what kind of input to expect and where to deposit the input after
    // decoding and checking it.

    this() {
        // The first argument is positional (meaning that the user specifies
        // it just after the command name, with an --option-name), because we
        // called Pos().  It's mandatory, because the Pos() invocation doesn't
        // specify a default value or an indicator.  (Indicators are explained
        // below.)  The AddRange() call rejects user input that isn't between
        // 1 and 20, inclusive.
        Pos("size of the widget", size).AddRange(1, 20);

        // The second argument is also positional, but it's optional, because
        // we specified a default colour: by default, our program will create
        // a green widget.  The user specifies colours by their names ('black',
        // 'blue', etc.), or any unambiguous abbreviation.
        Pos("colour of the widget", colour, Colours.green);

        // The third argument is a Boolean option that is named, as all
        // Boolean arguments are.  That means a user who wants to override
        // the default has to specify it by typing "--winged", or some
        // unambiguous abbreviation of it.  We've also provided a -w shortcut.
        //
        // All Boolean arguments are optional.
        Named("winged", winged) ('w');

        // The fourth argument, the number of windows, is a named argument,
        // with a long name of --windows and a short name of -i, and it's
        // optional.  A user who doesn't specify a window count gets six
        // windows.  Our AddRange() call ensures that no widget has more
        // than twelve and, because we pass in a uint, Argon will reject
        // all negative numbers.  The string "number of windows" is called a
        // description, and helps Argon auto-generate a more helpful
        // syntax summary.
        Named("windows", nr_windows, 6) ('i') ("number of windows").AddRange(0, 12);

        // The user can specify a name for the new widget.  Since the user
        // could explicitly specify an empty name, our program uses an
        // indicator, got_name, to determine whether a name was specified or
        // not, rather than checking whether the name is empty.
        Named("name", name, got_name) ('n').LimitLength(0, 20);
    }

    // Now write a separate method that calls Parse() and does something with
    // the user's input.  If the input is valid, your class's data members will
    // be populated; otherwise, Argon will throw an exception.
    
    auto Run(string[] args) {
        try {
            Parse(args);
            writeln("Size:    ", size);
            writeln("Colour:  ", colour);
            writeln("Wings?   ", winged);
            writeln("Windows: ", nr_windows);
            if (got_name)
                writeln("Name:    ", name);

            return 0;
        }
        catch (argon.ParseException x) {
            stderr.writeln(x.msg);
            stderr.writeln(BuildSyntaxSummary);
            return 1;
        }
    }
}

int main(string[] args) {
    auto handler = new MyHandler;
    return handler.Run(args);
}

There's plenty more that Argon can do.

Features for you:

Features for your users:

Some of this functionality is available in std.getopt; some isn't. Stick with getopt if you need the following features, which are not currently implemented in Argon:

Future directions:

Argon parses Unix-style syntax, as in ls -lah --author foo*. If you wanted to support other syntaxes, such as MS-DOS-style dir foo.* /s /b, replacing struct Parser and a few parts of class Handler, along with all the unit tests that follow them, should get you most of the way. The rest of the code ought to be reusable in more or less its current form. The author has no plans to support MS-DOS-style syntax.