Hejsil / zig-clap

Command line argument parsing library
MIT License
939 stars 67 forks source link

Whether subcommands are supported ? #90

Closed velasco300 closed 1 week ago

Hejsil commented 1 year ago

This is not a feature this library has yet and I've not had much motivation to implement this feature myself. It is not really a feature I need this library to have for my projects, so I've had little motivation to come up with a good API and implement it.

Here are some examples of how people have implemented subcommands and then used zig-clap to parse the args for each command:

james-callahan commented 1 year ago

I'm trying to implement subcommands with different parsers, but it appears that you can only use parseParamsComptime once due to it hardcoding Param(Help) in the return type.

```zig const Subcommand = enum { client, server }; const parsers = comptime .{ .int = clap.parsers.int, .SUBCOMMAND = clap.parsers.enumeration(Subcommand), }; const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit \\--version Prints version information \\ Either "client" or "server" \\ ); const client_params = comptime clap.parseParamsComptime( \\--cid The remote endpoint CID \\--port The remote endpoint port \\ ); const server_params = comptime clap.parseParamsComptime( \\--port The local port to listen on \\ ); var diag = clap.Diagnostic{}; var res = clap.parse(clap.Help, ¶ms, parsers, .{ .diagnostic = &diag, }) catch |err| { diag.report(std.io.getStdErr().writer(), err) catch {}; show_usage(clap.Help, ¶ms) catch {}; return; }; defer res.deinit(); if (res.positionals.len != 1) { std.io.getStdErr().writer().print("Missing subcommand\n", .{}) catch {}; show_usage(clap.Help, ¶ms) catch {}; return; } if (res.args.help) { const stdout = std.io.getStdOut(); try clap.help(stdout.writer(), clap.Help, ¶ms, .{}); try stdout.writer().print("\nclient subcommand options:\n", .{}); try clap.help(stdout.writer(), clap.Help, &client_params, .{}); try stdout.writer().print("\nserver subcommand options:\n", .{}); try clap.help(stdout.writer(), clap.Help, &server_params, .{}); return; } if (res.args.version) { const stdout = std.io.getStdOut(); try stdout.writer().print("v0.0.0", .{}); return; } switch (res.positionals[0]) { .client => { var client_res = clap.parse(clap.Help, &client_params, parsers, .{ .diagnostic = &diag, }) catch |err| { diag.report(std.io.getStdErr().writer(), err) catch {}; show_usage(&client_params) catch {}; return; }; defer client_res.deinit(); }, .server => { unreachable; }, } ```

Could you add a parseParamsComptime variant where I can pass in an Id?

Hejsil commented 1 year ago

I've tried your code and it does compile fine (after changing .int = clap.parsers.int, to .int = clap.parsers.int(u8, 10),).

The problem now is when you do:

$ zig-out/bin/exe client
Invalid argument 'client'

What happens here, is that clap.parse with params succeeds and the program end up in the .client switch case. Then clap.parse on client_params fails because it does not expect a positional argument, and clap.parse looks at all arguments given to the program. I recommend having a look at the links above for examples of how people have done sub commands.

Another resource is the PR https://github.com/Hejsil/zig-clap/pull/93. There is an example for how you can use clap to find the first positional and parse args[0..first_pos + 1] with one parser and args[first_pos + 1..] with another.