Git Product home page Git Product logo

zig-args's Introduction

Zig Argument Parser

Simple-to-use argument parser with struct-based config

Features

  • Automatic option generation from a config struct
  • Familiar look & feel:
    • Everything after the first -- is assumed to be a positional argument
    • A single - is interpreted as a positional argument which can be used as the stdin/stdout file placeholder
    • Short options with no argument can be combined into a single argument: -dfe
    • Long options can use either --option=value or --option value syntax (use --option=-- if you need -- as a long option argument)
    • verbs (sub-commands), with verb specific options. Non-verb specific (global) options can come before or after the verb on the command line. Non-verb option arguments are processed before determining verb. (see demo_verb.zig)
  • Integrated support for primitive types:
    • All integer types (signed & unsigned)
    • Floating point types
    • Booleans (takes optional argument. If no argument given, the bool is set, otherwise, one of yes, true, y, no, false, n is interpreted)
    • Strings
    • Enumerations

Example

const options = argsParser.parseForCurrentProcess(struct {
    // This declares long options for double hyphen
    output: ?[]const u8 = null,
    @"with-offset": bool = false,
    @"with-hexdump": bool = false,
    @"intermix-source": bool = false,
    numberOfBytes: ?i32 = null,
    signed_number: ?i64 = null,
    unsigned_number: ?u64 = null,
    mode: enum { default, special, slow, fast } = .default,

    // This declares short-hand options for single hyphen
    pub const shorthands = .{
        .S = "intermix-source",
        .b = "with-hexdump",
        .O = "with-offset",
        .o = "output",
    };
}, argsAllocator, .print) catch return 1;
defer options.deinit();

std.debug.print("executable name: {?s}\n", .{options.executable_name});

std.debug.print("parsed options:\n", .{});
inline for (std.meta.fields(@TypeOf(options.options))) |fld| {
    std.debug.print("\t{s} = {any}\n", .{
        fld.name,
        @field(options.options, fld.name),
    });
}

std.debug.print("parsed positionals:\n", .{});
for (options.positionals) |arg| {
    std.debug.print("\t'{s}'\n", .{arg});
}

zig-args's People

Contributors

avdn avatar bobf avatar courajs avatar data-man avatar deecellar avatar guigui220d avatar heinthanth avatar infrandomness avatar jiacai2050 avatar leecannon avatar linusg avatar lordmzte avatar masterq32 avatar mattnite avatar ominitay avatar seanthegleaming avatar vrischmann avatar zickzackv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zig-args's Issues

Help helper

Implement means to allow the following to be used:

const CliOptions = struct {
  help: bool = true,
  output: ?[]const u8 = null,
  long: u32 = 0,
  pub const shorthands = .{
    .h = "help",
    .o = "output",
  };
  pub const meta = .{
    .summary = "[-h] [-o] [--long] <files>",
    .full_text = "This command prints the current time.",
    .option_docs = .{
      .help = "Prints this help.",
      .output = "Sets the output file.",
      .long = "Sets the number of lines."
    },
  }
};

// ...

if(cli.options.help) {
  try args_parser.printHelp(CliOptions, stdout);
  return 0;
}

where printHelp prints this:

current_exe_name [-h] [-o] [--long] <files>

This command prints the current time.

Options:
  -h, --help    Prints this help.
  -o, --output  Sets the output file.
      --long    Sets the number of lines

Compiler errors should be emitted when option_docs is not documentation all options.

summary, full_text and option_docs are optional fields of meta

Implement support for obligatory values

Hi, I came across a use case for this library where it'd be useful if it could parse mandatory values that would be located at the end of the command :

For example if one makes a program that is processing the content of a file, it would be useful to be able tell the user he has to pass a path in order for the program to work;

It would also be useful having that system to have the last passed element in the command considered a specific element, for instance a path, that could be passed without hyphens

`parseWithVerbForCurrentProcess` does not work in self-hosted

When using parseWithVerbForCurrentProcess we hit a compiler TODO:

$ zig build install -fno-stage1
/home/lee/src/zriscv/external/zig-args/args.zig:85:1: error: TODO (LLVM): implement const of pointer type '*(struct decl=Module.Decl.Index(4211))' (value.Value.Tag.u1_type)
fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterator: anytype, allocator: std.mem.Allocator, error_handling: ErrorHandling) !ParseArgsResult(Generic, MaybeVerb) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: zriscv...
error: The step exited with error code 1
error: the following build command failed with exit code 1:
/home/lee/src/zriscv/zig-cache/o/0c3f7ddebe856d7ecddec6a3fdc16368/build /home/lee/src/zig/build/zig /home/lee/src/zriscv /home/lee/src/zriscv/zig-cache /home/lee/.cache/zig install -fno-stage1 -fno-stage1

