Skip to content

CLI Flags (std.flags)

The std.flags module provides ergonomic, type-safe command-line argument parsing with automatic help generation.

nic
import std.flags;

Quick Start

nic
import std.flags.parser(FlagParser);
import std.args(Args);

fn main(argc: i32, argv: **char) -> i32 {
    let args = Args.from_main(argc, argv);
    defer args.deinit();

    let parser = FlagParser.for_program("myapp", "A sample application");
    defer release parser;

    parser
        .flag_bool("verbose", "v", "Enable verbose output")
        .flag_i32("count", "c", "Number of iterations", 1)
        .flag_string("output", "o", "Output file", "out.txt")
        .positional_required("input", "Input file");

    match parser.parse(args) {
        Ok(flags) => {
            let verbose = flags.get_bool("verbose");
            let count = flags.get_i32("count");
            let output = flags.get_string("output");

            match flags.get_positional(0) {
                Some(input) => {
                    if verbose {
                        printf("Processing %s -> %s (%d times)\n",
                            input.as_str(), output.as_str(), count);
                    }
                },
                None => {},
            }

            release flags;
        },
        Err(e) => {
            printf("Error: %s\n", e.as_str());
            parser.print_help();
            release e;
            return 1;
        }
    }

    return 0;
}

Creating a Parser

FlagParser.for_program

Create a new parser with program name and description:

nic
let parser = FlagParser.for_program("mytool", "A helpful description");
defer release parser;

Defining Flags

All flag methods return *Self for method chaining.

Boolean Flags

nic
parser.flag_bool("verbose", "v", "Enable verbose output");
parser.flag_bool("debug", "d", "Enable debug mode");
parser.flag_bool("help", "h", "Show help message");

Usage: -v, --verbose, -vd (combined)

Integer Flags

nic
// i32 with default value
parser.flag_i32("count", "c", "Number of items", 10);

// i64 with default value
parser.flag_i64("size", "s", "Size in bytes", 1024);

Usage: -c 5, --count 5, --count=5

String Flags

nic
parser.flag_string("output", "o", "Output file path", "output.txt");
parser.flag_string("format", "f", "Output format", "json");

Usage: -o file.txt, --output file.txt, --output=file.txt

String List Flags

Flags that can be specified multiple times:

nic
parser.string_list("include", "I", "Include paths");

Usage: -I./src -I./lib, --include ./src --include ./lib

Positional Arguments

Optional Positional

nic
parser.positional("config", "Configuration file");

Required Positional

nic
parser.positional_required("input", "Input file to process");

Multiple positionals are collected in order:

nic
parser
    .positional_required("source", "Source file")
    .positional_required("dest", "Destination file")
    .positional("extra", "Optional extra file");

Subcommands

Create git-style subcommands:

nic
let parser = FlagParser.for_program("git", "Version control");

// Define 'clone' subcommand
let clone = parser.subcommand("clone", "Clone a repository");
clone
    .flag_string("branch", "b", "Branch to clone", "main")
    .flag_bool("shallow", "", "Shallow clone")
    .positional_required("url", "Repository URL");

// Define 'commit' subcommand
let commit = parser.subcommand("commit", "Record changes");
commit
    .flag_string("message", "m", "Commit message", "")
    .flag_bool("all", "a", "Commit all changed files");

match parser.parse(args) {
    Ok(flags) => {
        match flags.get_subcommand() {
            Some(cmd) => {
                if cmd.equals("clone") {
                    let url = flags.get_positional(0);
                    let branch = flags.get_string("branch");
                    // Handle clone...
                } else if cmd.equals("commit") {
                    let message = flags.get_string("message");
                    // Handle commit...
                }
            },
            None => {
                parser.print_help();
            },
        }
        release flags;
    },
    Err(e) => {
        printf("Error: %s\n", e.as_str());
        release e;
    }
}

Parsing Results

ParsedFlags Methods

nic
// Boolean flags (default: false)
let verbose = flags.get_bool("verbose");

// Integer flags
let count = flags.get_i32("count");
let size = flags.get_i64("size");

// String flags
let output = flags.get_string("output");  // Returns *String

// String lists
let includes = flags.get_string_list("include");  // Returns *Vec[*String]

// Positional arguments
match flags.get_positional(0) {
    Some(arg) => println("First arg: " + arg.as_str()),
    None => println("No positional args"),
}

let positional_count = flags.positional_count();

// Subcommand (if any)
match flags.get_subcommand() {
    Some(cmd) => println("Subcommand: " + cmd.as_str()),
    None => println("No subcommand"),
}

Help Generation

Automatic Help

Call print_help() to display formatted help:

