├── .github
└── workflows
│ └── main.yaml
├── .gitignore
├── LICENSE
├── README.md
├── build.zig
├── build.zig.zon
├── docs
├── README_extended.md
├── cova_demo.gif
├── guides
│ ├── arg_types
│ │ ├── arg_types.md
│ │ ├── command.md
│ │ ├── option.md
│ │ └── value.md
│ ├── getting_started
│ │ ├── install.md
│ │ ├── naming_conventions.md
│ │ └── quick_setup.md
│ ├── overview.md
│ └── parsing_analysis
│ │ ├── aliases.md
│ │ ├── analysis.md
│ │ ├── arg_groups.md
│ │ ├── parsing.md
│ │ └── usage_help.md
├── index.html
├── main.js
├── main.wasm
└── sources.tar
├── examples
├── basic_app.zig
├── basic_app_meta
│ ├── arg_templates
│ │ ├── basic-app-template.json
│ │ └── basic-app-template.kdl
│ ├── help_docs
│ │ ├── manpages
│ │ │ ├── basic-app-clean.1
│ │ │ ├── basic-app-list-filter.1
│ │ │ ├── basic-app-list.1
│ │ │ ├── basic-app-new.1
│ │ │ ├── basic-app-open.1
│ │ │ ├── basic-app-view-lists.1
│ │ │ └── basic-app.1
│ │ └── markdown
│ │ │ ├── basic-app-clean.md
│ │ │ ├── basic-app-list-filter.md
│ │ │ ├── basic-app-list.md
│ │ │ ├── basic-app-new.md
│ │ │ ├── basic-app-open.md
│ │ │ ├── basic-app-view-lists.md
│ │ │ └── basic-app.md
│ └── tab_completions
│ │ ├── _basic-app-completion.zsh
│ │ ├── basic-app-completion.bash
│ │ └── basic-app-completion.ps1
├── cova_demo_meta
│ ├── arg_templates
│ │ ├── covademo-template.json
│ │ └── covademo-template.kdl
│ ├── help_docs
│ │ ├── manpages
│ │ │ ├── covademo-add-user.1
│ │ │ ├── covademo-basic.1
│ │ │ ├── covademo-fn-cmd.1
│ │ │ ├── covademo-nest-1-nest-2.1
│ │ │ ├── covademo-nest-1.1
│ │ │ ├── covademo-struct-cmd-inner-cmd.1
│ │ │ ├── covademo-struct-cmd.1
│ │ │ ├── covademo-sub-cmd.1
│ │ │ ├── covademo-union-cmd.1
│ │ │ └── covademo.1
│ │ └── markdown
│ │ │ ├── covademo-add-user.md
│ │ │ ├── covademo-basic.md
│ │ │ ├── covademo-fn-cmd.md
│ │ │ ├── covademo-nest-1-nest-2.md
│ │ │ ├── covademo-nest-1.md
│ │ │ ├── covademo-struct-cmd-inner-cmd.md
│ │ │ ├── covademo-struct-cmd.md
│ │ │ ├── covademo-sub-cmd.md
│ │ │ ├── covademo-union-cmd.md
│ │ │ └── covademo.md
│ └── tab_completions
│ │ ├── _covademo-completion.zsh
│ │ ├── covademo-completion.bash
│ │ └── covademo-completion.ps1
├── covademo.zig
├── example_structs.zig
├── logger.zig
└── logger_meta
│ ├── arg_templates
│ ├── logger-template.json
│ └── logger-template.kdl
│ ├── help_docs
│ ├── manpages
│ │ └── logger.1
│ └── markdown
│ │ └── logger.md
│ └── tab_completions
│ ├── _logger-completion.zsh
│ ├── logger-completion.bash
│ └── logger-completion.ps1
└── src
├── Command.zig
├── Option.zig
├── Value.zig
├── cova.zig
├── generate.zig
├── generate
├── arg_template.zig
├── help_docs.zig
└── tab_completion.zig
├── generator.zig
└── utils.zig
/.github/workflows/main.yaml:
--------------------------------------------------------------------------------
1 | name: docs
2 |
3 | on:
4 | push:
5 | branches:
6 | - "main"
7 | - "v0*"
8 | workflow_dispatch:
9 |
10 | permissions:
11 | contents: read
12 | pages: write
13 | id-token: write
14 |
15 | concurrency:
16 | group: "pages"
17 | cancel-in-progress: true
18 |
19 | jobs:
20 | deploy:
21 | environment:
22 | name: github-pages
23 | url: ${{ steps.deployment.outputs.page_url }}
24 | runs-on: ubuntu-latest
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v3
28 | - name: Setup Pages
29 | uses: actions/configure-pages@v2
30 | - uses: mlugg/setup-zig@v1
31 | with:
32 | version: 0.14.0
33 | - run: zig build docs
34 | - name: Upload artifact
35 | uses: actions/upload-pages-artifact@v3
36 | with:
37 | path: "docs"
38 | - name: Deploy to GitHub Pages
39 | id: deployment
40 | uses: actions/deploy-pages@v4
41 |
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | commit_msg
2 | .zig-*/**
3 | zig-*/**
4 | bin/**
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 00JCIV00
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Commands **⋅** Options **⋅** Values **⋅** Arguments
4 | A simple yet robust cross-platform command line argument parsing library for Zig.
5 |
6 | [-orange?logo=Zig&logoColor=Orange&label=Zig&labelColor=Orange)](https://ziglang.org/download/)
7 | [](https://github.com/00JCIV00/cova/releases/tag/v0.10.1-beta)
8 | [)](https://github.com/00JCIV00/cova/commits/main/)
9 | [](https://github.com/00JCIV00/cova/blob/main/LICENSE)
10 |
11 | ___
12 |
13 | ## Overview
14 | `command --option value`
15 |
16 | Cova is based on the idea that Arguments will fall into one of three types: Commands, Options, or Values. These Types are assembled into a single Command struct which is then used to parse argument tokens. Whether you're looking for simple argument parsing or want to create something as complex as the [`ip`](https://www.man7.org/linux/man-pages/man8/ip.8.html) or [`git`](https://www.man7.org/linux/man-pages/man1/git.1.html) commands, Cova makes it easy.
17 |
18 | ## Get Started Quickly!
19 | - [Quick Start Guide](https://github.com/00JCIV00/cova/wiki/Getting-Started)
20 | - [Full Wiki Guide](https://github.com/00JCIV00/cova/wiki/)
21 | - [API Docs](https://00jciv00.github.io/cova/)
22 |
23 | ### Quick Example
24 | [Logger Example](https://github.com/00JCIV00/cova/blob/main/examples/logger.zig)
25 | ```shell
26 | logger --log-level info
27 | ```
28 | #### Set up a Command
29 | ```zig
30 | // ...
31 | pub const CommandT = cova.Command.Custom(.{...});
32 | pub const setup_cmd: CommandT = .{
33 | .name = "logger",
34 | .description = "A small demo of using the Log Level Enum as an Option.",
35 | .examples = &.{ "logger --log-level info" },
36 | .opts = &.{
37 | .{
38 | .name = "log_level",
39 | .description = "An Option using the `log.Level` Enum.",
40 | .long_name = "log-level",
41 | .mandatory = true,
42 | .val = .ofType(log.Level, .{
43 | .name = "log_level_val",
44 | .description = " This Value will handle the Enum."
45 | })
46 | }
47 | },
48 | };
49 | ```
50 | #### Parse the Command
51 | ```zig
52 | pub fn main() !void {
53 | // ...
54 | var main_cmd = try setup_cmd.init(alloc, .{});
55 | defer main_cmd.deinit();
56 | var args_iter = try cova.ArgIteratorGeneric.init(alloc);
57 | defer args_iter.deinit();
58 |
59 | cova.parseArgs(&args_iter, CommandT, main_cmd, stdout, .{}) catch |err| switch (err) {
60 | error.UsageHelpCalled => {},
61 | else => return err,
62 | };
63 | // ...
64 | ```
65 | #### Use the Data
66 | ```zig
67 | // ...
68 | const main_opts = try main_cmd.getOpts(.{});
69 | const log_lvl_opt = main_opts.get("log_level").?;
70 | const log_lvl = log_lvl_opt.val.getAs(log.Level) catch {
71 | log.err("The provided Log Level was invalid.", .{});
72 | return;
73 | };
74 | log.info("Provided Log Level: {s}", .{ @tagName(log_lvl) });
75 | }
76 | ```
77 |
78 | ## Features
79 | - **[Comptime Setup](#comptime-setup). [Runtime Use](#runtime-use).**
80 | - Cova is designed to have Argument Types set up at ***compile time*** so they can be validated during each compilation, thus providing you with immediate feedback.
81 | - Once validated, Argument Types are initialized to memory for ***runtime*** use where end user argument tokens are parsed then made ready to be analyzed by your code.
82 | - **[Build-time Bonuses!](#build-time-bonuses)** Cova also provides a simple build step to generate Help Docs, Tab Completion Scripts, and Argument Templates at ***build-time***!
83 | - **Simple Design:**
84 | - All argument tokens are parsed to Argument Types: Commands, Options, or Values.
85 | - Options = _Flags_ and Values = _Positional Arguments_
86 | - These Argument Types can be *created from* or *converted to* your Structs, Unions, and Functions along with their corresponding Fields and Parameters.
87 | - Default Arguments such as `usage` and `help` can be automatically added to all Commands easily.
88 | - This design allows for **infinitely nestable** Commands, Options, and Values in a way that's simple to parse, analyze, and use in your projects.
89 | - **Multiplatform.** Tested across common architectures of Linux, Mac, and Windows.
90 | - **Granular, Robust Customization:**
91 | - [POSIX Compliant](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) by default, with plenty of ways to configure to **whatever standard you'd like**.
92 | - Posix: `command --option option_string "standalone value" subcmd -i 42 --bool`
93 | - Windows: `Your-Command -StringOption "value" -FileOption .\user\file\path`
94 | - Cova offers deep customization through the Argument Types and several Config Structs. These customizations all provide simple and predictable defaults, allowing you to only configure what you need.
95 | - [***And much more!***](https://github.com/00JCIV00/cova/wiki/Feature-List)
96 |
97 | ## Usage
98 | Cova makes it easy to set up your Argument Types at _comptime_ and use the input provided by your end users at _runtime_!
99 |
100 | ### Comptime Setup
101 | There are two main ways to set up your Argument Types. You can either convert existing Zig Types within your project or create them manually. You can even mix and match these techniques to get the best of both!
102 |
103 |
104 | Code Example
105 |
106 | ```zig
107 | const std = @import("std");
108 | const cova = @import("cova");
109 | pub const CommandT = cova.Command.Base();
110 | pub const OptionT = CommandT.OptionT;
111 | pub const ValueT = CommandT.ValueT;
112 |
113 | // The root Command for your program.
114 | pub const setup_cmd: CommandT = .{
115 | .name = "basic-app",
116 | .description = "A basic user management application designed to highlight key features of the Cova library.",
117 | .cmd_groups = &.{ "INTERACT", "VIEW" },
118 | .sub_cmds = &.{
119 | // A Sub Command created from converting a Struct named `User`.
120 | // Usage Ex: `basic-app new -f Bruce -l Wayne -a 40 -p "555 555 5555" -A " 1007 Mountain Drive, Gotham" true`
121 | .from(User, .{
122 | .cmd_name = "new",
123 | .cmd_description = "Add a new user.",
124 | .cmd_group = "INTERACT",
125 | .sub_descriptions = &.{
126 | .{ "is_admin", "Add this user as an admin?" },
127 | .{ "first_name", "User's First Name." },
128 | .{ "last_name", "User's Last Name." },
129 | .{ "age", "User's Age." },
130 | .{ "phone", "User's Phone #." },
131 | .{ "address", "User's Address." },
132 | },
133 | }),
134 | // A Sub Command created from a Function named `open`.
135 | // Usage Ex: `basic-app open users.csv`
136 | .from(@TypeOf(open), .{
137 | .cmd_name = "open",
138 | .cmd_description = "Open or create a users file.",
139 | .cmd_group = "INTERACT",
140 | }),
141 | // A manually created Sub Command, same as the root `setup_cmd`.
142 | // Usage Ex: `basic-app clean` or `basic-app delete --file users.csv`
143 | .{
144 | .name = "clean",
145 | .description = "Clean (delete) the default users file (users.csv) and persistent variable file (.ba_persist).",
146 | .alias_names = &.{ "delete", "wipe" },
147 | .cmd_group = "INTERACT",
148 | .opts = &.{
149 | .{
150 | .name = "clean_file",
151 | .description = "Specify a single file to be cleaned (deleted) instead of the defaults.",
152 | .alias_long_names = &.{ "delete_file" },
153 | .short_name = 'f',
154 | .long_name = "file",
155 | .val = .ofType([]const u8, .{
156 | .name = "clean_file",
157 | .description = "The file to be cleaned.",
158 | .alias_child_type = "filepath",
159 | .valid_fn = cova.Value.ValidationFns.validFilepath,
160 | }),
161 | },
162 | },
163 | },
164 | }
165 | };
166 | // Continue to Runtime Use...
167 | ```
168 |
169 |
170 | ### Runtime Use
171 | Once Cova has parsed input from your end users, it puts that data into the Command you set up.
172 | You can call various methods on the Command to use that data however you need.
173 |
174 |
175 | Code Example
176 |
177 | ```zig
178 | // ...continued from the Comptime Setup.
179 | pub fn main() !void {
180 | const gpa: std.heap.DebugAllocator(.{}) = .init;
181 | const alloc = gpa.allocator();
182 |
183 | // Initializing the `setup_cmd` with an allocator will make it available for Runtime use.
184 | const main_cmd = try setup_cmd.init(alloc, .{});
185 | defer main_cmd.deinit();
186 |
187 | // Parsing
188 | var args_iter = try cova.ArgIteratorGeneric.init(alloc);
189 | defer args_iter.deinit();
190 | const stdout = std.io.getStdOut().writer();
191 |
192 | cova.parseArgs(&args_iter, CommandT, main_cmd, stdout, .{}) catch |err| switch (err) {
193 | error.UsageHelpCalled,
194 | error.TooManyValues,
195 | error.UnrecognizedArgument,
196 | error.UnexpectedArgument,
197 | error.CouldNotParseOption => {},
198 | else => return err,
199 | };
200 |
201 | // Analysis (Using the data.)
202 | if (builtin.mode == .Debug) try cova.utils.displayCmdInfo(CommandT, main_cmd, alloc, &stdout);
203 |
204 | // Glossing over some project variables here.
205 |
206 | // Convert a Command back into a Struct.
207 | if (main_cmd.matchSubCmd("new")) |new_cmd| {
208 | var new_user = try new_cmd.to(User, .{});
209 | new_user._id = getNextID();
210 | try users.append(new_user);
211 | try stdout.print("Added:\n{s}\n", .{ new_user });
212 | }
213 | // Convert a Command back into a Function and call it.
214 | if (main_cmd.matchSubCmd("open")) |open_cmd| {
215 | user_file = try open_cmd.callAs(open, null, std.fs.File);
216 | }
217 | // Get the provided sub Command and check an Option from that sub Command.
218 | if (main_cmd.matchSubCmd("clean")) |clean_cmd| cleanCmd: {
219 | if ((try clean_cmd.getOpts(.{})).get("clean_file")) |clean_opt| {
220 | if (clean_opt.val.isSet()) {
221 | const filename = try clean_opt.val.getAs([]const u8);
222 | try delete(filename);
223 | break :cleanCmd;
224 | }
225 | }
226 | try delete("users.csv");
227 | try delete(".ba_persist");
228 | }
229 | }
230 | ```
231 |
232 |
233 | ### More Examples
234 | - [logger](./examples/logger.zig): The simple example from the top of the README.
235 | - [basic-app](./examples/basic_app.zig): Where the above examples come from.
236 | - [covademo](./examples/covademo.zig): This is the testbed for Cova, but its a good demo of virtually every feature in the library.
237 |
238 | ## Build-time Bonuses
239 | Cova's simple Meta Doc Generator build step lets you quickly and easily generate documents in the following formats based on the Commands you set up at comptime:
240 | - Help Docs:
241 | - Manpages
242 | - Markdown
243 | - Tab Completion Scripts:
244 | - Bash
245 | - Zsh
246 | - Powershell
247 | - Argument Templates:
248 | - JSON
249 | - KDL
250 |
251 |
252 | Code Example
253 |
254 | ```zig
255 | // Within 'build.zig'
256 | pub fn build(b: *std.Build) void {
257 | // Set up your build variables as normal.
258 |
259 | const cova_dep = b.dependency("cova", .{ .target = target, .optimize = optimize });
260 | const cova_mod = cova_dep.module("cova");
261 |
262 | // Set up your exe step as you normally would.
263 |
264 | const cova_gen = @import("cova").addCovaDocGenStep(b, cova_dep, exe, .{
265 | .kinds = &.{ .all },
266 | .version = "0.10.1",
267 | .ver_date = "25 MAR 2025",
268 | .author = "00JCIV00",
269 | .copyright = "MIT License",
270 | });
271 | const meta_doc_gen = b.step("gen-meta", "Generate Meta Docs using Cova");
272 | meta_doc_gen.dependOn(&cova_gen.step);
273 | }
274 | ```
275 |
276 |
277 | ## Demo
278 | 
279 |
280 | ## Alternatives
281 | - [flags](https://github.com/n0s4/flags)
282 | - [snek](https://github.com/BitlyTwiser/snek)
283 | - [yazap](https://github.com/PrajwalCH/yazap)
284 | - [zig-args](https://github.com/masterQ32/zig-args)
285 | - [zig-clap](https://github.com/Hejsil/zig-clap)
286 | - [zig-cli](https://github.com/sam701/zig-cli)
287 | - [zli](https://gitlab.com/nihklas/zli)
288 |
--------------------------------------------------------------------------------
/build.zig:
--------------------------------------------------------------------------------
1 | const std = @import("std");
2 | pub const generate = @import("src/generate.zig");
3 |
4 | pub fn build(b: *std.Build) void {
5 | const target = b.standardTargetOptions(.{});
6 | const optimize = b.standardOptimizeOption(.{});
7 | //const build_options = b.addOptions();
8 | const bin_name = b.option([]const u8, "name", "A name for the binary being created.");
9 | b.exe_dir = "./bin";
10 |
11 | // Modules & Artifacts
12 | // - Lib Module
13 | const cova_mod = b.addModule("cova", .{
14 | .root_source_file = b.path("src/cova.zig"),
15 | });
16 | // - Generator Artifact
17 | _ = b.addModule("cova_gen", .{
18 | .root_source_file = b.path("src/generator.zig"),
19 | });
20 |
21 | // Static Lib (Used for Docs)
22 | const cova_lib = b.addStaticLibrary(.{
23 | .name = "cova",
24 | .root_source_file = b.path("src/cova.zig"),
25 | .target = target,
26 | .optimize = optimize,
27 | });
28 | //b.installArtifact(cova_lib);
29 |
30 | // Tests
31 | const cova_tests = b.addTest(.{
32 | .root_source_file = b.path("src/cova.zig"),
33 | .target = target,
34 | .optimize = optimize,
35 | });
36 | const run_cova_tests = b.addRunArtifact(cova_tests);
37 | const test_step = b.step("test", "Run the cova library tests");
38 | test_step.dependOn(&run_cova_tests.step);
39 |
40 | // Docs
41 | const cova_docs = cova_lib;
42 | //const cova_docs = cova_tests;
43 | const build_docs = b.addInstallDirectory(.{
44 | .source_dir = cova_docs.getEmittedDocs(),
45 | .install_dir = .prefix,
46 | .install_subdir = "../docs",
47 | });
48 | const build_docs_step = b.step("docs", "Build the cova library docs");
49 | build_docs_step.dependOn(&build_docs.step);
50 |
51 |
52 | //==========================================
53 | // Examples
54 | //==========================================
55 | const examples = &.{ "cova-demo", "basic_app", "logger" };
56 | var ex_arena = std.heap.ArenaAllocator.init(b.allocator);
57 | defer ex_arena.deinit();
58 | const ex_alloc = ex_arena.allocator();
59 | inline for (examples) |example| {
60 | var ex_scored_buf = ex_alloc.dupe(u8, example) catch @panic("OOM");
61 | const ex_scored = std.mem.replaceOwned(u8, ex_alloc, ex_scored_buf[0..], "-", "_") catch @panic("OOM");
62 | const ex_name = exName: {
63 | if (std.mem.eql(u8, example, "cova-demo")) break :exName "covademo";
64 | break :exName example;
65 | };
66 | // - Exe
67 | const ex_exe = b.addExecutable(.{
68 | .name = bin_name orelse ex_name,
69 | .root_source_file = b.path(std.fmt.allocPrint(ex_alloc, "examples/{s}.zig", .{ ex_name }) catch @panic("OOM")),
70 | .target = target,
71 | .optimize = optimize,
72 | });
73 | ex_exe.root_module.addImport("cova", cova_mod);
74 | const build_ex_demo = b.addInstallArtifact(ex_exe, .{});
75 | const build_ex_demo_step = b.step(example, "Build the '" ++ example ++ "' example (default: Debug)");
76 | build_ex_demo_step.dependOn(&build_ex_demo.step);
77 | // - Demo Meta Docs
78 | const ex_demo_gen = createDocGenStep(
79 | b,
80 | cova_mod,
81 | b.path("src/generator.zig"),
82 | ex_exe,
83 | .{
84 | .kinds = &.{ .all },
85 | .version = "0.10.2",
86 | .ver_date = "23 OCT 2024",
87 | .author = "00JCIV00",
88 | .copyright = "MIT License",
89 | .help_docs_config = .{
90 | .local_filepath = std.fmt.allocPrint(ex_alloc, "examples/{s}_meta/help_docs/", .{ ex_scored }) catch @panic("OOM"),
91 | },
92 | .tab_complete_config = .{
93 | .local_filepath = std.fmt.allocPrint(ex_alloc,"examples/{s}_meta/tab_completions/", .{ ex_scored }) catch @panic("OOM"),
94 | .include_opts = true,
95 | },
96 | .arg_template_config = .{
97 | .local_filepath = std.fmt.allocPrint(ex_alloc, "examples/{s}_meta/arg_templates/", .{ ex_scored }) catch @panic("OOM"),
98 | },
99 | },
100 | );
101 | const ex_demo_gen_step = b.step(example ++ "-gen", "Generate Meta Docs for the '" ++ example ++ "'");
102 | ex_demo_gen_step.dependOn(&ex_demo_gen.step);
103 | }
104 | }
105 |
106 |
107 | /// Add Cova's Meta Doc Generation Step to a project's `build.zig`.
108 | /// Note, the `program_step` must have the same Target as the host machine.
109 | /// Prefer to use `addCovaDocGenStepOrError` if the step will be used in cross-compilation CI pipeline.
110 | pub fn addCovaDocGenStep(
111 | b: *std.Build,
112 | /// The Cova Dependency of the project's `build.zig`.
113 | cova_dep: *std.Build.Dependency,
114 | /// The Program Compile Step where the Command Type and Setup Command can be found.
115 | /// This is typically created with `const exe = b.addExecutable(.{...});` or similar
116 | program_step: *std.Build.Step.Compile,
117 | /// The Config for Meta Doc Generation.
118 | doc_gen_config: generate.MetaDocConfig,
119 | ) *std.Build.Step.Run {
120 | //const cova_dep = covaDep(b, .{});
121 | return createDocGenStep(
122 | b,
123 | cova_dep.module("cova"),
124 | cova_dep.path("src/generator.zig"),
125 | program_step,
126 | doc_gen_config,
127 | );
128 | }
129 |
130 | /// Add Cova's Meta Doc Generation Step to a project's `build.zig` or return an error if there's a Target mismatch.
131 | /// A Target mismatch happens if the provided `program_step` doesn't have the same Target as the host machine.
132 | /// This function is useful for cross-compilation in CI pipelines to ensure Target mismatches are handled properly.
133 | pub fn addCovaDocGenStepOrError(
134 | b: *std.Build,
135 | /// The Cova Dependency of the project's `build.zig`.
136 | cova_dep: *std.Build.Dependency,
137 | /// The Program Compile Step where the Command Type and Setup Command can be found.
138 | /// This is typically created with `const exe = b.addExecutable(.{...});` or similar
139 | program_step: *std.Build.Step.Compile,
140 | /// The Config for Meta Doc Generation.
141 | doc_gen_config: generate.MetaDocConfig,
142 | ) !*std.Build.Step.Run {
143 | const host_triplets = b.graph.host.result.zigTriple(b.allocator) catch @panic("OOM");
144 | defer b.allocator.free(host_triplets);
145 | const program_triplets = program_step.rootModuleTarget().zigTriple(b.allocator) catch @panic("OOM");
146 | defer b.allocator.free(program_triplets);
147 | if (!std.mem.eql(u8, host_triplets, program_triplets)) return error.TargetMismatch;
148 | return createDocGenStep(
149 | b,
150 | cova_dep.module("cova"),
151 | cova_dep.path("src/generator.zig"),
152 | program_step,
153 | doc_gen_config,
154 | );
155 | }
156 |
157 | /// Create the Meta Doc Generation Step.
158 | fn createDocGenStep(
159 | b: *std.Build,
160 | cova_mod: *std.Build.Module,
161 | cova_gen_path: std.Build.LazyPath,
162 | program_step: *std.Build.Step.Compile,
163 | doc_gen_config: generate.MetaDocConfig,
164 | ) *std.Build.Step.Run {
165 | const program_mod = program_step.root_module;
166 | const cova_gen_exe = b.addExecutable(.{
167 | .name = std.fmt.allocPrint(b.allocator, "cova_generator_{s}", .{ program_step.name }) catch @panic("OOM"),
168 | .root_source_file = cova_gen_path,
169 | .target = b.graph.host,
170 | .optimize = .Debug,
171 | });
172 | b.installArtifact(cova_gen_exe);
173 | cova_gen_exe.root_module.addImport("cova", cova_mod);
174 | cova_gen_exe.root_module.addImport("program", program_mod);
175 |
176 | const md_conf_opts = b.addOptions();
177 | var sub_conf_map = std.StringHashMap(?*std.Build.Step.Options).init(b.allocator);
178 | sub_conf_map.put("help_docs_config", null) catch @panic("OOM");
179 | sub_conf_map.put("tab_complete_config", null) catch @panic("OOM");
180 | sub_conf_map.put("arg_template_config", null) catch @panic("OOM");
181 |
182 | inline for (@typeInfo(generate.MetaDocConfig).@"struct".fields) |field| {
183 | switch(@typeInfo(field.type)) {
184 | .@"struct", .@"enum" => continue,
185 | .optional => |optl| {
186 | switch (@typeInfo(optl.child)) {
187 | .@"struct" => |struct_info| {
188 | const maybe_conf = @field(doc_gen_config, field.name);
189 | if (maybe_conf) |conf| {
190 | const doc_conf_opts = b.addOptions();
191 | inline for (struct_info.fields) |s_field| {
192 | if (@typeInfo(s_field.type) == .@"enum") {
193 | doc_conf_opts.addOption(usize, s_field.name, @intFromEnum(@field(conf, s_field.name)));
194 | continue;
195 | }
196 | doc_conf_opts.addOption(s_field.type, s_field.name, @field(conf, s_field.name));
197 | }
198 | doc_conf_opts.addOption(bool, "provided", true);
199 | sub_conf_map.put(field.name, doc_conf_opts) catch @panic("OOM");
200 | }
201 | continue;
202 | },
203 | else => {},
204 | }
205 | },
206 | .pointer => |ptr| {
207 | if (ptr.child == generate.MetaDocConfig.MetaDocKind) {
208 | var kinds_list = std.ArrayList(usize).init(b.allocator);
209 | for (@field(doc_gen_config, field.name)) |kind|
210 | kinds_list.append(@intFromEnum(kind)) catch @panic("There was an issue with the Meta Doc Config.");
211 | md_conf_opts.addOption(
212 | []const usize,
213 | field.name,
214 | kinds_list.toOwnedSlice() catch @panic("There was an issue with the Meta Doc Config."),
215 | );
216 | continue;
217 | }
218 | },
219 | else => {},
220 | }
221 | md_conf_opts.addOption(
222 | field.type,
223 | field.name,
224 | @field(doc_gen_config, field.name),
225 | );
226 | }
227 | cova_gen_exe.root_module.addOptions("md_config_opts", md_conf_opts);
228 | var sub_conf_map_iter = sub_conf_map.iterator();
229 | while (sub_conf_map_iter.next()) |conf| {
230 | cova_gen_exe.root_module.addOptions(
231 | conf.key_ptr.*,
232 | if (conf.value_ptr.*) |conf_opts| conf_opts
233 | else confOpts: {
234 | const conf_opts = b.addOptions();
235 | conf_opts.addOption(bool, "provided", false);
236 | break :confOpts conf_opts;
237 | }
238 | );
239 | }
240 |
241 | return b.addRunArtifact(cova_gen_exe);
242 | }
243 |
244 | /// Return the Cova Dependency
245 | /// Courtesy of @castholm
246 | fn covaDep(b: *std.Build, args: anytype) *std.Build.Dependency {
247 | getDep: {
248 | const all_pkgs = @import("root").dependencies.packages;
249 | const pkg_hash =
250 | inline for (@typeInfo(all_pkgs).@"struct".decls) |decl| {
251 | const pkg = @field(all_pkgs, decl.name);
252 | if (@hasDecl(pkg, "build_zig") and pkg.build_zig == @This()) break decl.name;
253 | }
254 | else break :getDep;
255 | const dep_name =
256 | for (b.available_deps) |dep| { if (std.mem.eql(u8, dep[1], pkg_hash)) break dep[0]; }
257 | else break :getDep;
258 | return b.dependency(dep_name, args);
259 | }
260 | std.debug.panic("'cova' is not a dependency of '{s}'", .{ b.pathFromRoot("build.zig.zon") });
261 | }
262 |
--------------------------------------------------------------------------------
/build.zig.zon:
--------------------------------------------------------------------------------
1 | .{
2 | .name = .cova,
3 | .fingerprint = 0x88c84524738e1fc,
4 | .paths = .{
5 | "build.zig",
6 | "build.zig.zon",
7 | "src",
8 | },
9 | .version = "0.10.1",
10 | .dependencies = .{},
11 | }
12 |
--------------------------------------------------------------------------------
/docs/README_extended.md:
--------------------------------------------------------------------------------
1 | # cova
2 |
3 | Commands, Options, Values, Arguments. A simple yet robust cross-platform command line argument parsing library for Zig.
4 | ___
5 |
6 | ## Overview
7 | Cova is based on the idea that Arguments will fall into one of three types: Commands, Options, or Values. These Types are assembled into a single Command struct which is then used to parse argument tokens. _This extended Readme is the original. It's a bit more verbose, but has addtional information without requiring a search through the Docs and Guides._
8 |
9 | ## Table of Contents
10 | - [Demo](#demo)
11 | - [Features](#features)
12 | - [Goals](#goals)
13 | - [Pre-Public Beta Release](#pre-public-beta-release)
14 | - [Public Beta Release](#public-beta-release)
15 | - [Documentation](#documentation)
16 | - [Install](#install)
17 | - [Package Manager](#package-manager)
18 | - [Demo from source](#build-the-basic-app-demo-from-source)
19 | - [Usage](#usage)
20 | - [Quick Setup](#quick-setup)
21 | - [Basic Setup](#basic-setup)
22 | - [Advanced Setup](#advanced-setup)
23 | - [Alternatives](#alternatives)
24 |
25 | ## Demo
26 | 
27 |
28 | ## Features
29 | - **Comptime Setup. Runtime Use.**
30 | - Cova is designed to have Argument Types set up at ***compile time*** so they can be validated during each compilation, thus providing the library user with immediate feedback.
31 | - Once validated, Argument Types are initialized to memory for ***runtime*** use where end user argument tokens are parsed then made ready to be analyzed by library user code.
32 | - **Simple Design:**
33 | - All Argument tokens are parsed to Commands, Options, or Values.
34 | - These Argument Types can be *created from* or *converted to* Structs, Unions, and Functions along with their corresponding Fields and Parameters.
35 | - The most Basic Setup requires only Cova imports, a library user Struct, and a few function calls for parsing.
36 | - POSIX Compliant (as defined [here](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html)) by default.
37 | - Multiplatform. Tested across:
38 | - x86-linux
39 | - x86_64-linux
40 | - arm-linux
41 | - aarch64-linux
42 | - x86_64-windows
43 | - x86_64-macos
44 | - *Should support all POSIX compliant systems.*
45 | - Commands:
46 | - Contain sub-Commands, Options, and Values.
47 | - Precede their Options and Values when parsed (i.e. `command --option opt_val "value"`)
48 | - Auto-generated Help and Usage message functions that can be invoked as Commands and/or Options (i.e. `command help` or `command -h/--help`)
49 | - Can be Nested (i.e `main-cmd --main-opt sub-cmd --sub-opt "sub val"`)
50 | - Options:
51 | - Wrap Values to make them optional and allow them to be given in any order.
52 | - Can be given multiple times in any valid format (i.e `--int 1 --int=2 -i 3 -i4`)
53 | - Can be Delimited (i.e. `-i100,58,80 -f 100.1,84.96,74.58 --bools=true,false,false,true`)
54 | - Short Options:
55 | - Begin w/ `-`
56 | - Chainable (i.e. `-abc` = `-a -b -c`)
57 | - Variable Separators (i.e. `-c Tokyo` = `-c=Tokyo` = `-cTokyo`)
58 | - Long Options:
59 | - Begin w/ `--`
60 | - Variable Separators (i.e. `--city Tokyo` = `--city=Tokyo`)
61 | - Can be Abbreviated (i.e. `--long-option` = `--long`)
62 | - Values:
63 | - Also known as Positional Arguments.
64 | - Do not use prefixes (i.e. `command "string value"`)
65 | - Must be a specific type given in a specific order. (i.e. `command "string_val" 10 true`)
66 | - Can be given multiple times (i.e. `my-cmd "string val 1" "string val 2" "string val 3"`)
67 | - Can be Delimited (i.e. `my-cmd 50,100,68`)
68 | - **Granular, Robust Customization:**
69 | - Cova offers deep customization through the fields of the Argument Types and the several Config Structs. These Types and Structs all provide simple and predictable defaults, allowing library users to only configure what they need.
70 | - Parsing:
71 | - Auto-handle Usage/Help calls.
72 | - Choose how errors should be reacted to with either a Usage/Help message or no reaction.
73 | - Decide if Option parsing should be terminated after a standalone long prefix such as `--`.
74 | - Commands:
75 | - Configure Templates or Callback Functions for generated Command Help/Usage messages.
76 | - Set Rules for converting From/To a Struct, Union, or Function.
77 | - Mandate that a Command takes a sub-Command if available.
78 | - Mandate all Values be filled.
79 | - Options:
80 | - Configure Templates or Callback Functions for generated Option Help/Usage messages.
81 | - Customize Short and Long prefixes (i.e. `/s` or `__long-opt`).
82 | - Set the allowed Separator Character(s) between Options and their Values.
83 | - Allow/Disallow Abbreviated Long Options (i.e `--long-opt` can be `--long`).
84 | - Values:
85 | - Create Values with a specific type (Bool, Uint, Int, Float, or String) or even add custom Types.
86 | - Configure Values to be Individual or Multi, allowing multiple of the same type to be stored in a single Value.
87 | - Set the allowed Delimiter Characters between data elements provided to a single Value (i.e `my-cmd multi,string,value`).
88 | - Set the Rules for how Values are *set* through **custom Parsing and Validation Functions!**
89 | - ***And much more!***_********
90 |
91 | ## Goals
92 | ### Pre Public Beta Release
93 | - [v0.7.0-beta](https://github.com/00JCIV00/cova/issues/9)
94 | ### Public Beta Release
95 | - [v0.8.0-beta](https://github.com/00JCIV00/cova/issues?q=milestone%3Av0.8.0-beta)
96 | - [v0.9.0-beta](https://github.com/00JCIV00/cova/issues?q=milestone%3Av0.9.0-beta)
97 | - [v0.10.0-beta](https://github.com/00JCIV00/cova/issues?q=milestone%3Av0.10.0-beta)
98 |
99 | ## Documentation
100 | - [API](https://00jciv00.github.io/cova/#A;cova)
101 | - [Guides](https://00jciv00.github.io/cova/#G;)
102 |
103 | ## Install
104 | ### Package Manager
105 | 1. Find the latest `v#.#.#` commit [here](https://github.com/00JCIV00/cova/commits/main).
106 | 2. Copy the full SHA for the commit.
107 | 3. Add the dependency to `build.zig.zon`:
108 | ```bash
109 | zig fetch --save "https://github.com/00JCIV00/cova/archive/.tar.gz"
110 | ```
111 | 4. Add the dependency and module to `build.zig`:
112 | ```zig
113 | // Cova Dependency
114 | const cova_dep = b.dependency("cova", .{ .target = target });
115 | // Cova Module
116 | const cova_mod = cova_dep.module("cova");
117 | // Executable
118 | const exe = b.addExecutable(.{
119 | .name = "cova_example",
120 | .root_source_file = .{ .path = "src/main.zig" },
121 | .target = target,
122 | .optimize = optimize,
123 | });
124 | // Add the Cova Module to the Executable
125 | exe.root_module.addImport("cova", cova_mod);
126 | ```
127 |
128 | ### Package Manager - Alternative
129 | Note, this method makes Cova easier to update by simply re-running `zig fetch --save https://github.com/00JCIV00/cova/archive/[BRANCH].tar.gz`. However, it can lead to non-reproducible builds because the url will always point to the newest commit of the provided branch. Details can be found in [this discussion](https://ziggit.dev/t/feature-or-bug-w-zig-fetch-save/2565).
130 | 1. Choose a branch to stay in sync with.
131 | - `main` is the latest stable branch.
132 | - The highest `v#.#.#` is the development branch.
133 | 2. Add the dependency to `build.zig.zon`:
134 | ```shell
135 | zig fetch --save https://github.com/00JCIV00/cova/archive/[BRANCH FROM STEP 1].tar.gz
136 | ```
137 | 3. Continue from Step 4 above.
138 |
139 | ### Build the Basic-App Demo from source
140 | 1. Use the latest Zig (v0.12) for your system. Available [here](https://ziglang.org/download/).
141 | 2. Run the following in whichever directory you'd like to install to:
142 | ```bash
143 | git clone https://github.com/00JCIV00/cova.git
144 | cd cova
145 | zig build basic-app
146 | ```
147 | 3. Try it out!
148 | ```bash
149 | cd bin
150 | ./basic-app help
151 | ```
152 |
153 | ## Usage
154 | The following are a few working examples to get cova integrated into a project quickly. The library contains many more features that can be found in the Documentation section above.
155 |
156 | ### Quick Setup
157 | - This is a minimum working demo of Cova integrated into a project.
158 | ```zig
159 | const std = @import("std");
160 | const cova = @import("cova");
161 | const CommandT = cova.Command.Base();
162 |
163 | pub const ProjectStruct = struct {
164 | pub const SubStruct = struct {
165 | sub_uint: ?u8 = 5,
166 | sub_string: []const u8,
167 | },
168 |
169 | subcmd: SubStruct = .{},
170 | int: ?i4 = 10,
171 | flag: ?bool = false,
172 | strings: [3]const []const u8 = .{ "Three", "default", "strings." },
173 | };
174 |
175 | const setup_cmd = CommandT.from(ProjectStruct);
176 |
177 | pub fn main() !void {
178 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
179 | defer arena.deinit();
180 | const alloc = arena.allocator();
181 | const stdout = std.io.getStdOut().writer();
182 |
183 | const main_cmd = try setup_cmd.init(alloc, .{});
184 | defer main_cmd.deinit();
185 |
186 | var args_iter = try cova.ArgIteratorGeneric.init(alloc);
187 | defer args_iter.deinit();
188 |
189 | cova.parseArgs(&args_iter, CommandT, &main_cmd, stdout, .{}) catch |err| switch(err) {
190 | error.UsageHelpCalled,
191 | else => return err,
192 | }
193 | try cova.utils.displayCmdInfo(CommandT, &main_cmd, alloc, stdout);
194 | }
195 | ```
196 |
197 | #### Breakdown
198 | - Imports
199 | ```zig
200 | ...
201 | // The main cova library module. This is added via `build.zig` & `build.zig.zon` during
202 | // installation.
203 | const cova = @import("cova");
204 | // The Command Type for all Commands in this project.
205 | const CommandT = cova.Command.Base();
206 | ...
207 | ```
208 |
209 | - A Valid Project Struct. The rules for what makes a Valid struct and how they're converted into Commands can be found in the API Documentation under `cova.Command.Custom.from()`.
210 | ```zig
211 | ...
212 | // This comptime struct is valid to be parsed into a cova Command.
213 | pub const ProjectStruct = struct {
214 | // This nested struct is also valid.
215 | pub const SubStruct = struct {
216 | // Optional Primitive type fields will be converted into cova Options.
217 | // By default, Options will be given a long name and a short name based on the
218 | // field name. (i.e. int = `-i` or `--int`)
219 | sub_uint: ?u8 = 5,
220 |
221 | // Primitive type fields will be converted into Values.
222 | sub_string: []const u8,
223 | },
224 |
225 | // Struct fields will be converted into cova Commands.
226 | subcmd: SubStruct = .{},
227 |
228 | // The default values of Primitive type fields will be applied as the default value
229 | // of the converted Option or Value.
230 | int: ?i4 = 10,
231 |
232 | // Optional Booleans will become cova Options that don't take a Value and are set to
233 | // `true` simply by calling the Option's short or long name.
234 | flag: ?bool = false,
235 |
236 | // Arrays will be turned into Multi-Values or Multi-Options based on the array's
237 | // child type.
238 | strings: [3]const []const u8 = .{ "Three", "default", "strings." },
239 | };
240 | ...
241 | ```
242 |
243 | - Creating the Comptime Command.
244 | ```zig
245 | ...
246 | // `from()` method will convert the above Struct into a Command.
247 | // This must be done at Comptime for proper Validation before Initialization to memory
248 | // for Runtime use.
249 | const setup_cmd = CommandT.from(ProjectStruct);
250 | ...
251 | ```
252 |
253 | - Command Validation and Initialization to memory for Runtime Use.
254 | ```zig
255 | ...
256 | pub fn main() !void {
257 | ...
258 |
259 | // The `init()` method of a Command instance will Validate the Command's
260 | // Argument Types for correctness and distinct names, then it will return a
261 | // memory allocated copy of the Command for argument token parsing and
262 | // follow on analysis.
263 | const main_cmd = try setup_cmd.init(alloc, .{});
264 | defer main_cmd.deinit();
265 |
266 | ...
267 | }
268 | ```
269 |
270 | - Set up the Argument Iterator.
271 | ```zig
272 | pub fn main() {
273 | ...
274 |
275 | // The ArgIteratorGeneric is used to step through argument tokens.
276 | // By default (using `init()`), it will provide Zig's native, cross-platform ArgIterator
277 | // with end user argument tokens. There's also cova's RawArgIterator that can be used to
278 | // parse any slice of strings as argument tokens.
279 | var args_iter = try cova.ArgIteratorGeneric.init(alloc);
280 | defer args_iter.deinit();
281 |
282 | ...
283 | }
284 | ```
285 |
286 | - Parse argument tokens and Display the result.
287 | ```zig
288 | pub fn main() !void {
289 | ...
290 |
291 | // The `parseArgs()` function will parse the provided ArgIterator's (`&args_iter`)
292 | // tokens into Argument Types within the provided Command (`main_cmd`).
293 | try cova.parseArgs(&args_iter, CommandT, &main_cmd, stdout, .{});
294 |
295 | // Once parsed, the provided Command will be available for analysis by the
296 | // project code. Using `utils.displayCmdInfo()` will create a neat display
297 | // of the parsed Command for debugging.
298 | try utils.displayCmdInfo(CommandT, &main_cmd, alloc, stdout);
299 | }
300 | ```
301 |
302 | ### Basic Setup
303 | - [Basic App](./examples/basic_app.zig)
304 | - This is a well-documented example of how to integrate Cova into a basic project and utilize many of its standout features.
305 |
306 | ### Advanced Setup
307 | - [Advanced Demo](./examples/covademo.zig)
308 | - The `covademo` is a showcase of most of Cova's features. This demo also serves as a test bed for features that are in development, so it's not well-documented in several areas. That said, it can still be a useful reference for how certain features can be used.
309 |
310 |
311 | ## Alternatives
312 | - [yazap](https://github.com/PrajwalCH/yazap)
313 | - [zig-args](https://github.com/masterQ32/zig-args)
314 | - [zig-clap](https://github.com/Hejsil/zig-clap)
315 | - [zig-cli](https://github.com/sam701/zig-cli)
316 | - [zig-parse-args](https://github.com/winksaville/zig-parse-args)
317 |
--------------------------------------------------------------------------------
/docs/cova_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/00JCIV00/cova/e8c25e57ca9c3dba6de32406168c717845812a71/docs/cova_demo.gif
--------------------------------------------------------------------------------
/docs/guides/arg_types/arg_types.md:
--------------------------------------------------------------------------------
1 | # Overview
2 | Cova is based on the idea that Arguments will fall into one of three types: Commands, Options, or Values. These types are assembled into a single Command struct which is then used to parse argument tokens.
3 |
4 | ## Argument Types
5 | ### Command
6 | A Command is a container Argument for sub Commands, Options, and Values. It can contain any mix of those Arguments or none at all if it's to be used as a standalone command (for instance `covademo help`). The [`checkCmd()`](`cova.Command.Custom.checkCmd`) A HashMap for Options or Values can be created using either the `getOpts()` or `getVals()` method. Usage and Help statements for a Command can also be generated using the `usage()` and `help()` methods respectively.
7 | #### Example:
8 | ```zig
9 | const cmd = try alloc.create(Command);
10 | cmd.* = .{
11 | .name = "covademo",
12 | .help_prefix = "CovaDemo",
13 | .description = "A demo of the Cova command line argument parser.",
14 | .sub_cmds = subCmdsSetup: {
15 | var setup_cmds = [_]*const Command{
16 | &Command{
17 | .name = "help",
18 | .help_prefix = "CovaDemo",
19 | .description = "Show the CovaDemo help display.",
20 | },
21 | &Command{
22 | .name = "usage",
23 | .help_prefix = "CovaDemo",
24 | .description = "Show the CovaDemo usage display.",
25 | },
26 | };
27 | break :subCmdsSetup setup_cmds[0..];
28 | },
29 | .opts = { ... },
30 | .vals = { ... }
31 | }
32 | defer alloc.destroy(cmd);
33 | ```
34 |
35 | ### Option
36 | An Option is an Argument which wraps a Value and is ALWAYS optional. It may have a Short Name (ex: `-h`), a Long Name (ex: `--name "Lilly"`), or both. The prefixes for both Short and Long names can be set by the library user. If the wrapped Value has a Boolean type it will default to False and can be set to True using the Option without a following Argument (ex: `-t` or `--toggle`). They also provide `usage()` and `help()` methods similar to Commands.
37 | #### Example:
38 | ```zig
39 | .opts = optsSetup: {
40 | var setup_opts = [_]*const Option{
41 | &Option{
42 | .name = "stringOpt",
43 | .short_name = 's',
44 | .long_name = "stringOpt",
45 | .val = &Value.init([]const u8, .{
46 | .name = "stringVal",
47 | .description = "A string value.",
48 | }),
49 | .description = "A string option.",
50 | },
51 | &Option{
52 | .name = "intOpt",
53 | .short_name = 'i',
54 | .long_name = "intOpt",
55 | .val = &Value.init(i16, .{
56 | .name = "intVal",
57 | .description = "An integer value.",
58 | .val_fn = struct{ fn valFn(int: i16) bool { return int < 666; } }.valFn
59 | }),
60 | .description = "An integer option.",
61 | },
62 | &Option{
63 | .name = "help",
64 | .short_name = 'h',
65 | .long_name = "help",
66 | .val = &Value.init(bool, .{
67 | .name = "helpFlag",
68 | .description = "Flag for help!",
69 | }),
70 | .description = "Show the CovaDemo help display.",
71 | },
72 | };
73 | break :optsSetup setup_opts[0..];
74 | },
75 | ```
76 |
77 | ### Value
78 | A Value (also known as a Positional Argument) is an Argument that is expected in a specific order and should be interpreted as a specific type. The full list of available types can be seen in `src/Value.zig/Generic`, but the basics are Boolean, String (`[]const u8`), Integer (`u/i##`), or Float (`f##`). A Value will be parsed to its corresponding type and can be retrieved using `get()`. They can also be given a Default value using the `.default_val` field and a Validation Function using the `.val_fn` field.
79 | #### Example:
80 | ```zig
81 | .vals = valsSetup: {
82 | var setup_vals = [_]*const Value.Generic{
83 | &Value.init([]const u8, .{
84 | .name = "cmdStr",
85 | .description = "A string value for the command.",
86 | }),
87 | &Value.init(u128, .{
88 | .name = "cmd_u128",
89 | .description = "A u128 value for the command.",
90 | // Default Value
91 | .default_val = 654321,
92 | // Validation Function
93 | .val_fn = struct{ fn valFn(val: u128) bool { return val > 123456 and val < 987654; } }.valFn,
94 | }),
95 | };
96 | break :valsSetup setup_vals[0..];
97 | }
98 | ```
99 |
100 | ### Parsing
101 | Parsing is handled by the `parseArgs()` function in cova.zig. It takes in an ArgIterator, a Command, and a Writer, then parses each token sequentially. The results of a successful parse are stored in the provided Command which can then be analyzed by the user.
102 | #### Example:
103 | ```zig
104 | pub fn main() !void {
105 | // Parse Arguments
106 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
107 | defer arena.deinit();
108 | const alloc = arena.allocator();
109 |
110 | const cmd = try alloc.create(Command);
111 | cmd.* = .{ ... };
112 | defer alloc.destroy(cmd);
113 |
114 | const args = try proc.argsWithAllocator(alloc);
115 | try cova.parseArgs(&args, cmd, stdout);
116 |
117 | // Analyze Data
118 | // - Check for the "help" Sub Command and run its help() method
119 | if (cmd.sub_cmd != null and std.mem.eql(u8, cmd.sub_cmd.?.name, "help")) try cmd.help(stdout);
120 | // - Get a HashMap of the Command's Options
121 | if (cmd.opts != null) {
122 | var opt_map: StringHashMap(*const Option) = try cmd.getOpts(alloc);
123 | defer opt_map.deinit();
124 | }
125 | // - Print out all of the Command's Values
126 | for (cmd.vals orelse return) |val| {
127 | switch (meta.activeTag(val.*)) {
128 | .string => {
129 | std.debug.print(" Val: {?s}, Data: {s}\n", .{
130 | val.name(),
131 | val.string.get() catch "",
132 | });
133 | },
134 | inline else => |tag| {
135 | std.debug.print(" Val: {?s}, Data: {any}\n", .{
136 | val.name(),
137 | @field(val, @tagName(tag)).get() catch null,
138 | });
139 | },
140 | }
141 | }
142 | }
143 | ```
144 |
--------------------------------------------------------------------------------
/docs/guides/arg_types/command.md:
--------------------------------------------------------------------------------
1 | # Command
2 | A Command is a container Argument Type for sub-Commands, Options, and Values. It can contain any mix of those Argument Types or none at all if it's to be used as a standalone Command (i.e. `covademo help`).
3 |
4 | ## Configuring a Command Type
5 | Before a Command is used within a project, a Command Type should be configured. A Command Type is used to set common-to-all properties of each Command created from it. Typically, this will cover the main Command of a project and all of its sub-Commands. The easiest way to configure a Command Type is to simply use `cova.Command.Base`() which is the default Command Type. To configure a custom Command Type, use `cova.Command.Custom`() with a `cova.Command.Config` (`config`) which provides several customizations to set up the Option Type, Value Type, Help/Usage messages, Mandatory sub-Commands/Values, and max sub Arguments. Once configured, the Command Type has access to all of the functions under `cova.Command.Custom` and any Command created from the Command Type similarly has access to all of the corresponding methods.
6 |
7 | ## Setting up a Command
8 | Commands are meant to be set up in Comptime and used in Runtime. This means that the Command and all of its subordinate Arguments (Commands, Options, and Values) should be Comptime-known, allowing for proper Validation which provides direct feedback to the library user during compilation instead of preventable errors to the end user during Runtime.
9 |
10 | There are two ways to set up a Command. The first is to use Zig's standard syntax for creating a struct instance and fill in the fields of the previously configured Command Type. Alternatively, if the project has a Struct, Union, or Function Type that can be represented as a Command, the `cova.Command.Custom.from`() function can be used to create the Command.
11 |
12 | After they're set up, Commands should be Validated and Allocated to the heap for Runtime use. This is accomplished using `cova.Command.Custom.init`(). At this point, the data within the Command should be treated as read-only by the libary user, so the library is set up to handle initialized Commands as constants (`const`).
13 |
14 | ## Additional Info
15 | For easy analysis, parsed Commands can be converted to valid Structs or Unions using the `cova.Command.Custom.to`() function, or called as Functions using the `cova.Command.Custom.callAs`() function. Other functions for analysis include `cova.Command.Custom.checkCmd`() and `cova.Command.Custom.matchCmd`() to access specific sub-Commands, creating a String HashMap for Options or Values using the respective `cova.Command.Custom.getOpts`() or `cova.Command.Custom.getVals`() methods, and using the `cova.Command.Custom.checkFlag`() method to simply check if a sub-Argument was set. Usage and Help statements for a Command can also be generated using the `cova.Command.Custom.usage`() and `cova.Command.Custom.help`() methods respectively.
16 |
17 | ## Example:
18 | ```zig
19 | ...
20 | pub const cova = @import("cova");
21 | pub const CommandT = cova.Command.Custom(.{ global_help_prefix = "CovaDemo" });
22 |
23 | // Comptime Setup
24 | const setup_cmd: CommandT = .{
25 | .name = "covademo",
26 | .description = "A demo of the Cova command line argument parser.",
27 | .sub_cmds = &.{
28 | .{
29 | .name = "sub_cmd",
30 | .description = "This is a Sub Command within the 'covademo' main Command.",
31 | },
32 | command_from_elsewhere,
33 | CommandT.from(SomeValidStructType),
34 | }
35 | .opts = { ... },
36 | .vals = { ... },
37 | };
38 |
39 | pub fn main() !void {
40 | ...
41 | // Runtime Use
42 | const main_cmd = try setup_cmd.init(alloc);
43 | defer main_cmd.deinit();
44 |
45 | cova.parseArgs(..., &main_cmd, ...);
46 | utils.displayCmdInfo(CustomCommand, &main_cmd, alloc, stdout);
47 | }
48 | ```
49 |
--------------------------------------------------------------------------------
/docs/guides/arg_types/option.md:
--------------------------------------------------------------------------------
1 | # Option
2 | An Option (also known as a Flag) is an Argument Type which wraps a Value and is typically optional. They should be used for Values that an end user is not always expected to provide. Additionally, unlike Values, they can be expected in any order since they are set by name.
3 |
4 | ## Configuring an Option Type
5 | Similar to Commands, an Option Type should be configured before any Options are created. Fortunately, the process is virtually the same as with Command Types and both configurations are designed to be done simultaneously. The standard way to configure an Option Type is by configuring the `cova.Command.Config.opt_config` field during Command Type configuration. This field is a `cova.Option.Config` and works effectively the same way as its Command counterpart. If the field is not configured it will be set to the default configuration. Done this way, the Option Type will be a part of the Command Type and will have access to all of the respective functions and methods within `cova.Option.Custom`().
6 |
7 | ## Setting up an Option
8 | The most straightforward way to set up an Option is to simply use Zig's standard syntax for filling out a struct. More specifically, Options can bet set up within the `cova.Command.Custom.opts` field of a Command using Anonymous Struct (or Tuple) syntax. Similarly, an Option's internal Value can also be set up this way via the Option's `val` field.
9 |
10 | Alternatively, Options will be created automatically when using `cova.Command.Custom.from`().
11 |
12 | ## Additional Info
13 | An Option must have a Short Name (ex: `-h`), a Long Name (ex: `--name "Lilly"`), or both. The prefixes for both Short and Long names are set by the library user during a normal setup. If the wrapped Value of an Option has a Boolean Type it will default to `false` and can be set to `true` using the Option without a following argument token from the end user (ex: `-t` or `--toggle`). They also provide `usage()` and `help()` methods similar to Commands.
14 |
15 | ## Example:
16 | ```zig
17 | // Within a Command
18 | ...
19 | .opts = &.{
20 | .{
21 | .name = "string_opt",
22 | .description = "A string option.",
23 | .short_name = 's',
24 | .long_name = "string",
25 | .val = ValueT.ofType([]const u8, .{
26 | .name = "stringVal",
27 | .description = "A string value.",
28 | }),
29 | },
30 | .{
31 | .name = "int_opt",
32 | .description = "An integer option.",
33 | .short_name = 'i',
34 | .long_name = "int",
35 | .val = ValueT.ofType(i16, .{
36 | .name = "int_opt_val",
37 | .description = "An integer option value.",
38 | .val_fn = struct{ fn valFn(int: i16) bool { return int < 666; } }.valFn
39 | }),
40 | },
41 | },
42 | ```
43 |
--------------------------------------------------------------------------------
/docs/guides/arg_types/value.md:
--------------------------------------------------------------------------------
1 | # Value
2 | A Value (also known as a Positional Argument) is an Argument Type that is expected in a specific order and should be interpreted as a specific Type. The full list of available Types can be seen in `cova.Value.Generic` and customized via `cova.Value.Custom`, but the basics are Boolean, String (`[]const u8`), Integer (`u/i##`), or Float (`f##`). A single Value can store individual or multiple instances of one of these Types. Values are also used to hold and represent the data held by an Option via the `cova.Option.Custom.val` field. As such, anything that applies to Values is also "inherited" by Options.
3 |
4 | ## Understanding Typed vs Generic vs Custom Values
5 | The base data for a Value is held within a `cova.Value.Typed` instance. However, to provide flexibility for the cova library and library users, the `cova.Value.Generic` union will wrap any `cova.Value.Typed` and provide access to several common-to-all methods. This allows Values to be handled in a generic manner in cases such as function parameters, function returns, collections, etc. However, if the actual parsed data of the Value is needed, the appropriate `cova.Value.Generic` field must be used. Field names for this union are simply the Value's Child Type name with the exception of `[]const u8` which is the `.string` field.
6 |
7 | Finally, the `cova.Value.Custom` sets up and wraps `cova.Value.Generic` union. This Type is used similary to `cova.Command.Custom` and `cova.Option.Custom`. It allows common-to-all properties of Values within a project to be configured and provides easy methods for accessing properties of individual Values.
8 |
9 | ## Configuring a Value Type
10 | This process mirrors that of Option Types nearly one-for-one. A `cova.Value.Config` can be configured directly within the Command Config via the `cova.Command.Config.val_config` field. If not configured, the defaults will be used. A major feature of the Custom Value Type and Generic Value Union combination is the ability to set custom types for the Generic Value Union. This is accomplished via the `cova.Value.Config`, by setting the `cova.Value.Config.custom_types` field.
11 |
12 | ### Adding Custom Child Types
13 | Adding custom Child Types allows Cova to parse argument tokens into virtually any Type. This is accomplished by first adding the custom Child Types to the `Value.Config.custom_types` field, then (non-primitive Types) specifying parsing functions for these Types via `Value.Config.child_type_parse_fns` as seen here:
14 | ```zig
15 | const CommandT = cova.Command.Custom(cova.Command.Config{
16 | .global_help_prefix = "Example",
17 | .val_config = .{
18 | .custom_types = &.{
19 | std.net.Address,
20 | std.fs.File,
21 | },
22 | .child_type_parse_fns = &.{
23 | .{
24 | .ChildT = std.net.Address,
25 | .parse_fn = struct{
26 | pub fn parseIP(addr: []const u8, _: std.mem.Allocator) !std.net.Address {
27 | var iter = std.mem.splitScalar(u8, addr, ':');
28 | return net.Address.parseIp(
29 | iter.first(),
30 | try std.fmt.parseInt(u16, iter.next() orelse "-", 10),
31 | ) catch |err| {
32 | std.log.err("The provided IP address '{s}' is invalid.", .{ addr });
33 | return err;
34 | };
35 | }
36 | }.parseIP,
37 | },
38 | .{
39 | .ChildT = std.fs.File,
40 | .parse_fn = struct{
41 | pub fn parseFile(path: []const u8, _: std.mem.Allocator) !std.fs.File {
42 | var cwd = std.fs.cwd();
43 | return cwd.openFile(path, .{ .lock = .shared }) catch |err| {
44 | std.log.err("The provided path to the File '{s}' is invalid.", .{ path });
45 | return err;
46 | };
47 | }
48 | }.parseFile,
49 | },
50 | },
51 | }
52 | });
53 | ```
54 |
55 | ## Setting up a Value
56 | Similar to Options, Values are designed to be set up within a Command. Specifically, within a Command's `cova.Command.Custom.vals` field. This can be done using a combination of Zig's Union and Anonymous Struct (Tuple) syntax or by using the `cova.Value.ofType`() function.
57 |
58 | Values can be given a Default value using the `cova.Value.Typed.default_val` field as well as an alternate Parsing Function and a Validation Function using the `cova.Value.Typed.parse_fn` and `cova.Value.Typed.valid_fn` fields respectively. An example of how to create an anonymous function for these fields can be seen below. There are also common functions and function builders available within both `cova.Value.ParsingFns` and `cova.Value.ValidationFns`.
59 |
60 | These functions allow for simple and powerful additions to how Values are parsed. For instance, the `true` value for Booleans can be expanded to include more words (i.e. `true = "yes", "y", "on"`), a numeric value can be limited to a certain range of numbers (i.e. `arg > 10 and arg <= 1000`), or an arbitrary string can be converted to something else (i.e. `"eight" = 8`). Moreover, since these functions all follow normal Zig syntax, they can be combined into higher level functions for more complex parsing and validation. Finally, the custom parsing functions in particular allow Custom Types to be parsed directly from a given argument token. For example, converting a given filepath into a `std.fs.File`.
61 |
62 | Example:
63 | ```zig
64 | // Within a Command
65 | ...
66 | .vals = &.{
67 | Value.ofType([]const u8, .{
68 | .name = "str_val",
69 | .description = "A string value for the command.",
70 | }),
71 | // Using Zig's union creation syntax
72 | .{ .generic = .{ .u128, .{
73 | .name = "cmd_u128",
74 | .description = "A u128 value for the command.",
75 | // Default Value
76 | .default_val = 654321,
77 | // Validation Function
78 | .valid_fn = struct{ fn valFn(val: u128) bool { return val > 123 and val < 987654321; } }.valFn,
79 | } } },
80 | }
81 | ```
82 |
83 | ## Additional Info
84 | Values will be parsed to their corresponding types which can then be retrieved using `get()` for Inidivual Values or `getAll()` for Multi-Values.
85 |
--------------------------------------------------------------------------------
/docs/guides/getting_started/install.md:
--------------------------------------------------------------------------------
1 | # Install
2 | ## Package Manager
3 | 1. Find the latest `v#.#.#` commit [here](https://github.com/00JCIV00/cova/commits/main).
4 | 2. Copy the full SHA for the commit.
5 | 3. Add the dependency to `build.zig.zon`:
6 | ```bash
7 | zig fetch --save "https://github.com/00JCIV00/cova/archive/.tar.gz"
8 | ```
9 | 4. Add the dependency and module to `build.zig`:
10 | ```zig
11 | // Cova Dependency
12 | const cova_dep = b.dependency("cova", .{ .target = target });
13 | // Cova Module
14 | const cova_mod = cova_dep.module("cova");
15 | // Executable
16 | const exe = b.addExecutable(.{
17 | .name = "cova_example",
18 | .root_source_file = .{ .path = "src/main.zig" },
19 | .target = target,
20 | .optimize = optimize,
21 | });
22 | // Add the Cova Module to the Executable
23 | exe.root_module.addImport("cova", cova_mod);
24 | ```
25 |
26 | ## Package Manager - Alternative
27 | Note, this method makes Cova easier to update by simply re-running `zig fetch --save https://github.com/00JCIV00/cova/archive/[BRANCH].tar.gz`. However, it can lead to non-reproducible builds because the url will always point to the newest commit of the provided branch. Details can be found in [this discussion](https://ziggit.dev/t/feature-or-bug-w-zig-fetch-save/2565).
28 | 1. Choose a branch to stay in sync with.
29 | - `main` is the latest stable branch.
30 | - The highest `v#.#.#` is the development branch.
31 | 2. Add the dependency to `build.zig.zon`:
32 | ```shell
33 | zig fetch --save https://github.com/00JCIV00/cova/archive/[BRANCH FROM STEP 1].tar.gz
34 | ```
35 | 3. Continue from Step 4 above.
36 |
37 |
38 | ## Build the Basic-App Demo from source
39 | 1. Use Zig v0.12 for your system. Available [here](https://ziglang.org/download/).
40 | 2. Run the following in whichever directory you'd like to install to:
41 | ```
42 | git clone https://github.com/00JCIV00/cova.git
43 | cd cova
44 | zig build basic-app -Doptimize=ReleaseSafe
45 | ```
46 | 3. Try it out!
47 | ```
48 | cd bin
49 | ./basic-app help
50 | ```
51 |
--------------------------------------------------------------------------------
/docs/guides/getting_started/naming_conventions.md:
--------------------------------------------------------------------------------
1 | # Naming Conventions
2 | Cova follows Zig's naming conventions for the most part. This guide covers the few places where it deviates and certain conventions that may be peculiar in the Library and/or Guide.
3 |
4 | ## Argument Type vs Argument
5 | The terms 'Argument Type' and 'Argument' are used throughout this guide. While the terms are related, they're not inter-changeable.
6 |
7 | 'Argument Type' refers to the Types `cova.Command.Custom`, `cova.Option.Custom`, and `cova.Value.Custom`. These are the base Types of Cova which can be configured to apply customizations to all of their respective instances. For brevity, these are also referred to as `CommandT`, `OptionT`, and `ValueT`.
8 |
9 | Conversely, `Argument` refers to any instance of those Types, which can also be referenced individualy as a Command, Option, or Value.
10 |
11 | ## Callback Functions
12 | Cova uses Callback Functions to allow the library user to overwrite or otherwise customize certain aspects of the argument parsing process. The functions are represented as fields within both the Argument Type Config Structs and Instances. They're always some variation of the Type `?*const fn()` (with varying parameters and Return Types) and end with the `_fn` suffix to distinguish them from both other fields and normal functions.
13 |
14 | ## Functions vs Methods
15 | While Zig doesn't officially have methods, it does have 'Member Functions'. These are functions that belong to a Type and whose first parameter is an instance or pointer of that Type. They work effectively the same way as methods in other languages, so the term 'method' is used in this guide for both brevity and clarity.
16 |
17 | ## Global & Child Type Prefixes
18 | Some fields are shared between both the Argument Type Config Structs and their Instances. These fields all share the purpose of overwriting some default property with varying levels of precedence as denoted by their prefixes:
19 | 1. No Prefix: These will always be found directly on the Argument they apply to and have the highest precedence. They apply only to that Argument.
20 | 2. `child_type_`: These are only found within Value Config Structs and have the 2nd highest precedence. They apply to any Values with the corresponding Child Type.
21 | 3. `global_`: These will always be found within a Config Struct and have lowest precedence. They apply to all Arguments of the configured Type.
22 |
23 | ## Title Case
24 | Types and certain Key Concepts will be shown in Title Case for distinction. For Types, this includes their descriptions, such as 'Command Type', and their Type Names like in 'CommandT'.
25 |
26 | ## Users
27 | This guide makes reference to both 'Library' and 'End' users. 'Library user' refers to the developer using the Cova library (probably you, the reader) and 'End user' refers to the user of an application or project built with the help of the Cova library.
28 |
29 |
--------------------------------------------------------------------------------
/docs/guides/getting_started/quick_setup.md:
--------------------------------------------------------------------------------
1 | # Quick Setup
2 | - This is a minimum working demo of Cova integrated into a project.
3 |
4 | ```zig
5 | const std = @import("std");
6 | const cova = @import("cova");
7 | const CommandT = cova.Command.Base();
8 |
9 | pub const ProjectStruct = struct {
10 | pub const SubStruct = struct {
11 | sub_uint: ?u8 = 5,
12 | sub_string: []const u8,
13 | },
14 |
15 | subcmd: SubStruct = .{},
16 | int: ?i4 = 10,
17 | flag: ?bool = false,
18 | strings: [3]const []const u8 = .{ "Three", "default", "strings." },
19 | };
20 |
21 | const setup_cmd = CommandT.from(ProjectStruct);
22 |
23 | pub fn main() !void {
24 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
25 | defer arena.deinit();
26 | const alloc = arena.allocator();
27 | const stdout = std.io.getStdOut().writer();
28 |
29 | const main_cmd = try setup_cmd.init(alloc, .{});
30 | defer main_cmd.deinit();
31 |
32 | var args_iter = try cova.ArgIteratorGeneric.init(alloc);
33 | defer args_iter.deinit();
34 |
35 | cova.parseArgs(&args_iter, CommandT, &main_cmd, stdout, .{}) catch |err| switch(err) {
36 | error.UsageHelpCalled,
37 | else => return err,
38 | }
39 | try cova.utils.displayCmdInfo(CommandT, &main_cmd, alloc, stdout);
40 | }
41 | ```
42 |
43 | ## Breakdown
44 | This is a detailed explanation of the code above.
45 | - Imports
46 | ```zig
47 | ...
48 | // The main cova library module. This is added via `build.zig` & `build.zig.zon` during
49 | // installation.
50 | const cova = @import("cova");
51 |
52 | // The Command Type for all Commands in this project.
53 | const CommandT = cova.Command.Base();
54 | ...
55 | ```
56 |
57 | - A Valid Project Struct. The rules for what makes a Valid struct and how they're converted into Commands can be found in the API Documentation under `cova.Command.Custom.from()`.
58 | ```zig
59 | ...
60 | // This comptime struct is valid to be parsed into a cova Command.
61 | pub const ProjectStruct = struct {
62 | // This nested struct is also valid.
63 | pub const SubStruct = struct {
64 | // Optional Primitive type fields will be converted into cova Options.
65 | // By default, Options will be given a long name and a short name based on the
66 | // field name. (i.e. int = `-i` or `--int`)
67 | sub_uint: ?u8 = 5,
68 |
69 | // Primitive type fields will be converted into Values.
70 | sub_string: []const u8,
71 | },
72 |
73 | // Struct fields will be converted into cova Commands.
74 | subcmd: SubStruct = .{},
75 |
76 | // The default values of Primitive type fields will be applied as the default value
77 | // of the converted Option or Value.
78 | int: ?i4 = 10,
79 |
80 | // Optional Booleans will become cova Options that don't take a Value and are set to
81 | // `true` simply by calling the Option's short or long name.
82 | flag: ?bool = false,
83 |
84 | // Arrays will be turned into Multi-Values or Multi-Options based on the array's
85 | // child type.
86 | strings: [3]const []const u8 = .{ "Three", "default", "strings." },
87 | };
88 | ...
89 | ```
90 |
91 | - Creating the Comptime Command.
92 | ```zig
93 | ...
94 | // `from()` method will convert the above Struct into a Command.
95 | // This must be done at Comptime for proper Validation before Initialization to memory
96 | // for Runtime use.
97 | const setup_cmd = CommandT.from(ProjectStruct);
98 | ...
99 | ```
100 |
101 | - Command Validation and Initialization to memory for Runtime Use.
102 | ```zig
103 | ...
104 | pub fn main() !void {
105 | ...
106 |
107 | // The `init()` method of a Command instance will Validate the Command's
108 | // Argument Types for correctness and distinct names, then it will return a
109 | // memory allocated copy of the Command for argument token parsing and
110 | // follow on analysis.
111 | const main_cmd = try setup_cmd.init(alloc, .{});
112 | defer main_cmd.deinit();
113 |
114 | ...
115 | }
116 | ```
117 |
118 | - Set up the Argument Iterator.
119 | ```zig
120 | pub fn main() {
121 | ...
122 |
123 | // The ArgIteratorGeneric is used to step through argument tokens.
124 | // By default (using `init()`), it will provide Zig's native, cross-platform ArgIterator
125 | // with end user argument tokens. There's also cova's RawArgIterator that can be used to
126 | // parse any slice of strings as argument tokens.
127 | var args_iter = try cova.ArgIteratorGeneric.init(alloc);
128 | defer args_iter.deinit();
129 |
130 | ...
131 | }
132 | ```
133 |
134 | - Parse argument tokens and Display the result.
135 | ```zig
136 | pub fn main() !void {
137 | ...
138 |
139 | // The `parseArgs()` function will parse the provided ArgIterator's (`&args_iter`)
140 | // tokens into Argument Types within the provided Command (`main_cmd`).
141 | try cova.parseArgs(&args_iter, CommandT, &main_cmd, stdout, .{});
142 |
143 | // Once parsed, the provided Command will be available for analysis by the
144 | // project code. Using `utils.displayCmdInfo()` will create a neat display
145 | // of the parsed Command for debugging.
146 | try utils.displayCmdInfo(CommandT, &main_cmd, alloc, stdout);
147 | }
148 | ```
149 |
--------------------------------------------------------------------------------
/docs/guides/overview.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | Commands, Options, Values, Arguments. A simple yet robust command line argument parsing library for Zig.
4 |
5 | Cova is based on the idea that arguments will fall into one of three Argument Types: Commands, Options, or Values. These Types are assembled into a single Command struct which is then used to parse argument tokens.
6 |
7 | This guide is a Work in Progress, but is intended to cover everything from how to install the cova libary into a projectx, to basic setup and many of the library's advanced features. For a more direct look at the library in action, checkout `examples/covademo.zig`, `examples/basic-app.zig`, and the tests in `src/cova.zig` where many of the examples are lifted directly from.
8 |
9 | While this guide does cover several aspects of the libary, everything that's needed to get up and running can be found in the Getting Started section.
10 |
--------------------------------------------------------------------------------
/docs/guides/parsing_analysis/aliases.md:
--------------------------------------------------------------------------------
1 | # Aliases
2 | Cova allows aliasing in two areas: Commands & Options and Value Child Types.
3 |
4 | ## Command & Option Aliases
5 | Command aliases are created with the `cova.Command.Custom.alias_names` field and can be used to allow more than one name to be recognized as the same Command. Similarly, Option aliases are created with the `cova.Option.Custom.alias_long_names` field and can be used to allow more than one long name to be recognized as the same Option. These aliases are validated during initialization to ensure they don't conflict with other Commands/Options or their respective aliases.
6 |
7 | Example:
8 | ```zig
9 | pub const setup_cmd: CommandT = .{
10 | .name = "covademo",
11 | .description = "A demo of the Cova command line argument parser.",
12 | .sub_cmds = &.{
13 | .{
14 | .name = "sub-cmd",
15 | .description = "A demo sub command.",
16 | .alias_names = &.{ "alias-cmd", "test-alias" },
17 | }
18 | },
19 | .opts = &.{
20 | .{
21 | .name = "toggle_opt",
22 | .description = "A toggle/boolean option.",
23 | .short_name = 't',
24 | .long_name = "toggle",
25 | .alias_long_names = &.{ "switch", "bool" },
26 | },
27 | }
28 | };
29 | ```
30 |
31 | ## Value Child Type Aliases
32 | Aliases can also be created for the Child Types of Values, allowing Usage and Help messages to be changed without changing the actual underlying Type. This can be done with either `cova.Value.Typed.alias_child_type` for a single Value or `cova.Value.Config.child_type_aliases` for all Values with a specific Child Type. For instance, Values with a `[]const u8` can be changed to show `text` instead using the following set up:
33 | ```zig
34 | pub const CommandT = cova.Command.Custom(.{
35 | .val_config = .{
36 | .child_type_aliases = &.{
37 | .{
38 | .ChildT = []const u8,
39 | .alias = "text",
40 | },
41 | },
42 | },
43 | });
44 | ```
45 |
46 | Or a single Value with a Child Type of `i8` can be changed to show `number` like so:
47 | ```zig
48 | .val = ValueT.ofType(i8, .{
49 | .name = "num_val",
50 | .description = "A number value.",
51 | .default_val = 42,
52 | .alias_child_type = "number",
53 | }),
54 | ```
55 |
--------------------------------------------------------------------------------
/docs/guides/parsing_analysis/analysis.md:
--------------------------------------------------------------------------------
1 | # Analysis
2 | Once initialized and parsed, a Command can be analyzed. In the context of Cova, Analysis refers to dealing with the result of parsed Argument Types. This can range from simply debugging the results, to checking if an Argument Type was set, to utilizing the resulting values in a project. The Command Type has several functions and methods to make this easier, with methods for checking and matching sub-Commands being the standard starting point. Addtionally, it's possible to convert the Command into a comptime-known Struct, Union, or Function Type and use the resulting Type normally. For a more direct look, all of the sub-Arguments of a Command can also be analyzed individually.
3 |
4 | ## Checking and Matching Sub Commands
5 | The `cova.Command.Custom.checkSubCmd`() and `cova.Command.Custom.matchSubCmd`() methods are designed to be the starting point for analysis. The check function simply returns a Boolean value based on a check of whether or not the provided Command name (`cmd_name`) is the same as the Command's active sub-Command. The match function works similarly, but will return the active sub-Command if it's matched or `null` otherwise. Chaining these methods into conditional `if/else` statements makes iterating over and analyzing all sub-Commands of each Command simple and easy, even when done recursively.
6 |
7 | For a detailed example of these methods in action, refer to the [Basic-App](https://github.com/00JCIV00/cova/blob/main/examples/basic_app.zig) demo under the `// - Handle Parsed Commands` comment in the `main()` function.
8 |
9 | Of note, there is also the `cova.Command.Custom.SubCommandsEnum`() method which will create an Enum of all of the sub-Commands of a given Command. Unfortunately, the Command this is called from must be comptime-known, making it cumbersome to use in all but the most basic of cases. For the time being, the check and match methods above should be preferred.
10 |
11 | ## Conversions
12 | ### To a Struct or Union
13 | Once a Command has been initialized and parsed to, using the `cova.Command.Custom.to`() method will convert it into a struct or union of a comptime-known Struct or Union Type. The function takes a valid comptime-known Struct or Union Type (`ToT`) and a ToConfig (`to_config`). Details for the method, including the rules for a valid Struct or Union Type, can be found under `cova.Command.Custom.to`(). Once sucessfully created, the new struct or union can be used normally throughout the rest of the project. This process looks as follows:
14 | ```zig
15 | const DemoStruct {
16 | // Valid field values
17 | ...
18 | };
19 |
20 | ...
21 |
22 | pub fn main() !void {
23 | ...
24 | const main_cmd = ...;
25 | // Parse into the initialized Command
26 | ...
27 |
28 | // Convert to struct
29 | const demo_struct = main_cmd.to(DemoStruct, .{});
30 |
31 | // Use the struct normally
32 | some_fn(demo_struct);
33 | }
34 |
35 | ```
36 |
37 | The `cova.Command.Custom.ToConfig` can be used to specify how the Command will be converted to a struct.
38 |
39 | ### To a Function
40 | Alternatively, the Command can also be called as a comptime-known function using `cova.Command.Custom.callAs`(). This method takes a function (`call_fn`), an optional self parameter for the function (`fn_self`), and the return Type of the function (`ReturnT`) to call the function using the Command's Arguments as the parameters. Example:
41 | ```zig
42 | pub fn projectFn(some: anytype, params: []const u8) void {
43 | _ = some;
44 | _ = params;
45 | }
46 |
47 | ...
48 |
49 | pub fn main() !void {
50 | ...
51 | const main_cmd = ...;
52 | // Parse into the initialized Command
53 | ...
54 |
55 | // Call as a Function
56 | main_cmd.callAs(projectFn, null, void);
57 | }
58 |
59 | ```
60 |
61 | ## Checking and Matching Options
62 | There are a few ways to analyze Options as well, which include the `cova.Command.Custom.checkOpts`() and `cova.Command.Custom.matchOpts`() methods. These work similarly to their sub-Command counterparts detailed above. Additionally, the `cova.Command.Custom.OptionsCheckConfig` can be passed to these methods to change the kind of boolean logic they use.
63 |
64 | ## Direct Access
65 | To directly access the sub-Argument of a Command the following fields and methods of `cova.Command.Custom` can be used:
66 | ### Fields
67 | - `sub_cmd`: Access the sub Command of this Command if set.
68 | - `opts`: Access the Options of this Command if any.
69 | - `vals`: Access the Values of this Command if any.
70 |
71 | ### Methods
72 | - `checkFlag()`: Check if a Command or Boolean Option/Value is set for this Command.
73 | - `getOpts()` / `getOptsAlloc`: Get a String Hashmap of all of the Options in this Command as ``.
74 | - `getVals()` / `getValsAlloc`: Get a String Hashmap of all of the Values in this Command as ``.
75 |
76 | ### Examples
77 | Check the `cova.utils.displayCmdInfo`() and `cova.utils.displayValInfo`() functions for examples of direct access.
78 |
--------------------------------------------------------------------------------
/docs/guides/parsing_analysis/arg_groups.md:
--------------------------------------------------------------------------------
1 | # Argument Groups
2 | Argument Groups can be used to organize Arguments based on similarities between them. For instance Commands might be organized into different categories for what they do while Options and Values can be grouped based on the kind of data they provide. These groups will be validated during the initialization to ensure that each group assigned to an Argument exists within the respective Group List for that Argument's parent Command.
3 |
4 | Example:
5 | ```zig
6 | pub const setup_cmd: CommandT = .{
7 | .name = "covademo",
8 | .description = "A demo of the Cova command line argument parser.",
9 | .cmd_groups = &.{ "RAW", "STRUCT-BASED", "FN-BASED" },
10 | .opt_groups = &.{ "INT", "BOOL", "STRING" },
11 | .val_groups = &.{ "INT", "BOOL", "STRING" },
12 | .sub_cmds_mandatory = false,
13 | .mandatory_opt_groups = &.{ "BOOL" },
14 | .sub_cmds = &.{
15 | .{
16 | .name = "sub-cmd",
17 | .description = "A demo sub command.",
18 | .cmd_group = "RAW",
19 | },
20 | .{
21 | .name = "basic",
22 | .description = "The most basic Command.",
23 | .cmd_group = "RAW",
24 | },
25 | CommandT.from(DemoStruct, .{
26 | .cmd_name = "struct-cmd",
27 | .cmd_description = "A demo sub command made from a struct.",
28 | .cmd_group = "STRUCT-BASED",
29 | }),
30 | CommandT.from(DemoUnion, .{
31 | .cmd_name = "union-cmd",
32 | .cmd_description = "A demo sub command made from a union.",
33 | .cmd_group = "STRUCT-BASED",
34 | }),
35 | CommandT.from(@TypeOf(demoFn), .{
36 | .cmd_name = "fn-cmd",
37 | .cmd_description = "A demo sub command made from a function.",
38 | .cmd_group = "FN-BASED",
39 | }),
40 | CommandT.from(ex_structs.add_user, .{
41 | .cmd_name = "add-user",
42 | .cmd_description = "A demo sub command for adding a user.",
43 | .cmd_group = "STRUCT-BASED",
44 | }),
45 | },
46 | .opts = &.{
47 | .{
48 | .name = "string_opt",
49 | .description = "A string option. (Can be given up to 4 times.)",
50 | .opt_group = "STRING",
51 | .short_name = 's',
52 | .long_name = "string",
53 | .val = ValueT.ofType([]const u8, .{}),
54 | },
55 | .{
56 | .name = "int_opt",
57 | .description = "An integer option. (Can be given up to 10 times.)",
58 | .opt_group = "INT",
59 | .short_name = 'i',
60 | .long_name = "int",
61 | .val = ValueT.ofType(i16, .{}),
62 | },
63 | },
64 | .vals = &.{
65 | ValueT.ofType([]const u8, .{
66 | .name = "cmd_str",
67 | .description = "A string value for the command.",
68 | .val_group = "STRING",
69 | }),
70 | ValueT.ofType(bool, .{
71 | .name = "cmd_bool",
72 | .description = "A boolean value for the command.",
73 | .val_group = "BOOL",
74 | }),
75 | }
76 | };
77 | ```
78 |
79 | ## Usage & Help Messages
80 | If these groups are used, they will be shown in Usage and Help Messages. Their are two Format fields, `cova.Command.Config.group_title_fmt` and `cova.Command.Config.group_sep_fmt`, that can be used to customize how the groups are displayed.
81 |
82 | ## Parsing
83 | Argument Groups can be used to mandate certain groups of Options are used by setting the `cova.Command.Custom.mandatory_opt_groups` field.
84 |
85 | Example:
86 | ```zig
87 | pub const setup_cmd: CommandT = .{
88 | .name = "covademo",
89 | .description = "A demo of the Cova command line argument parser.",
90 | .opt_groups = &.{ "INT", "BOOL", "STRING" },
91 | .mandatory_opt_groups = &.{ "BOOL" },
92 | .opts = &.{
93 | .{
94 | .name = "cardinal_opt",
95 | .description = "A cardinal number option.",
96 | .opt_group = "INT",
97 | .short_name = 'c',
98 | .long_name = "cardinal",
99 | .val = ValueT.ofType(u8, .{}),
100 | },
101 | .{
102 | .name = "toggle_opt",
103 | .description = "A toggle/boolean option.",
104 | .opt_group = "BOOL",
105 | .short_name = 't',
106 | .long_name = "toggle",
107 | },
108 | .{
109 | .name = "bool_opt",
110 | .description = "A toggle/boolean option.",
111 | .opt_group = "BOOL",
112 | .short_name = 'b',
113 | .long_name = "bool",
114 | },
115 | },
116 | };
117 | ```
118 |
119 | ## Analysis
120 | They can also be used to Get, Check, or Match the Values and Options of a Command. For Options, an Option Group can be passed to `cova.Command.Custom.getOpts`(), `cova.Command.Custom.checkOpt`(), or `cova.Command.Custom.matchOpt`(). For Values, a Value Group can be passed to `cova.Command.Custom.getVals`().
121 |
--------------------------------------------------------------------------------
/docs/guides/parsing_analysis/parsing.md:
--------------------------------------------------------------------------------
1 | # Parsing
2 | Parsing is handled by the `cova.parseArgs`() function. It takes in a pointer to an ArgIterator (`args`), a Command type (`CommandT`), a pointer to an initialized Command (`cmd`), a Writer (`writer`), and a ParseConfig (`parse_config`), then parses each argument token sequentially. The results of a successful parse are stored in the provided Command (`cmd`) which can then be analyzed by the library user's project code.
3 |
4 | Notably, the `cova.parseArgs`() function can return several errorrs, most of which (especially `error.UsageHelpCalled`) can be safely ignored when using the default behavior. This is demonstrated below.
5 |
6 | ## Default Setup
7 | For the default setup, all that's needed is a pointer to an initialized `cova.ArgIteratorGeneric` (`&args_iter`), the project's Command Type (`CommandT`), a pointer to an initialized Command (`&main_cmd`), a Writer to stdout (`stdout`), and the default `ParseConfig` (`.{}`) as shown here:
8 |
9 | ```zig
10 | const cova = @import("cova");
11 |
12 | // Command Type
13 | const CommandT = cova.Command.Custom(.{});
14 |
15 | // Comptime Setup Command
16 | const setup_cmd: CommandT = .{ ... };
17 |
18 | pub fn main() !void {
19 | // Allocator
20 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
21 | defer arena.deinit();
22 | const alloc = arena.allocator();
23 |
24 | // Command
25 | // Note, the Command Type and `setup_cmd` are created during comptime before the `main()` function.
26 | const main_cmd = try setup_cmd.init(alloc, .{});
27 | defer main_cmd.deinit();
28 |
29 | // Argument Iterator
30 | var args_iter = try cova.ArgIteratorGeneric.init(alloc);
31 | defer args_iter.deinit();
32 |
33 | // Writer to stdout
34 | const stdout = std.io.getStdOut().writer();
35 |
36 | // Parse Function
37 | cova.parseArgs(&args_iter, CommandT, &main_cmd, stdout, .{}) catch |err| switch (err) {
38 | error.UsageHelpCalled,
39 | else => return err,
40 | };
41 | }
42 | ```
43 |
44 | ## Custom Setup
45 | ### Choosing an ArgIterator
46 | There are two implementations to choose from within `cova.ArgIteratorGeneric`: `.zig` and `.raw`.
47 | - `.zig`: This implementation uses a `std.process.ArgIterator`, which is the default, cross-platform ArgIterator for Zig. It should be the most common choice for normal argument token parsing since it pulls the argument string from the process and tokenizes it into iterable arguments. Setup is handled by the `.init()` method as shown above.
48 | - `.raw`: This implementation uses the `cova.RawArgIterator` and is intended for testing, but can also be useful parsing externally sourced argument tokens. "Externally sourced" meaning argument tokens that aren't provided by the process from the OS or Shell when the project application is run. It's set up as follows:
49 | ```zig
50 | const test_args: []const [:0]const u8 = &.{ "test-cmd", "--string", "opt string 1", "-s", "opt string 2", "--int=1,22,333,444,555,666", "--flo", "f10.1,20.2,30.3", "-t", "val string", "sub-test-cmd", "--sub-s=sub_opt_str", "--sub-int", "21523", "help" };
51 | var raw_iter = RawArgIterator{ .args = test_args };
52 | var test_iter = ArgIteratorGeneric.from(raw_iter);
53 | try parseArgs(&test_iter...);
54 | ```
55 |
56 | #### Tokenization
57 | As mentioned, the `std.process.ArgIterator` tokenizes its arguments automatically. However, if the `cova.RawArgIterator` is needed, then the `cova.tokenizeArgs`() function can be used to convert an argument string (`[]const u8`) into a slice of argument token strings (`[]const []const u8`). This slice can then be provided to `cova.RawArgIterator`. The `cova.TokenizeConfig` can be used to configure how the argument string is tokenized. Example:
58 | ```zig
59 | var arena = std.heap.ArenaAllocator.init(testing.allocator);
60 | defer arena.deinit();
61 | const alloc = arena.allocator();
62 | const arg_str = "cova struct-cmd --multi-str \"demo str\" -m 'a \"quoted string\"' -m \"A string using an 'apostrophe'\" 50";
63 | const test_args = try tokenizeArgs(arg_str, alloc, .{});
64 | ```
65 |
66 | ### Creating a Command Type and a Command
67 | As described above, the parsing process relies on the creation of a Command Type and a main Command. The specifics for this can be found under `cova.Command` in the API and the [Command Guide](../arg_types/command) in the Guides.
68 |
69 | The basic steps are:
70 | 1. Configure a Command Type.
71 | 2. Create a comptime-known Command.
72 | 3. Initialize the comptime-known Command for runtime-use.
73 |
74 | ### Setting up a Writer
75 | The Writer is used to output Usage/Help messages to the end user in the event of an error during parsing. The standard is to use a Writer to `stdout` or `stderr` for this as shown above. However, a Writer to a different file can also be used to avoid outputting to the end user as shown here:
76 | ```zig
77 | var arena = std.heap.ArenaAllocator.init(testing.allocator);
78 | defer arena.deinit();
79 | const alloc = arena.allocator();
80 |
81 | var writer_list = std.ArrayList(u8).init(alloc);
82 | defer writer_list.deinit();
83 | const writer = writer_list.writer();
84 | ```
85 |
86 | ### Parsing Configuration
87 | The `cova.ParseConfig` allows for several configurations pertaining to how argument tokens are parsed.
88 |
89 | ### Custom Parsing & Validation Functions
90 | #### Parsing Functions
91 | Values and, by extension, Options can be given custom functions for parsing from an argument token to the Value's Child Type. These functions will take precedence over the default parsing and can be given at two levels, directly to the Value and to all Value's with a specific Child Type. Regardless of which level these functions are provided at, they must follow the same general structure. The first parameters must be a `[]const u8` for the argument token and the second must be a `std.mem.Allocator` that will be provided by Cova based on the Allocator given during Command Initialization (though this can be skipped using `_: std.mem.Allocator`). Finally, the Return Type must be an Error Union with the Value's Child Type.
92 |
93 | 1. `Value.Typed.parse_fn` is the field used to provide a parsing function directly to a Value as it's being set up. This function has the highest priority and is used as follows:
94 | ```zig
95 | //Within a Command
96 | .vals = &.{
97 | ValueT.ofType(bool, .{
98 | .name = "pos_or_neg",
99 | .description = "An example boolean that must be set as 'positive' or 'negative'.",
100 | .parse_fn = struct{
101 | pub fn parseBool(arg: []const u8, _: std.mem.Allocator) !bool {
102 | if (std.ascii.eqlIgnoreCase(arg, "positive") return true;
103 | if (std.ascii.eqlIgnoreCase(arg, "negative") return false;
104 | else return error.BoolParseError;
105 | }
106 | }.parseBool,
107 | }),
108 | },
109 | ```
110 | 2. `Value.Config.child_type_parse_fn` is the field used to provide a parsing function to all Value's that have a specific Child Type. These functions rank second in priority, behind `Value.Typed.parse_fn` but ahead of the default parsing. An example can be seen [here](../arg_types/value.md#adding-custom-child-types).
111 |
112 | #### Validation Functions
113 | Validation Functions are set up very similarly to Parsing Functions. They differ in that they're used to validate a Value after it's been parsed to its Child Type. As such, their structure differs by requiring the first parameter to be an instance of the Value's Child Type and the Return Type to be an Error Union with a Boolean. Notably, these functions can only be applied directly to a Value.
114 |
115 | Example:
116 | ```zig
117 | // Within a Command
118 | .opts = &.{
119 | .{
120 | .name = "verbosity_opt",
121 | .description = "Set the CovaDemo verbosity level. (WIP)",
122 | .short_name = 'v',
123 | .long_name = "verbosity",
124 | .val = ValueT.ofType(u4, .{
125 | .name = "verbosity_level",
126 | .description = "The verbosity level from 0 (err) to 3 (debug).",
127 | .default_val = 3,
128 | .valid_fn = struct{ fn valFn(val: u4, _: mem.Allocator) bool { return val >= 1 and val <= 3; } }.valFn,
129 | }),
130 | },
131 | },
132 | ```
133 |
--------------------------------------------------------------------------------
/docs/guides/parsing_analysis/usage_help.md:
--------------------------------------------------------------------------------
1 | # Usage & Help Message
2 | By default, Usage and Help messages are created based on the metadata of Arguments, and are displayed when there's an error during Parsing. Setting up the display for Usage and Help messages is as simple as setting up each Argument as seen in their respective guides. That said, the way they are created and the criteria for when they're displayed can be fully customized by the libary user.
3 |
4 | ## Parsing Error Messages
5 | The `cova.ParseConfig.err_reaction` field is used to help control what the end user sees if there's an error during parsing. In particular, it allows for a Usage message, a Help message, or no message at all to be displayed following an error.
6 |
7 | ## Argument Usage & Help Functions
8 | Each Argument Type has `usage()` and `help()` functions that can be called on an Argument to create messages. The most commonly used of these functions are `cova.Command.Custom.usage`() and `cova.Command.Custom.help`() which create and write out a Usage or Help message based on the Command's sub Arguments.
9 |
10 | ## Custom Usage & Help Formats
11 | The Config for each Argument Type provides Format fields with the `_fmt` suffix that can be used to customize how Usage and Help messages are displayed. These Format fields are each documented with their required format placeholders (details for those can be found in `std.fmt.format`).
12 |
13 | Example:
14 | ```zig
15 | pub const CommandT = cova.Command.Custom(.{
16 | .global_help_prefix="Cova Usage & Help Example",
17 | .help_header_fmt =
18 | \\ {s}COMMAND: {s}
19 | \\
20 | \\ {s}DESCRIPTION: {s}
21 | \\
22 | \\
23 | ,
24 | .opt_config = .{
25 | usage_fmt = "{c}{?c}, {s}{?s} <{s} ({s})>",
26 | },
27 | });
28 | ```
29 |
30 | ## Custom Usage & Help Callback Functions
31 | For greater flexibiliy, custom Usage & Help callback functions can be provided for each Argument Type. These functions can be given directly to an Argument, to each Value or Option with a specific Child Type, or globally to all Arguments of an Argument Type; in that order of precendence.
32 |
33 | Example:
34 | ```zig
35 | pub const CommandT = cova.Command.Custom(.{
36 | .global_help_prefix="Cova Usage & Help Example",
37 | // Command Global Help Function
38 | .global_help_fn = struct{
39 | fn help(self: anytype, writer: anytype, _: mem.Allocator) !void {
40 | const CmdT = @TypeOf(self.*);
41 | const OptT = CmdT.OptionT;
42 | const indent_fmt = CmdT.indent_fmt;
43 |
44 | try writer.print("{s}\n", .{ self.help_prefix });
45 | try self.usage(writer);
46 | try writer.print("\n", .{});
47 | try writer.print(CmdT.help_header_fmt, .{
48 | indent_fmt, self.name,
49 | indent_fmt, self.description
50 | });
51 |
52 | if (self.sub_cmds) |cmds| {
53 | try writer.print("SUBCOMMANDS\n", .{});
54 | for (cmds) |cmd| {
55 | try writer.print("{s}{s}: {s}\n", .{
56 | indent_fmt,
57 | cmd.name,
58 | cmd.description,
59 | });
60 | }
61 | try writer.print("\n", .{});
62 | }
63 | if (self.opts) |opts| {
64 | try writer.print("OPTIONS\n", .{});
65 | for (opts) |opt| {
66 | try writer.print(
67 | \\{s}{s}{s} "{s} ({s})"
68 | \\{s}{s}{s}
69 | \\
70 | \\
71 | , .{
72 | indent_fmt,
73 | OptT.long_prefix orelse OptT.short_prefix, opt.long_name orelse "",
74 | opt.val.name(), opt.val.childType(),
75 | indent_fmt, indent_fmt,
76 | opt.description,
77 | }
78 | );
79 | }
80 | }
81 | if (self.vals) |vals| {
82 | try writer.print("VALUES\n", .{});
83 | for (vals) |val| {
84 | try writer.print("{s}", .{ indent_fmt });
85 | try val.usage(writer);
86 | try writer.print("\n", .{});
87 | }
88 | try writer.print("\n", .{});
89 | }
90 | }
91 | }.help,
92 | .opt_config = .{
93 | // Option Global Help Function
94 | .global_help_fn = struct{
95 | fn help(self: anytype, writer: anytype, _: mem.Allocator) !void {
96 | const indent_fmt = @TypeOf(self.*).indent_fmt;
97 | try self.usage(writer);
98 | try writer.print("\n{?s}{?s}{?s}{s}", .{ indent_fmt, indent_fmt, indent_fmt, self.description });
99 | }
100 | }.help
101 | },
102 | ```
103 |
104 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Zig Documentation
6 |
7 |
279 |
280 |
281 |
323 |