parseForCurrentProcess seems to work fine.

Print errors correctly

Unknown command line option: { 105, 110, 112, 117, 116, 32, 108, 111, 108 }
String slices are being printed as arrays of integers. I think the {s} is needed inside the formatting to make it a string.

Allow for shadowing a global flag

It would be useful to shadow a global help flag. It may be useful to shadow any global flag, but I've only needed -h,--help.

To print the app's help message:

app -h verb

And the verb help message with:

app verb -h

[Help] Helper functions

Hello;

I am trying to create some helper functions to avoid having multiple occurences of parseForCurrentProcess() in my code but I'm having some troubles;
I have made that code but I am unsure of what I should replace the "return;" to

pub fn parseArgs(comptime Spec: type) !args.ParseArgsResult(Spec) {
    var errorCollectorGPA = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = errorCollectorGPA.deinit();

    var collection = args.ErrorCollection.init(&errorCollectorGPA.allocator);
    defer _ = collection.deinit();
    
    const ops = args.parseForCurrentProcess(Spec, std.heap.page_allocator, args.ErrorHandling{ .collect = &collection }) catch {
        for (collection.errors()) |err| {
            try std.io.getStdOut().writer().print("{}\n", .{err});
        }
        return;
    };
    return ops;
}

The compiler returns me

error: expected type '.args.ParseArgsResult(struct:9:32)', found 'void'
        return;
        ^

core dump running demo_verb

You get a core-dump if you run the demo_verb example program and only provide a verb and the error message "Short command line options are not supported", doesn't make much sense in that context:

$ /demo_verb foo                                                                                   
Short command line options are not supported.
executable name: ./demo_verb
thread 152356 panic: attempt to use null value
/data1/DOWNLOAD/zig/zig-args/demo_verb.zig:39:25: 0x22f042 in main (demo_verb)
    switch (options.verb.?) {
                        ^
/opt/zig/zig-linux-x86_64-0.10.0-dev.580+53e6c719e/lib/std/start.zig:561:37: 0x228481 in std.start.callMain (demo_verb)
            const result = root.main() catch |err| {
                                    ^
/opt/zig/zig-linux-x86_64-0.10.0-dev.580+53e6c719e/lib/std/start.zig:495:12: 0x20905e in std.start.callMainWithArgs (demo_verb)
    return @call(.{ .modifier = .always_inline }, callMain, .{});
           ^
/opt/zig/zig-linux-x86_64-0.10.0-dev.580+53e6c719e/lib/std/start.zig:409:17: 0x2080f6 in std.start.posixCallMainAndExit (demo_verb)
    std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
                ^
/opt/zig/zig-linux-x86_64-0.10.0-dev.580+53e6c719e/lib/std/start.zig:322:5: 0x207f02 in std.start._start (demo_verb)
    @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
    ^
[1]    152356 abort (core dumped)  ./demo_verb foo

When providing an option, the core dump doesn't happen (but the error message still doesn't make much sense)

Add support for yes/no booleans

Some programs require "troolean" options that can have a default, but also be forced to true and false.

We can either implement it as a special case for ?bool or provide a custom type for this:

const Trinary = enum { unset, false, true };

var cli = parse(struct {
    @"emit-bin": ?bool = null, // --emit-bin and --no-emit-bin
    @"emit-bin": args.Trinary = .unset, // --emit-bin and --no-emit-bin
}, …);

Generate completions

Add shell completion support.

pub CompletionShell = enum {bash, fish, zsh};
pub fn writeCompletions(shell: CompletionShell, myoutstream: var) !void`

Bonus feature: add a utility method to add a completion subcommand

Compiling error with parseForCurrentProcess()

zig-args version

Commit 51a3a82

Steps to Reproduce

Try to build an example using parseForCurrentProcess().

Expected Behavior

Successful compilation.

Actual Behavior

Zig returns the following error message:

./src/args/args.zig:14:38: error: expected 2 argument(s), found 1
    const executable_name = args.next() orelse {
                                     ^
./src/main.zig:85:48: note: called from here
    const options = args.parseForCurrentProcess(struct {
                                               ^
./src/main.zig:83:21: note: called from here
pub fn main() !void {
                    ^
/usr/lib/zig/std/process.zig:477:9: note: declared here
    pub fn next(self: *ArgIterator, allocator: Allocator) ?(NextError![:0]u8) {
        ^

Best regards.

support for passing memory sizes

Like --buffer_size=5k --file_size=20G
It's often useful to do that, instead of writing --file_size=50000000, many utilities have command line arguments where you pass a size.

support for slice and/or array types - multiple values on one option

not sure how to word this one;
it would be cool to have support for slice and/or array types for multiple values on one option

const options = try argsParser.parse(struct {
    outputs: ?[][]const u8 = null, // slice, "--outputs output1 output2 etc."
    resolution: ?[2]u32 = null, // array, "--resolution 1920 1080"

    pub const shorthands = .{
        .r = "resolution",
        .o = "outputs",
    };
}, &args, argsAllocator);

im not sure how difficult this would be to implement though ^_^;

Shorthands for verbs

It would be very useful if we could support shorthands for verbs similar to how we support them for arguments:

const Verb = union(enum) {
    test: Test,

    pub const shorthands = .{
        .t = "test",
    };
};

Add support for enum arguments

const EmissionType = enum { @"asm", @"bin", @"llvm-ir" };

const options = try argsParser.parse(struct {
    emit: EmissionType = .asm,
}, &args, argsAllocator);

Can we make Error Kind public?

Hello,

I'm make own error handler for custom error messages. Can we make Error Kind type public, so that we can "Switch" them!
I'm currently using this repo as submodule.

Screenshot 2023-02-13 at 8 48 59 AM

Add support for counting options

Some options like --verbose or -v can often be specified multiple times. This behaviour can be implemented by a predefined type from args:

var cli = parse(struct {
    verbose: args.Counter = .{}, // starts at 0
}, …);
std.debug.print("{}\n", .{cli.options.verbose.count });

Compilation error: found '?std.process.NextError![:0]u8'

Hi, I'm using Zig 0.9.0 (linux binary release)

std.process.ArgIterator.next probably changed recently. Now requiring an allocator and returning optional error or allocated string.

using ``

./.zigmod/deps/git/github.com/MasterQ32/zig-args/args.zig:42:43: error: expected error union type, found '?std.process.NextError![:0]u8'
    const executable_name = (try args.next(allocator)) orelse {

Every call to next is broken.

Mutually exclusive options

Since #3 is already using inner structs to express subcommands, I was thinking of something like:

const expect = @import("std").testing.expect;
const ap = @import("args.zig");

_ = try ap.parse(struct {
    // Some restrictions for this argument:
    //
    // * On the application help (if it gets implemented), the name `case_sensitivity` shouldn't be seen - only its
    // inner options, maybe grouped under some kind of section.
    //
    // * Shorthands for this argument cannot be declared on the `shorthands` constant of this struct and instead
    // should be declared on this mutually exclusive group's type (CaseSensitivity).
    //
    // Also, this syntax looks a little bit flicky; I'm not sure if it is the best way to do this.
    case_sensitivity: ap.MutuallyExclusive(CaseSensitivity) = ap.MutuallyExclusive(CaseSensitivity){ .value = .@"case-sensitive" },

    pub const shorthands = .{
        .s = "case_sensitivity", // as said, this doesn't make sense and shouldn't work.
    };
}, &args, &allocator);

// The type could be either a tagged union (`union(enum)`) - if any of the options accepted args - or an enum - if
// no options accepted args. - In this case, I'm using an enum because `case-sensitive` and `case-insensitive` just
// need to be enabled, and don't need any extra configuration.
const CaseSensitivity = enum {
    @"case-sensitive",
    @"case-insensitive",

    // We can declare the shorthands for this mutually exclusive group here. They may not be repeated on the parent
    // type or by other mutually exclusive groups.
    pub const shorthands = .{
        .S = "case-sensitive",
        .I = "case-insensitive",
    };
};

Implement support for subcommands

Using a tagged union instead of a struct for implementing sub commands:

const options = try argsParser.parse(union(enum) {
    fibonacci: struct {
        limit: usize,
    },
    primes: struct {
        limit: usize,
        algorithm: enum { naive, sieve } = .naive,
    },
}, &args, argsAllocator);

This would allow cmd fibonacci --limit and cmd primes --algorithm sieve

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.