nic
parser.print_help();

Output:

myapp - A sample application

USAGE:
    myapp [OPTIONS] <input>

ARGUMENTS:
    <input>          Input file (required)

OPTIONS:
    -v, --verbose    Enable verbose output
    -c, --count <num>    Number of iterations
    -o, --output <value>    Output file

Custom Help Flag

nic
if args.contains("-h") || args.contains("--help") {
    parser.print_help();
    return 0;
}

Flag Formats

The parser supports multiple formats:

FormatExampleDescription
Short-vSingle character flag
Long--verboseFull name flag
Combined short-vvvMultiple short flags
Value (space)-c 5Value after space
Value (equals)--count=5Value after equals
Value (attached)-c5Value immediately after short
End of flags--Treat rest as positionals

Complete Example

nic
import std.flags.parser(FlagParser);
import std.flags.result(ParsedFlags);
import std.args(Args);

fn main(argc: i32, argv: **char) -> i32 {
    let args = Args.from_main(argc, argv);
    defer args.deinit();

    let parser = FlagParser.for_program("compiler", "The Nic compiler");
    defer release parser;

    // Global options
    parser
        .flag_bool("verbose", "v", "Verbose output")
        .flag_bool("help", "h", "Show help")
        .flag_string("output", "o", "Output file", "a.out")
        .string_list("include", "I", "Include directories");

    // Build subcommand
    let build = parser.subcommand("build", "Build the project");
    build
        .flag_bool("release", "r", "Release build")
        .flag_i32("jobs", "j", "Parallel jobs", 4);

    // Run subcommand
    let run = parser.subcommand("run", "Build and run");
    run
        .positional("file", "File to run");

    // Parse
    match parser.parse(args) {
        Ok(flags) => {
            if flags.get_bool("help") {
                parser.print_help();
                release flags;
                return 0;
            }

            match flags.get_subcommand() {
                Some(cmd) => {
                    if cmd.equals("build") {
                        let release_build = flags.get_bool("release");
                        let jobs = flags.get_i32("jobs");
                        printf("Building with %d jobs (release=%d)\n",
                            jobs, release_build as i32);
                    } else if cmd.equals("run") {
                        match flags.get_positional(0) {
                            Some(file) => {
                                printf("Running %s\n", file.as_str());
                            },
                            None => {
                                println("No file specified");
                            },
                        }
                    }
                },
                None => {
                    parser.print_help();
                },
            }

            release flags;
        },
        Err(e) => {
            printf("Error: %s\n", e.as_str());
            parser.print_help();
            release e;
            return 1;
        }
    }

    return 0;
}

API Reference

FlagParser Class

nic
pub class FlagParser {
    /// Create a new parser for a program
    pub for_program(name: string, desc: string) -> *FlagParser;

    /// Add a boolean flag
    pub flag_bool(self, name: string, short: string, help: string) -> *Self;

    /// Add an i32 flag with default value
    pub flag_i32(self, name: string, short: string, help: string, default: i32) -> *Self;

    /// Add an i64 flag with default value
    pub flag_i64(self, name: string, short: string, help: string, default: i64) -> *Self;

    /// Add a string flag with default value
    pub flag_string(self, name: string, short: string, help: string, default: string) -> *Self;

    /// Add a string list flag (can be repeated)
    pub string_list(self, name: string, short: string, help: string) -> *Self;

    /// Add an optional positional argument
    pub positional(self, name: string, help: string) -> *Self;

    /// Add a required positional argument
    pub positional_required(self, name: string, help: string) -> *Self;

    /// Add a subcommand
    pub subcommand(self, name: string, desc: string) -> *FlagParser;

    /// Parse arguments
    pub parse(self, args: *Args) -> Result[*ParsedFlags, *String];

    /// Print help to stdout
    pub print_help(self) -> unit;

    /// Generate help text as a String
    pub help_string(self) -> *String;
}

ParsedFlags Class

nic
pub class ParsedFlags {
    /// Get a boolean flag value
    pub get_bool(self, name: string) -> bool;

    /// Get an i32 flag value
    pub get_i32(self, name: string) -> i32;

    /// Get an i64 flag value
    pub get_i64(self, name: string) -> i64;

    /// Get a string flag value
    pub get_string(self, name: string) -> *String;

    /// Get a string list flag value
    pub get_string_list(self, name: string) -> *Vec[*String];

    /// Get a positional argument by index
    pub get_positional(self, index: u64) -> Option[*String];

    /// Get number of positional arguments
    pub positional_count(self) -> u64;

    /// Get the subcommand name (if any)
    pub get_subcommand(self) -> Option[*String];
}

See Also

Released under the MIT License.