├── .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 | ![cova_icon_v2 1](https://github.com/00JCIV00/cova/assets/68087632/0b485f6b-ddf4-4772-96eb-899d4606f9cc) 2 | 3 | # Commands **⋅** Options **⋅** Values **⋅** Arguments 4 | A simple yet robust cross-platform command line argument parsing library for Zig. 5 | 6 | [![Static Badge](https://img.shields.io/badge/v0.14.1(stable)-orange?logo=Zig&logoColor=Orange&label=Zig&labelColor=Orange)](https://ziglang.org/download/) 7 | [![Static Badge](https://img.shields.io/badge/v0.10.1b-blue?logo=GitHub&label=Release)](https://github.com/00JCIV00/cova/releases/tag/v0.10.1-beta) 8 | [![GitHub commit activity](https://img.shields.io/github/commits-difference/00JCIV00/cova?base=v0.10.1&head=main&logo=Github&label=Commits%20(v0.11.0b))](https://github.com/00JCIV00/cova/commits/main/) 9 | [![Static Badge](https://img.shields.io/badge/MIT-silver?label=License)](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 | ![cova_demo](./docs/cova_demo.gif) 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 | ![cova_demo](./docs/cova_demo.gif) 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 | 327 |
328 |

Loading...

329 |

[src]

330 | 333 | 334 | 339 | 346 | 350 | 354 | 359 | 364 | 369 | 376 | 383 | 388 | 393 | 397 | 401 |
402 | 412 | 413 | 414 | 415 | 416 | -------------------------------------------------------------------------------- /docs/main.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/00JCIV00/cova/e8c25e57ca9c3dba6de32406168c717845812a71/docs/main.wasm -------------------------------------------------------------------------------- /examples/basic_app_meta/arg_templates/basic-app-template.json: -------------------------------------------------------------------------------- 1 | "Meta Info": { 2 | "name": "basic-app", 3 | "description": "A basic user management application designed to highlight key features of the Cova library.", 4 | "version": "0.10.2", 5 | "ver_date": "23 OCT 2024", 6 | "author": "00JCIV00", 7 | "copyright": "MIT License" 8 | }, 9 | "Arguments": { 10 | "name": "basic-app", 11 | "description": "A basic user management application designed to highlight key features of the Cova library.", 12 | "sub_cmds": [ 13 | { 14 | "name": "new", 15 | "description": "Add a new user.", 16 | "group": "INTERACT", 17 | "examples": [ 18 | "basic-app new -f Bruce -l Wayne -a 40 -p \"555 555 5555\" -A \" 1007 Mountain Drive, Gotham\" true" 19 | ], 20 | "opts": [ 21 | { 22 | "name": "first-name", 23 | "long_name": "first-name", 24 | "short_name": 102, 25 | "description": "User's First Name.", 26 | "type_name": "[]const u8", 27 | "set_behavior": "Last", 28 | "max_entries": 1 29 | }, 30 | { 31 | "name": "last-name", 32 | "long_name": "last-name", 33 | "short_name": 108, 34 | "description": "User's Last Name.", 35 | "type_name": "[]const u8", 36 | "set_behavior": "Last", 37 | "max_entries": 1 38 | }, 39 | { 40 | "name": "age", 41 | "long_name": "age", 42 | "short_name": 97, 43 | "description": "User's Age.", 44 | "type_name": "u8", 45 | "set_behavior": "Last", 46 | "max_entries": 1 47 | }, 48 | { 49 | "name": "phone", 50 | "long_name": "phone", 51 | "short_name": 112, 52 | "description": "User's Phone #.", 53 | "type_name": "[]const u8", 54 | "set_behavior": "Last", 55 | "max_entries": 1 56 | }, 57 | { 58 | "name": "address", 59 | "long_name": "address", 60 | "short_name": 65, 61 | "description": "User's Address.", 62 | "type_name": "[]const u8", 63 | "set_behavior": "Last", 64 | "max_entries": 1 65 | } 66 | ], 67 | "vals": [ 68 | { 69 | "name": "is-admin", 70 | "description": "Add this user as an admin?", 71 | "type_name": "bool", 72 | "set_behavior": "Last", 73 | "max_entries": 1 74 | } 75 | ] 76 | }, 77 | { 78 | "name": "open", 79 | "description": "Open or create a users file.", 80 | "group": "INTERACT", 81 | "examples": [ 82 | "basic-app open users.csv" 83 | ], 84 | "vals": [ 85 | { 86 | "name": "__nosubdescriptionsprovided__", 87 | "description": "", 88 | "type_name": "[]const u8", 89 | "set_behavior": "Last", 90 | "max_entries": 1 91 | } 92 | ] 93 | }, 94 | { 95 | "name": "list", 96 | "description": "List all current users.", 97 | "group": "VIEW", 98 | "sub_cmds": [ 99 | { 100 | "name": "filter", 101 | "description": "List all current users matching the provided filter. Filters can be exactly ONE of any user field.", 102 | "opts": [ 103 | { 104 | "name": "id", 105 | "long_name": "id", 106 | "short_name": 105, 107 | "description": "The 'id' Option.", 108 | "type_name": "u16", 109 | "set_behavior": "Last", 110 | "max_entries": 1 111 | }, 112 | { 113 | "name": "admin", 114 | "long_name": "admin", 115 | "short_name": 97, 116 | "description": "The 'admin' Option.", 117 | "type_name": "bool", 118 | "set_behavior": "Last", 119 | "max_entries": 1 120 | }, 121 | { 122 | "name": "age", 123 | "long_name": "age", 124 | "short_name": 65, 125 | "description": "The 'age' Option.", 126 | "type_name": "u8", 127 | "set_behavior": "Last", 128 | "max_entries": 1 129 | }, 130 | { 131 | "name": "first-name", 132 | "long_name": "first-name", 133 | "short_name": 102, 134 | "description": "The 'first-name' Option.", 135 | "type_name": "[]const u8", 136 | "set_behavior": "Last", 137 | "max_entries": 1 138 | }, 139 | { 140 | "name": "last-name", 141 | "long_name": "last-name", 142 | "short_name": 108, 143 | "description": "The 'last-name' Option.", 144 | "type_name": "[]const u8", 145 | "set_behavior": "Last", 146 | "max_entries": 1 147 | }, 148 | { 149 | "name": "phone", 150 | "long_name": "phone", 151 | "short_name": 112, 152 | "description": "The 'phone' Option.", 153 | "type_name": "[]const u8", 154 | "set_behavior": "Last", 155 | "max_entries": 1 156 | }, 157 | { 158 | "name": "address", 159 | "long_name": "address", 160 | "short_name": 100, 161 | "description": "The 'address' Option.", 162 | "type_name": "[]const u8", 163 | "set_behavior": "Last", 164 | "max_entries": 1 165 | } 166 | ] 167 | } 168 | ] 169 | }, 170 | { 171 | "name": "clean", 172 | "description": "Clean (delete) the default users file (users.csv) and persistent variable file (.ba_persist).", 173 | "aliases": [ 174 | "delete" 175 | ], 176 | "group": "INTERACT", 177 | "examples": [ 178 | "basic-app clean", 179 | "basic-app delete --file users.csv" 180 | ], 181 | "opts": [ 182 | { 183 | "name": "clean_file", 184 | "long_name": "file", 185 | "short_name": 102, 186 | "description": "Specify a single file to be cleaned (deleted) instead of the defaults.", 187 | "aliases": [ 188 | "delete_file" 189 | ], 190 | "type_name": "[]const u8", 191 | "type_alias": "filepath", 192 | "set_behavior": "Last", 193 | "max_entries": 1 194 | } 195 | ] 196 | }, 197 | { 198 | "name": "view-lists", 199 | "description": "View all lists (csv files) in the current directory.", 200 | "group": "VIEW" 201 | } 202 | ] 203 | } -------------------------------------------------------------------------------- /examples/basic_app_meta/arg_templates/basic-app-template.kdl: -------------------------------------------------------------------------------- 1 | # This KDL template is formatted to match the `usage` tool as detailed here: https://sr.ht/~jdx/usage/ 2 | 3 | name "basic-app" 4 | bin "basic-app" 5 | about "A basic user management application designed to highlight key features of the Cova library." 6 | version "0.10.2" 7 | author "00JCIV00" 8 | 9 | cmd "new" help="Add a new user." { 10 | flag "-f,--first-name" help="User's First Name." 11 | flag "-l,--last-name" help="User's Last Name." 12 | flag "-a,--age" help="User's Age." 13 | flag "-p,--phone" help="User's Phone #." 14 | flag "-A,--address" help="User's Address." 15 | 16 | arg "is-admin" help="Add this user as an admin?" 17 | } 18 | 19 | cmd "open" help="Open or create a users file." { 20 | arg "__nosubdescriptionsprovided__" help="" 21 | } 22 | 23 | cmd "list" help="List all current users." { 24 | cmd "filter" help="List all current users matching the provided filter. Filters can be exactly ONE of any user field." { 25 | flag "-i,--id" help="The 'id' Option." 26 | flag "-a,--admin" help="The 'admin' Option." 27 | flag "-A,--age" help="The 'age' Option." 28 | flag "-f,--first-name" help="The 'first-name' Option." 29 | flag "-l,--last-name" help="The 'last-name' Option." 30 | flag "-p,--phone" help="The 'phone' Option." 31 | flag "-d,--address" help="The 'address' Option." 32 | } 33 | } 34 | 35 | cmd "clean" help="Clean (delete) the default users file (users.csv) and persistent variable file (.ba_persist)." { 36 | alias "delete" 37 | 38 | flag "-f,--file" help="Specify a single file to be cleaned (deleted) instead of the defaults." 39 | } 40 | 41 | cmd "view-lists" help="View all lists (csv files) in the current directory." 42 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/manpages/basic-app-clean.1: -------------------------------------------------------------------------------- 1 | .TH basic-app-clean 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B basic-app-clean 5 | 6 | .SH SYNOPSIS 7 | .B basic-app-clean 8 | .RB [OPTIONS] 9 | 10 | .SH DESCRIPTION 11 | .B Clean (delete) the default users file (users.csv) and persistent variable file (.ba_persist). 12 | .SH ARGUMENTS 13 | .SS OPTIONS 14 | .B clean_file: 15 | [-f,--file "clean_file (filepath)"]: 16 | Specify a single file to be cleaned (deleted) instead of the defaults. 17 | 18 | .SH EXAMPLES 19 | 20 | .B basic-app clean 21 | 22 | .B basic-app delete --file users.csv 23 | 24 | 25 | 26 | .SH AUTHOR 27 | .B 00JCIV00 28 | 29 | .SH COPYRIGHT 30 | .B MIT License 31 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/manpages/basic-app-list-filter.1: -------------------------------------------------------------------------------- 1 | .TH basic-app-list-filter 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B basic-app-list-filter 5 | 6 | .SH SYNOPSIS 7 | .B basic-app-list-filter 8 | .RB [OPTIONS] 9 | 10 | .SH DESCRIPTION 11 | .B List all current users matching the provided filter. Filters can be exactly ONE of any user field. 12 | .SH ARGUMENTS 13 | .SS OPTIONS 14 | .B id: 15 | [-i,--id "id (u16)"]: 16 | The 'id' Option. 17 | 18 | .B admin: 19 | [-a,--admin "admin (bool)"]: 20 | The 'admin' Option. 21 | 22 | .B age: 23 | [-A,--age "age (u8)"]: 24 | The 'age' Option. 25 | 26 | .B first-name: 27 | [-f,--first-name "first-name ([]const u8)"]: 28 | The 'first-name' Option. 29 | 30 | .B last-name: 31 | [-l,--last-name "last-name ([]const u8)"]: 32 | The 'last-name' Option. 33 | 34 | .B phone: 35 | [-p,--phone "phone ([]const u8)"]: 36 | The 'phone' Option. 37 | 38 | .B address: 39 | [-d,--address "address ([]const u8)"]: 40 | The 'address' Option. 41 | 42 | 43 | .SH AUTHOR 44 | .B 00JCIV00 45 | 46 | .SH COPYRIGHT 47 | .B MIT License 48 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/manpages/basic-app-list.1: -------------------------------------------------------------------------------- 1 | .TH basic-app-list 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B basic-app-list 5 | 6 | .SH SYNOPSIS 7 | .B basic-app-list 8 | .RB [SUB COMMAND...] 9 | 10 | .SH DESCRIPTION 11 | .B List all current users. 12 | .SH ARGUMENTS 13 | .SS COMMANDS 14 | .B filter: 15 | List all current users matching the provided filter. Filters can be exactly ONE of any user field. 16 | 17 | 18 | .SH AUTHOR 19 | .B 00JCIV00 20 | 21 | .SH COPYRIGHT 22 | .B MIT License 23 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/manpages/basic-app-new.1: -------------------------------------------------------------------------------- 1 | .TH basic-app-new 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B basic-app-new 5 | 6 | .SH SYNOPSIS 7 | .B basic-app-new 8 | .RB [OPTIONS] 9 | .RB [VALUES] 10 | 11 | .SH DESCRIPTION 12 | .B Add a new user. 13 | .SH ARGUMENTS 14 | .SS OPTIONS 15 | .B first-name: 16 | [-f,--first-name "first-name ([]const u8)"]: 17 | User's First Name. 18 | 19 | .B last-name: 20 | [-l,--last-name "last-name ([]const u8)"]: 21 | User's Last Name. 22 | 23 | .B age: 24 | [-a,--age "age (u8)"]: 25 | User's Age. 26 | 27 | .B phone: 28 | [-p,--phone "phone ([]const u8)"]: 29 | User's Phone #. 30 | 31 | .B address: 32 | [-A,--address "address ([]const u8)"]: 33 | User's Address. 34 | 35 | .SS VALUES 36 | .B is-admin: 37 | (bool): Add this user as an admin? 38 | 39 | .SH EXAMPLES 40 | 41 | .B basic-app new -f Bruce -l Wayne -a 40 -p "555 555 5555" -A " 1007 Mountain Drive, Gotham" true 42 | 43 | 44 | 45 | .SH AUTHOR 46 | .B 00JCIV00 47 | 48 | .SH COPYRIGHT 49 | .B MIT License 50 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/manpages/basic-app-open.1: -------------------------------------------------------------------------------- 1 | .TH basic-app-open 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B basic-app-open 5 | 6 | .SH SYNOPSIS 7 | .B basic-app-open 8 | .RB [VALUES] 9 | 10 | .SH DESCRIPTION 11 | .B Open or create a users file. 12 | .SH ARGUMENTS 13 | .SS VALUES 14 | .B __nosubdescriptionsprovided__: 15 | ([]const u8): 16 | 17 | .SH EXAMPLES 18 | 19 | .B basic-app open users.csv 20 | 21 | 22 | 23 | .SH AUTHOR 24 | .B 00JCIV00 25 | 26 | .SH COPYRIGHT 27 | .B MIT License 28 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/manpages/basic-app-view-lists.1: -------------------------------------------------------------------------------- 1 | .TH basic-app-view-lists 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B basic-app-view-lists 5 | 6 | .SH SYNOPSIS 7 | .B basic-app-view-lists 8 | 9 | .SH DESCRIPTION 10 | .B View all lists (csv files) in the current directory. 11 | 12 | .SH AUTHOR 13 | .B 00JCIV00 14 | 15 | .SH COPYRIGHT 16 | .B MIT License 17 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/manpages/basic-app.1: -------------------------------------------------------------------------------- 1 | .TH basic-app 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B basic-app 5 | 6 | .SH SYNOPSIS 7 | .B basic-app 8 | .RB [SUB COMMAND...] 9 | 10 | .SH DESCRIPTION 11 | .B A basic user management application designed to highlight key features of the Cova library. 12 | .SH ARGUMENTS 13 | .SS COMMANDS 14 | .B new: 15 | Add a new user. 16 | 17 | .B open: 18 | Open or create a users file. 19 | 20 | .B list: 21 | List all current users. 22 | 23 | .B clean: 24 | Clean (delete) the default users file (users.csv) and persistent variable file (.ba_persist). 25 | 26 | .B view-lists: 27 | View all lists (csv files) in the current directory. 28 | 29 | 30 | .SH AUTHOR 31 | .B 00JCIV00 32 | 33 | .SH COPYRIGHT 34 | .B MIT License 35 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/markdown/basic-app-clean.md: -------------------------------------------------------------------------------- 1 | # clean 2 | __[basic-app](./basic-app.md)__ > __clean__ 3 | 4 | Clean (delete) the default users file (users.csv) and persistent variable file (.ba_persist). 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE: 11 | clean -f,--file,--delete_file 12 | clean 13 | 14 | ``` 15 | 16 | ## Alias(es) 17 | - `delete` 18 | 19 | ## Examples 20 | 21 | - `basic-app clean` 22 | - `basic-app delete --file users.csv` 23 | 24 | ## Arguments 25 | ### Options 26 | - __clean_file__: 27 | - `-f, --file, --delete_file ` 28 | - Specify a single file to be cleaned (deleted) instead of the defaults. 29 | 30 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/markdown/basic-app-list-filter.md: -------------------------------------------------------------------------------- 1 | # filter 2 | __[basic-app](./basic-app.md)__ > __[list](./basic-app-list.md)__ > __filter__ 3 | 4 | List all current users matching the provided filter. Filters can be exactly ONE of any user field. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE: 11 | filter -i,--id | -a,--admin | -A,--age | -f,--first-name <[]const u8> | -l,--last-name <[]const u8> | -p,--phone <[]const u8> | -d,--address <[]const u8> 12 | filter 13 | 14 | ``` 15 | 16 | ## Arguments 17 | ### Options 18 | - __id__: 19 | - `-i, --id ` 20 | - The 'id' Option. 21 | - __admin__: 22 | - `-a, --admin ` 23 | - The 'admin' Option. 24 | - __age__: 25 | - `-A, --age ` 26 | - The 'age' Option. 27 | - __first-name__: 28 | - `-f, --first-name ` 29 | - The 'first-name' Option. 30 | - __last-name__: 31 | - `-l, --last-name ` 32 | - The 'last-name' Option. 33 | - __phone__: 34 | - `-p, --phone ` 35 | - The 'phone' Option. 36 | - __address__: 37 | - `-d, --address
` 38 | - The 'address' Option. 39 | 40 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/markdown/basic-app-list.md: -------------------------------------------------------------------------------- 1 | # list 2 | __[basic-app](./basic-app.md)__ > __list__ 3 | 4 | List all current users. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE: 11 | list filter 12 | 13 | ``` 14 | 15 | ## Arguments 16 | ### Commands 17 | - [__filter__](./basic-app-list-filter.md): List all current users matching the provided filter. Filters can be exactly ONE of any user field. 18 | 19 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/markdown/basic-app-new.md: -------------------------------------------------------------------------------- 1 | # new 2 | __[basic-app](./basic-app.md)__ > __new__ 3 | 4 | Add a new user. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE: 11 | new -f,--first-name <[]const u8> | -l,--last-name <[]const u8> | -a,--age | -p,--phone <[]const u8> | -A,--address <[]const u8> 12 | new "is-admin (bool)" 13 | new 14 | 15 | ``` 16 | 17 | ## Examples 18 | 19 | - `basic-app new -f Bruce -l Wayne -a 40 -p "555 555 5555" -A " 1007 Mountain Drive, Gotham" true` 20 | 21 | ## Arguments 22 | ### Options 23 | - __first-name__: 24 | - `-f, --first-name ` 25 | - User's First Name. 26 | - __last-name__: 27 | - `-l, --last-name ` 28 | - User's Last Name. 29 | - __age__: 30 | - `-a, --age ` 31 | - User's Age. 32 | - __phone__: 33 | - `-p, --phone ` 34 | - User's Phone #. 35 | - __address__: 36 | - `-A, --address
` 37 | - User's Address. 38 | ### Values 39 | - __is-admin__ (bool) 40 | - Add this user as an admin? 41 | 42 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/markdown/basic-app-open.md: -------------------------------------------------------------------------------- 1 | # open 2 | __[basic-app](./basic-app.md)__ > __open__ 3 | 4 | Open or create a users file. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE: 11 | open "__nosubdescriptionsprovided__ ([]const u8)" 12 | open 13 | 14 | ``` 15 | 16 | ## Examples 17 | 18 | - `basic-app open users.csv` 19 | 20 | ## Arguments 21 | ### Values 22 | - ____nosubdescriptionsprovided____ ([]const u8) 23 | - 24 | 25 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/markdown/basic-app-view-lists.md: -------------------------------------------------------------------------------- 1 | # view-lists 2 | __[basic-app](./basic-app.md)__ > __view-lists__ 3 | 4 | View all lists (csv files) in the current directory. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE: 11 | view-lists 12 | 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /examples/basic_app_meta/help_docs/markdown/basic-app.md: -------------------------------------------------------------------------------- 1 | # basic-app 2 | A basic user management application designed to highlight key features of the Cova library. 3 | 4 | __Version:__ 0.10.2
5 | __Date:__ 23 OCT 2024
6 | __Author:__ 00JCIV00
7 | __Copyright:__ MIT License
8 | ___ 9 | 10 | ## Usage 11 | ```shell 12 | USAGE: 13 | basic-app new | open | list | clean | view-lists 14 | 15 | ``` 16 | 17 | ## Arguments 18 | ### Commands 19 | - [__new__](./basic-app-new.md): Add a new user. 20 | - [__open__](./basic-app-open.md): Open or create a users file. 21 | - [__list__](./basic-app-list.md): List all current users. 22 | - [__clean__](./basic-app-clean.md): Clean (delete) the default users file (users.csv) and persistent variable file (.ba_persist). 23 | - [__view-lists__](./basic-app-view-lists.md): View all lists (csv files) in the current directory. 24 | 25 | -------------------------------------------------------------------------------- /examples/basic_app_meta/tab_completions/_basic-app-completion.zsh: -------------------------------------------------------------------------------- 1 | #compdef basic-app 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # Zsh Completion Installation Instructions for basic-app 8 | # 1. Place this script in a directory specified in your $fpath, or a new one such as 9 | # ~/.zsh/completion/ 10 | # 11 | # 2. Ensure the script has executable permissions: 12 | # chmod +x _basic-app-completion.zsh 13 | # 14 | # 3. Add the script's directory to your $fpath in your .zshrc if not already included: 15 | # fpath=(~/.zsh/completion $fpath) 16 | # 17 | # 4. Enable and initialize completion in your .zshrc if you haven't already (oh-my-zsh does this automatically): 18 | # autoload -Uz compinit && compinit 19 | # 20 | # 5. Restart your terminal session or source your .zshrc again: 21 | # source ~/.zshrc 22 | 23 | 24 | # Associative array to hold Commands, Options, and their descriptions with arbitrary depth 25 | typeset -A cmd_args 26 | cmd_args=( 27 | "basic-app" "new open list clean view-lists help usage --help --usage" 28 | "basic-app_new" "help usage --first-name --last-name --age --phone --address --help --usage" 29 | "basic-app_open" "help usage --help --usage" 30 | "basic-app_list" "filter help usage --help --usage" 31 | "basic-app_list_filter" "help usage --id --admin --age --first-name --last-name --phone --address --help --usage" 32 | "basic-app_clean" "help usage --file --help --usage" 33 | "basic-app_view-lists" "help usage --help --usage" 34 | ) 35 | # Generic function for command completions 36 | _basic-app_completions() { 37 | local -a completions 38 | # Determine the current command context 39 | local context="basic-app" 40 | for word in "${words[@]:1:$CURRENT-1}"; do 41 | if [[ -n $cmd_args[${context}_${word}] ]]; then 42 | context="${context}_${word}" 43 | fi 44 | done 45 | # Generate completions for the current context 46 | completions=(${(s: :)cmd_args[$context]}) 47 | if [[ -n $completions ]]; then 48 | _describe -t commands "basic-app" completions && return 0 49 | fi 50 | } 51 | _basic-app_completions "$@" -------------------------------------------------------------------------------- /examples/basic_app_meta/tab_completions/basic-app-completion.bash: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # Bash Completion Installation Instructions for basic-app 8 | # 1. Place this script in a directory like /etc/bash_completion.d/ (Linux) 9 | # or /usr/local/etc/bash_completion.d/ (Mac, if using Homebrew and bash-completion) 10 | # 11 | # 2. Ensure the script has executable permissions: 12 | # chmod +x basic-app-completion.bash 13 | # 14 | # 3. Source this script from your .bashrc or .bash_profile by adding: 15 | # . /path/to/basic-app-completion.bash 16 | # 17 | # 4. Restart your terminal session or source your profile again: 18 | # source ~/.bashrc # or ~/.bash_profile 19 | 20 | 21 | _basic-app_completions() { 22 | local cur prev 23 | COMPREPLY=() 24 | cur="${COMP_WORDS[COMP_CWORD]}" 25 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 26 | 27 | case "${prev}" in 28 | "new") 29 | _basic-app_new_completions 30 | ;; 31 | "open") 32 | _basic-app_open_completions 33 | ;; 34 | "list") 35 | _basic-app_list_completions 36 | ;; 37 | "clean") 38 | _basic-app_clean_completions 39 | ;; 40 | "view-lists") 41 | _basic-app_view-lists_completions 42 | ;; 43 | "basic-app") 44 | COMPREPLY=($(compgen -W "new open list clean view-lists help usage --help --usage" -- ${cur})) 45 | ;; 46 | *) 47 | COMPREPLY=($(compgen -f -- ${cur})) 48 | esac 49 | } 50 | 51 | _basic-app_new_completions() { 52 | local cur prev 53 | COMPREPLY=() 54 | cur="${COMP_WORDS[COMP_CWORD]}" 55 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 56 | 57 | case "${prev}" in 58 | "new") 59 | COMPREPLY=($(compgen -W "help usage --first-name --last-name --age --phone --address --help --usage" -- ${cur})) 60 | ;; 61 | *) 62 | COMPREPLY=($(compgen -f -- ${cur})) 63 | esac 64 | } 65 | 66 | _basic-app_open_completions() { 67 | local cur prev 68 | COMPREPLY=() 69 | cur="${COMP_WORDS[COMP_CWORD]}" 70 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 71 | 72 | case "${prev}" in 73 | "open") 74 | COMPREPLY=($(compgen -W "help usage --help --usage" -- ${cur})) 75 | ;; 76 | *) 77 | COMPREPLY=($(compgen -f -- ${cur})) 78 | esac 79 | } 80 | 81 | _basic-app_list_completions() { 82 | local cur prev 83 | COMPREPLY=() 84 | cur="${COMP_WORDS[COMP_CWORD]}" 85 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 86 | 87 | case "${prev}" in 88 | "filter") 89 | _basic-applist_filter_completions 90 | ;; 91 | "list") 92 | COMPREPLY=($(compgen -W "filter help usage --help --usage" -- ${cur})) 93 | ;; 94 | *) 95 | COMPREPLY=($(compgen -f -- ${cur})) 96 | esac 97 | } 98 | 99 | _basic-app_list_filter_completions() { 100 | local cur prev 101 | COMPREPLY=() 102 | cur="${COMP_WORDS[COMP_CWORD]}" 103 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 104 | 105 | case "${prev}" in 106 | "filter") 107 | COMPREPLY=($(compgen -W "help usage --id --admin --age --first-name --last-name --phone --address --help --usage" -- ${cur})) 108 | ;; 109 | *) 110 | COMPREPLY=($(compgen -f -- ${cur})) 111 | esac 112 | } 113 | 114 | _basic-app_clean_completions() { 115 | local cur prev 116 | COMPREPLY=() 117 | cur="${COMP_WORDS[COMP_CWORD]}" 118 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 119 | 120 | case "${prev}" in 121 | "clean") 122 | COMPREPLY=($(compgen -W "help usage --file --help --usage" -- ${cur})) 123 | ;; 124 | *) 125 | COMPREPLY=($(compgen -f -- ${cur})) 126 | esac 127 | } 128 | 129 | _basic-app_view-lists_completions() { 130 | local cur prev 131 | COMPREPLY=() 132 | cur="${COMP_WORDS[COMP_CWORD]}" 133 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 134 | 135 | case "${prev}" in 136 | "view-lists") 137 | COMPREPLY=($(compgen -W "help usage --help --usage" -- ${cur})) 138 | ;; 139 | *) 140 | COMPREPLY=($(compgen -f -- ${cur})) 141 | esac 142 | } 143 | 144 | 145 | complete -F _basic-app_completions basic-app -------------------------------------------------------------------------------- /examples/basic_app_meta/tab_completions/basic-app-completion.ps1: -------------------------------------------------------------------------------- 1 | # Requires PowerShell v5.1+ 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # PowerShell Completion Installation Instructions for basic-app 8 | # 1. Load the completion script into your current PowerShell session: 9 | # . .\basic-app-completion.ps1 10 | # 11 | # 2. Ensure your Execution Policy allows the script to be run. Example: 12 | # Set-ExecutionPolicy RemoteSigned 13 | # 14 | # 3. To ensure this completion script is loaded automatically in future sessions, 15 | # add the above sourcing command to your PowerShell profile: 16 | # Notepad $PROFILE 17 | # Add the line: . C:\path\to\basic-app-completion.ps1 18 | # 19 | # 4. Restart your PowerShell session or source your profile again: 20 | # . $PROFILE 21 | 22 | 23 | function _basic-app { 24 | param($wordToComplete, $commandAst) 25 | $suggestions = @( 26 | 'new', 27 | 'open', 28 | 'list', 29 | 'clean', 30 | 'view-lists', 31 | 'help', 32 | 'usage', 33 | '--help', 34 | '--usage' 35 | ) 36 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 37 | } 38 | 39 | function _basic-app-new { 40 | param($wordToComplete, $commandAst) 41 | $suggestions = @( 42 | 'help', 43 | 'usage', 44 | '--first-name', 45 | '--last-name', 46 | '--age', 47 | '--phone', 48 | '--address', 49 | '--help', 50 | '--usage' 51 | ) 52 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 53 | } 54 | 55 | function _basic-app-open { 56 | param($wordToComplete, $commandAst) 57 | $suggestions = @( 58 | 'help', 59 | 'usage', 60 | '--help', 61 | '--usage' 62 | ) 63 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 64 | } 65 | 66 | function _basic-app-list { 67 | param($wordToComplete, $commandAst) 68 | $suggestions = @( 69 | 'filter', 70 | 'help', 71 | 'usage', 72 | '--help', 73 | '--usage' 74 | ) 75 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 76 | } 77 | 78 | function _basic-app-list-filter { 79 | param($wordToComplete, $commandAst) 80 | $suggestions = @( 81 | 'help', 82 | 'usage', 83 | '--id', 84 | '--admin', 85 | '--age', 86 | '--first-name', 87 | '--last-name', 88 | '--phone', 89 | '--address', 90 | '--help', 91 | '--usage' 92 | ) 93 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 94 | } 95 | 96 | function _basic-app-clean { 97 | param($wordToComplete, $commandAst) 98 | $suggestions = @( 99 | 'help', 100 | 'usage', 101 | '--file', 102 | '--help', 103 | '--usage' 104 | ) 105 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 106 | } 107 | 108 | function _basic-app-view-lists { 109 | param($wordToComplete, $commandAst) 110 | $suggestions = @( 111 | 'help', 112 | 'usage', 113 | '--help', 114 | '--usage' 115 | ) 116 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 117 | } 118 | 119 | Register-ArgumentCompleter -CommandName 'basic-app.exe' -ScriptBlock { 120 | param($wordToComplete, $commandAst, $cursorPos) 121 | 122 | $functionName = "_" + $($commandAst.Extent.Text.replace(' ', '-').replace(".exe", "")) 123 | if ($wordToComplete) { 124 | $functionName = $functionName.replace("-$wordToComplete", "") 125 | } 126 | 127 | # Check if the function exists and invoke it 128 | if (Get-Command -Name $functionName -ErrorAction SilentlyContinue) { 129 | & $functionName $wordToComplete $commandAst 130 | } else { 131 | # Fallback logic to show files in the current directory 132 | Get-ChildItem -Path '.' -File | Where-Object Name -like "*$wordToComplete*" | ForEach-Object { 133 | [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /examples/cova_demo_meta/arg_templates/covademo-template.kdl: -------------------------------------------------------------------------------- 1 | # This KDL template is formatted to match the `usage` tool as detailed here: https://sr.ht/~jdx/usage/ 2 | 3 | name "covademo" 4 | bin "covademo" 5 | about "A demo of the Cova command line argument parser." 6 | version "0.10.2" 7 | author "00JCIV00" 8 | 9 | flag "-s,--string" help="A string option. (Can be given up to 4 times.)" 10 | flag "-i,--int" help="An integer option. (Can be given up to 10 times.)" 11 | flag "-f,--float" help="An float option. (Can be given up to 10 times.)" 12 | flag "-F,--file" help="A filepath option." 13 | flag "-o,--ordinal" help="An ordinal number option." 14 | flag "-c,--cardinal" help="A cardinal number option." 15 | flag "-t,--toggle" help="A toggle/boolean option." 16 | flag "-b,--bool" help="A toggle/boolean option." 17 | flag "-v,--verbosity" help="Set the CovaDemo verbosity level. (WIP)" 18 | 19 | arg "cmd_str" help="A string value for the command." 20 | arg "cmd_bool" help="A boolean value for the command." 21 | arg "cmd_u64" help="A u64 value for the command." 22 | 23 | cmd "sub-cmd" help="A demo sub command." { 24 | alias "alias-cmd" 25 | alias "test-alias" 26 | 27 | flag "-i," help="A nested integer option." 28 | flag "-s,--nested_str" help="A nested string option." 29 | 30 | arg "nested_str_val" help="A nested string value." 31 | arg "nested_float_val" help="A nested float value." 32 | } 33 | 34 | cmd "basic" help="The most basic Command." { 35 | alias "basic-cmd" 36 | } 37 | 38 | cmd "nest-1" help="Nested Level 1." { 39 | cmd "nest-2" help="Nested Level 2." { 40 | cmd "nest-3" help="Nested Level 3." { 41 | flag "-i,--inheritable" help="Inheritable Option" 42 | cmd "nest-4" help="Nested Level 4." 43 | } 44 | } 45 | } 46 | 47 | cmd "struct-cmd" help="A demo sub command made from a struct." { 48 | flag "-i,--int" help="The first Integer Value for the struct-cmd." 49 | flag "-s,--str" help="The 'str' Option." 50 | flag "-S,--str2" help="The 'str2' Option." 51 | flag "-f,--flt" help="The 'flt' Option." 52 | flag "-I,--int2" help="The 'int2' Option." 53 | flag "-m,--multi-int" help="The 'multi-int' Value." 54 | flag "-M,--multi-str" help="The 'multi-str' Value." 55 | flag "-r,--rgb-enum" help="The 'rgb-enum' Option." 56 | flag "-t,--struct-bool" help="The 'struct_bool' Option of type 'toggle'." 57 | flag "-T,--struct-str" help="The 'struct_str' Option of type 'text'." 58 | flag "-R,--struct-int" help="The 'struct_int' Option of type 'i64'." 59 | 60 | arg "multi-int-val" help="The 'multi-int-val' Value." 61 | 62 | cmd "inner-cmd" help="An inner/nested command for struct-cmd" { 63 | flag "-i,--in-bool" help="The 'in_bool' Option of type 'toggle'." 64 | flag "-I,--in-float" help="The 'in_float' Option of type 'f32'." 65 | flag "-H,--h-string" help="The 'h_string' Option of type 'text'." 66 | } 67 | } 68 | 69 | cmd "union-cmd" help="A demo sub command made from a union." { 70 | flag "-i,--int" help="The first Integer Value for the union-cmd." 71 | flag "-s,--str" help="The first String Value for the union-cmd." 72 | 73 | arg "union-uint" help="The 'union-uint' Value." 74 | arg "union-str" help="The 'union-str' Value." 75 | } 76 | 77 | cmd "fn-cmd" help="A demo sub command made from a function." { 78 | arg "int" help="The first Integer Value for the fn-cmd." 79 | arg "string" help="The first String Value for the fn-cmd." 80 | arg "byte_array" help="A 6-Byte Array for fn-cmd" 81 | } 82 | 83 | cmd "add-user" help="A demo sub command for adding a user." { 84 | flag "-f,--fname" help="The 'fname' Option." 85 | flag "-l,--lname" help="The 'lname' Option." 86 | flag "-a,--age" help="The 'age' Option." 87 | flag "-A,--admin" help="The 'admin' Option." 88 | flag "-r,--ref-ids" help="The 'ref-ids' Value." 89 | 90 | arg "id" help="The 'id' Value." 91 | } 92 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-add-user.1: -------------------------------------------------------------------------------- 1 | .TH covademo-add-user 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-add-user 5 | 6 | .SH SYNOPSIS 7 | .B covademo-add-user 8 | .RB [OPTIONS] 9 | .RB [VALUES] 10 | 11 | .SH DESCRIPTION 12 | .B A demo sub command for adding a user. 13 | .SH ARGUMENTS 14 | .SS OPTIONS 15 | .B fname: 16 | [-f,--fname "fname (text)"]: 17 | The 'fname' Option. 18 | 19 | .B lname: 20 | [-l,--lname "lname (text)"]: 21 | The 'lname' Option. 22 | 23 | .B age: 24 | [-a,--age "age (u8)"]: 25 | The 'age' Option. 26 | 27 | .B admin: 28 | [-A,--admin "admin (toggle)"]: 29 | The 'admin' Option. 30 | 31 | .B ref-ids: 32 | [-r,--ref-ids "ref-ids (u8)"]: 33 | The 'ref-ids' Value. 34 | 35 | .SS VALUES 36 | .B id: 37 | (u16): The 'id' Value. 38 | 39 | 40 | .SH AUTHOR 41 | .B 00JCIV00 42 | 43 | .SH COPYRIGHT 44 | .B MIT License 45 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-basic.1: -------------------------------------------------------------------------------- 1 | .TH covademo-basic 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-basic 5 | 6 | .SH SYNOPSIS 7 | .B covademo-basic 8 | 9 | .SH DESCRIPTION 10 | .B The most basic Command. 11 | 12 | .SH AUTHOR 13 | .B 00JCIV00 14 | 15 | .SH COPYRIGHT 16 | .B MIT License 17 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-fn-cmd.1: -------------------------------------------------------------------------------- 1 | .TH covademo-fn-cmd 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-fn-cmd 5 | 6 | .SH SYNOPSIS 7 | .B covademo-fn-cmd 8 | .RB [VALUES] 9 | 10 | .SH DESCRIPTION 11 | .B A demo sub command made from a function. 12 | .SH ARGUMENTS 13 | .SS VALUES 14 | .B int: 15 | (i32): The first Integer Value for the fn-cmd. 16 | 17 | .B string: 18 | (text): The first String Value for the fn-cmd. 19 | 20 | .B byte_array: 21 | (u8): A 6-Byte Array for fn-cmd 22 | 23 | 24 | .SH AUTHOR 25 | .B 00JCIV00 26 | 27 | .SH COPYRIGHT 28 | .B MIT License 29 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-nest-1-nest-2.1: -------------------------------------------------------------------------------- 1 | .TH covademo-nest-1-nest-2 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-nest-1-nest-2 5 | 6 | .SH SYNOPSIS 7 | .B covademo-nest-1-nest-2 8 | .RB [SUB COMMAND...] 9 | 10 | .SH DESCRIPTION 11 | .B Nested Level 2. 12 | .SH ARGUMENTS 13 | .SS COMMANDS 14 | .B nest-3: 15 | Nested Level 3. 16 | 17 | 18 | .SH AUTHOR 19 | .B 00JCIV00 20 | 21 | .SH COPYRIGHT 22 | .B MIT License 23 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-nest-1.1: -------------------------------------------------------------------------------- 1 | .TH covademo-nest-1 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-nest-1 5 | 6 | .SH SYNOPSIS 7 | .B covademo-nest-1 8 | .RB [SUB COMMAND...] 9 | 10 | .SH DESCRIPTION 11 | .B Nested Level 1. 12 | .SH ARGUMENTS 13 | .SS COMMANDS 14 | .B nest-2: 15 | Nested Level 2. 16 | 17 | 18 | .SH AUTHOR 19 | .B 00JCIV00 20 | 21 | .SH COPYRIGHT 22 | .B MIT License 23 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-struct-cmd-inner-cmd.1: -------------------------------------------------------------------------------- 1 | .TH covademo-struct-cmd-inner-cmd 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-struct-cmd-inner-cmd 5 | 6 | .SH SYNOPSIS 7 | .B covademo-struct-cmd-inner-cmd 8 | .RB [OPTIONS] 9 | 10 | .SH DESCRIPTION 11 | .B An inner/nested command for struct-cmd 12 | .SH ARGUMENTS 13 | .SS OPTIONS 14 | .B in-bool: 15 | [-i,--in-bool "in-bool (toggle)"]: 16 | The 'in_bool' Option of type 'toggle'. 17 | 18 | .B in-float: 19 | [-I,--in-float "in-float (f32)"]: 20 | The 'in_float' Option of type 'f32'. 21 | 22 | .B h-string: 23 | [-H,--h-string "h-string (text)"]: 24 | The 'h_string' Option of type 'text'. 25 | 26 | 27 | .SH AUTHOR 28 | .B 00JCIV00 29 | 30 | .SH COPYRIGHT 31 | .B MIT License 32 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-struct-cmd.1: -------------------------------------------------------------------------------- 1 | .TH covademo-struct-cmd 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-struct-cmd 5 | 6 | .SH SYNOPSIS 7 | .B covademo-struct-cmd 8 | .RB [OPTIONS] 9 | .RB [VALUES] 10 | .RB [SUB COMMAND...] 11 | 12 | .SH DESCRIPTION 13 | .B A demo sub command made from a struct. 14 | .SH ARGUMENTS 15 | .SS COMMANDS 16 | .B inner-cmd: 17 | An inner/nested command for struct-cmd 18 | 19 | .SS OPTIONS 20 | .B int: 21 | [-i,--int "int (i32)"]: 22 | The first Integer Value for the struct-cmd. 23 | 24 | .B str: 25 | [-s,--str "str (text)"]: 26 | The 'str' Option. 27 | 28 | .B str2: 29 | [-S,--str2 "str2 (text)"]: 30 | The 'str2' Option. 31 | 32 | .B flt: 33 | [-f,--flt "flt (f16)"]: 34 | The 'flt' Option. 35 | 36 | .B int2: 37 | [-I,--int2 "int2 (u16)"]: 38 | The 'int2' Option. 39 | 40 | .B multi-int: 41 | [-m,--multi-int "multi-int (u8)"]: 42 | The 'multi-int' Value. 43 | 44 | .B multi-str: 45 | [-M,--multi-str "multi-str (text)"]: 46 | The 'multi-str' Value. 47 | 48 | .B rgb-enum: 49 | [-r,--rgb-enum "rgb-enum (covademo.DemoStruct.InnerEnum)"]: 50 | The 'rgb-enum' Option. 51 | 52 | .B struct-bool: 53 | [-t,--struct-bool "struct-bool (toggle)"]: 54 | The 'struct_bool' Option of type 'toggle'. 55 | 56 | .B struct-str: 57 | [-T,--struct-str "struct-str (text)"]: 58 | The 'struct_str' Option of type 'text'. 59 | 60 | .B struct-int: 61 | [-R,--struct-int "struct-int (i64)"]: 62 | The 'struct_int' Option of type 'i64'. 63 | 64 | .SS VALUES 65 | .B multi-int-val: 66 | (u16): The 'multi-int-val' Value. 67 | 68 | 69 | .SH AUTHOR 70 | .B 00JCIV00 71 | 72 | .SH COPYRIGHT 73 | .B MIT License 74 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-sub-cmd.1: -------------------------------------------------------------------------------- 1 | .TH covademo-sub-cmd 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-sub-cmd 5 | 6 | .SH SYNOPSIS 7 | .B covademo-sub-cmd 8 | .RB [OPTIONS] 9 | .RB [VALUES] 10 | 11 | .SH DESCRIPTION 12 | .B A demo sub command. 13 | .SH ARGUMENTS 14 | .SS OPTIONS 15 | .B nested_int_opt: 16 | [-i,--null "nested_int_val (u8)"]: 17 | A nested integer option. 18 | 19 | .B nested_str_opt: 20 | [-s,--nested_str "nested_str_val (text)"]: 21 | A nested string option. 22 | 23 | .SS VALUES 24 | .B nested_str_val: 25 | (text): A nested string value. 26 | 27 | .B nested_float_val: 28 | (f32): A nested float value. 29 | 30 | .SH EXAMPLES 31 | 32 | .B covademo sub-cmd 3.14 33 | 34 | 35 | 36 | .SH AUTHOR 37 | .B 00JCIV00 38 | 39 | .SH COPYRIGHT 40 | .B MIT License 41 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo-union-cmd.1: -------------------------------------------------------------------------------- 1 | .TH covademo-union-cmd 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo-union-cmd 5 | 6 | .SH SYNOPSIS 7 | .B covademo-union-cmd 8 | .RB [OPTIONS] 9 | .RB [VALUES] 10 | 11 | .SH DESCRIPTION 12 | .B A demo sub command made from a union. 13 | .SH ARGUMENTS 14 | .SS OPTIONS 15 | .B int: 16 | [-i,--int "int (i32)"]: 17 | The first Integer Value for the union-cmd. 18 | 19 | .B str: 20 | [-s,--str "str (text)"]: 21 | The first String Value for the union-cmd. 22 | 23 | .SS VALUES 24 | .B union-uint: 25 | (u8): The 'union-uint' Value. 26 | 27 | .B union-str: 28 | (text): The 'union-str' Value. 29 | 30 | 31 | .SH AUTHOR 32 | .B 00JCIV00 33 | 34 | .SH COPYRIGHT 35 | .B MIT License 36 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/manpages/covademo.1: -------------------------------------------------------------------------------- 1 | .TH covademo 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B covademo 5 | 6 | .SH SYNOPSIS 7 | .B covademo 8 | .RB [OPTIONS] 9 | .RB [VALUES] 10 | .RB [SUB COMMAND...] 11 | 12 | .SH DESCRIPTION 13 | .B A demo of the Cova command line argument parser. 14 | .SH ARGUMENTS 15 | .SS COMMANDS 16 | .B sub-cmd: 17 | A demo sub command. 18 | 19 | .B basic: 20 | The most basic Command. 21 | 22 | .B nest-1: 23 | Nested Level 1. 24 | 25 | .B struct-cmd: 26 | A demo sub command made from a struct. 27 | 28 | .B union-cmd: 29 | A demo sub command made from a union. 30 | 31 | .B fn-cmd: 32 | A demo sub command made from a function. 33 | 34 | .B add-user: 35 | A demo sub command for adding a user. 36 | 37 | .SS OPTIONS 38 | .B string_opt: 39 | [-s,--string "string_val (string)"]: 40 | A string option. (Can be given up to 4 times.) 41 | 42 | .B int_opt: 43 | [-i,--int "int_val (i16)"]: 44 | An integer option. (Can be given up to 10 times.) 45 | 46 | .B float_opt: 47 | [-f,--float "float_val (f16)"]: 48 | An float option. (Can be given up to 10 times.) 49 | 50 | .B file_opt: 51 | [-F,--file "filepath (filepath)"]: 52 | A filepath option. 53 | 54 | .B ordinal_opt: 55 | [-o,--ordinal "ordinal_val (text)"]: 56 | An ordinal number option. 57 | 58 | .B cardinal_opt: 59 | [-c,--cardinal "cardinal_val (u8)"]: 60 | A cardinal number option. 61 | 62 | .B toggle_opt: 63 | [-t,--toggle "toggle_val (toggle)"]: 64 | A toggle/boolean option. 65 | 66 | .B bool_opt: 67 | [-b,--bool "bool_val (toggle)"]: 68 | A toggle/boolean option. 69 | 70 | .B verbosity_opt: 71 | [-v,--verbosity "verbosity_level (u4)"]: 72 | Set the CovaDemo verbosity level. (WIP) 73 | 74 | .SS VALUES 75 | .B cmd_str: 76 | (text): A string value for the command. 77 | 78 | .B cmd_bool: 79 | (toggle): A boolean value for the command. 80 | 81 | .B cmd_u64: 82 | (u64): A u64 value for the command. 83 | 84 | .SH EXAMPLES 85 | 86 | .B covademo -b --string "Optional String" 87 | 88 | .B covademo -i 0 -i=1 -i2 -i=3,4,5 -i6,7 --int 8 --int=9 89 | 90 | .B covademo --file "/some/file" 91 | 92 | 93 | 94 | .SH AUTHOR 95 | .B 00JCIV00 96 | 97 | .SH COPYRIGHT 98 | .B MIT License 99 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-add-user.md: -------------------------------------------------------------------------------- 1 | # add-user 2 | __[covademo](./covademo.md)__ > __add-user__ 3 | 4 | A demo sub command for adding a user. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | add-user [ --fname | --lname | --age | --admin | --ref-ids ] 12 | ``` 13 | 14 | ## Arguments 15 | ### Options 16 | - __fname__: 17 | - `-f, --fname ` 18 | - The 'fname' Option. 19 | - __lname__: 20 | - `-l, --lname ` 21 | - The 'lname' Option. 22 | - __age__: 23 | - `-a, --age ` 24 | - The 'age' Option. 25 | - __admin__: 26 | - `-A, --admin ` 27 | - The 'admin' Option. 28 | - __ref-ids__: 29 | - `-r, --ref-ids ` 30 | - The 'ref-ids' Value. 31 | ### Values 32 | - __id__ (u16) 33 | - The 'id' Value. 34 | 35 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-basic.md: -------------------------------------------------------------------------------- 1 | # basic 2 | __[covademo](./covademo.md)__ > __basic__ 3 | 4 | The most basic Command. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | basic``` 12 | 13 | ## Alias(es) 14 | - `basic-cmd` 15 | 16 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-fn-cmd.md: -------------------------------------------------------------------------------- 1 | # fn-cmd 2 | __[covademo](./covademo.md)__ > __fn-cmd__ 3 | 4 | A demo sub command made from a function. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | fn-cmd``` 12 | 13 | ## Arguments 14 | ### Values 15 | - __int__ (i32) 16 | - The first Integer Value for the fn-cmd. 17 | - __string__ (text) 18 | - The first String Value for the fn-cmd. 19 | - __byte_array__ (u8) 20 | - A 6-Byte Array for fn-cmd 21 | 22 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-nest-1-nest-2.md: -------------------------------------------------------------------------------- 1 | # nest-2 2 | __[covademo](./covademo.md)__ > __[nest-1](./covademo-nest-1.md)__ > __nest-2__ 3 | 4 | Nested Level 2. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | nest-2 [ nest-3 ] 12 | ``` 13 | 14 | ## Arguments 15 | ### Commands 16 | - [__nest-3__](./covademo-nest-1-nest-2-nest-3.md): Nested Level 3. 17 | 18 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-nest-1.md: -------------------------------------------------------------------------------- 1 | # nest-1 2 | __[covademo](./covademo.md)__ > __nest-1__ 3 | 4 | Nested Level 1. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | nest-1 [ nest-2 ] 12 | ``` 13 | 14 | ## Arguments 15 | ### Commands 16 | - [__nest-2__](./covademo-nest-1-nest-2.md): Nested Level 2. 17 | 18 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-struct-cmd-inner-cmd.md: -------------------------------------------------------------------------------- 1 | # inner-cmd 2 | __[covademo](./covademo.md)__ > __[struct-cmd](./covademo-struct-cmd.md)__ > __inner-cmd__ 3 | 4 | An inner/nested command for struct-cmd 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | inner-cmd [ --in-bool | --in-float | --h-string ] 12 | ``` 13 | 14 | ## Arguments 15 | ### Options 16 | - __in-bool__: 17 | - `-i, --in-bool ` 18 | - The 'in_bool' Option of type 'toggle'. 19 | - __in-float__: 20 | - `-I, --in-float ` 21 | - The 'in_float' Option of type 'f32'. 22 | - __h-string__: 23 | - `-H, --h-string ` 24 | - The 'h_string' Option of type 'text'. 25 | 26 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-struct-cmd.md: -------------------------------------------------------------------------------- 1 | # struct-cmd 2 | __[covademo](./covademo.md)__ > __struct-cmd__ 3 | 4 | A demo sub command made from a struct. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | struct-cmd [ --int | --str | --str2 | --flt | --int2 | --multi-int | --multi-str | --rgb-enum | --struct-bool | --struct-str | --struct-int ] 12 | struct-cmd [ inner-cmd ] 13 | ``` 14 | 15 | ## Arguments 16 | ### Commands 17 | - [__inner-cmd__](./covademo-struct-cmd-inner-cmd.md): An inner/nested command for struct-cmd 18 | ### Options 19 | - __int__: 20 | - `-i, --int ` 21 | - The first Integer Value for the struct-cmd. 22 | - __str__: 23 | - `-s, --str ` 24 | - The 'str' Option. 25 | - __str2__: 26 | - `-S, --str2 ` 27 | - The 'str2' Option. 28 | - __flt__: 29 | - `-f, --flt ` 30 | - The 'flt' Option. 31 | - __int2__: 32 | - `-I, --int2 ` 33 | - The 'int2' Option. 34 | - __multi-int__: 35 | - `-m, --multi-int ` 36 | - The 'multi-int' Value. 37 | - __multi-str__: 38 | - `-M, --multi-str ` 39 | - The 'multi-str' Value. 40 | - __rgb-enum__: 41 | - `-r, --rgb-enum ` 42 | - The 'rgb-enum' Option. 43 | - __struct-bool__: 44 | - `-t, --struct-bool ` 45 | - The 'struct_bool' Option of type 'toggle'. 46 | - __struct-str__: 47 | - `-T, --struct-str ` 48 | - The 'struct_str' Option of type 'text'. 49 | - __struct-int__: 50 | - `-R, --struct-int ` 51 | - The 'struct_int' Option of type 'i64'. 52 | ### Values 53 | - __multi-int-val__ (u16) 54 | - The 'multi-int-val' Value. 55 | 56 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-sub-cmd.md: -------------------------------------------------------------------------------- 1 | # sub-cmd 2 | __[covademo](./covademo.md)__ > __sub-cmd__ 3 | 4 | A demo sub command. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | sub-cmd [ --i | --nested_str ] 12 | ``` 13 | 14 | ## Alias(es) 15 | - `alias-cmd` 16 | - `test-alias` 17 | 18 | ## Examples 19 | 20 | - `covademo sub-cmd 3.14` 21 | 22 | ## Arguments 23 | ### Options 24 | - __nested_int_opt__: 25 | - `-i ` 26 | - A nested integer option. 27 | - __nested_str_opt__: 28 | - `-s, --nested_str ` 29 | - A nested string option. 30 | ### Values 31 | - __nested_str_val__ (text) 32 | - A nested string value. 33 | - __nested_float_val__ (f32) 34 | - A nested float value. 35 | 36 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo-union-cmd.md: -------------------------------------------------------------------------------- 1 | # union-cmd 2 | __[covademo](./covademo.md)__ > __union-cmd__ 3 | 4 | A demo sub command made from a union. 5 | 6 | ___ 7 | 8 | ## Usage 9 | ```shell 10 | USAGE 11 | union-cmd [ --int | --str ] 12 | ``` 13 | 14 | ## Arguments 15 | ### Options 16 | - __int__: 17 | - `-i, --int ` 18 | - The first Integer Value for the union-cmd. 19 | - __str__: 20 | - `-s, --str ` 21 | - The first String Value for the union-cmd. 22 | ### Values 23 | - __union-uint__ (u8) 24 | - The 'union-uint' Value. 25 | - __union-str__ (text) 26 | - The 'union-str' Value. 27 | 28 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/help_docs/markdown/covademo.md: -------------------------------------------------------------------------------- 1 | # covademo 2 | A demo of the Cova command line argument parser. 3 | 4 | __Version:__ 0.10.2
5 | __Date:__ 23 OCT 2024
6 | __Author:__ 00JCIV00
7 | __Copyright:__ MIT License
8 | ___ 9 | 10 | ## Usage 11 | ```shell 12 | USAGE 13 | covademo [ --string | --int | --float | --file | --ordinal | --cardinal | --toggle | --bool | --verbosity ] 14 | covademo [ sub-cmd | basic | nest-1 | struct-cmd | union-cmd | fn-cmd | add-user ] 15 | ``` 16 | 17 | ## Examples 18 | 19 | - `covademo -b --string "Optional String"` 20 | - `covademo -i 0 -i=1 -i2 -i=3,4,5 -i6,7 --int 8 --int=9` 21 | - `covademo --file "/some/file"` 22 | 23 | ## Arguments 24 | ### Commands 25 | - [__sub-cmd__](./covademo-sub-cmd.md): A demo sub command. 26 | - [__basic__](./covademo-basic.md): The most basic Command. 27 | - [__nest-1__](./covademo-nest-1.md): Nested Level 1. 28 | - [__struct-cmd__](./covademo-struct-cmd.md): A demo sub command made from a struct. 29 | - [__union-cmd__](./covademo-union-cmd.md): A demo sub command made from a union. 30 | - [__fn-cmd__](./covademo-fn-cmd.md): A demo sub command made from a function. 31 | - [__add-user__](./covademo-add-user.md): A demo sub command for adding a user. 32 | ### Options 33 | - __string_opt__: 34 | - `-s, --string, --text ` 35 | - A string option. (Can be given up to 4 times.) 36 | - __int_opt__: 37 | - `-i, --int ` 38 | - An integer option. (Can be given up to 10 times.) 39 | - __float_opt__: 40 | - `-f, --float ` 41 | - An float option. (Can be given up to 10 times.) 42 | - __file_opt__: 43 | - `-F, --file ` 44 | - A filepath option. 45 | - __ordinal_opt__: 46 | - `-o, --ordinal ` 47 | - An ordinal number option. 48 | - __cardinal_opt__: 49 | - `-c, --cardinal ` 50 | - A cardinal number option. 51 | - __toggle_opt__: 52 | - `-t, --toggle, --switch ` 53 | - A toggle/boolean option. 54 | - __bool_opt__: 55 | - `-b, --bool ` 56 | - A toggle/boolean option. 57 | - __verbosity_opt__: 58 | - `-v, --verbosity ` 59 | - Set the CovaDemo verbosity level. (WIP) 60 | ### Values 61 | - __cmd_str__ (text) 62 | - A string value for the command. 63 | - __cmd_bool__ (toggle) 64 | - A boolean value for the command. 65 | - __cmd_u64__ (u64) 66 | - A u64 value for the command. 67 | 68 | -------------------------------------------------------------------------------- /examples/cova_demo_meta/tab_completions/_covademo-completion.zsh: -------------------------------------------------------------------------------- 1 | #compdef covademo 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # Zsh Completion Installation Instructions for covademo 8 | # 1. Place this script in a directory specified in your $fpath, or a new one such as 9 | # ~/.zsh/completion/ 10 | # 11 | # 2. Ensure the script has executable permissions: 12 | # chmod +x _covademo-completion.zsh 13 | # 14 | # 3. Add the script's directory to your $fpath in your .zshrc if not already included: 15 | # fpath=(~/.zsh/completion $fpath) 16 | # 17 | # 4. Enable and initialize completion in your .zshrc if you haven't already (oh-my-zsh does this automatically): 18 | # autoload -Uz compinit && compinit 19 | # 20 | # 5. Restart your terminal session or source your .zshrc again: 21 | # source ~/.zshrc 22 | 23 | 24 | # Associative array to hold Commands, Options, and their descriptions with arbitrary depth 25 | typeset -A cmd_args 26 | cmd_args=( 27 | "covademo" "sub-cmd basic nest-1 struct-cmd union-cmd fn-cmd add-user help usage --string --int --float --file --ordinal --cardinal --toggle --bool --verbosity --help --usage" 28 | "covademo_sub-cmd" "help usage --nested_str --help --usage" 29 | "covademo_basic" "help usage --help --usage" 30 | "covademo_nest-1" "nest-2 help usage --help --usage" 31 | "covademo_nest-1_nest-2" "nest-3 help usage --help --usage" 32 | "covademo_nest-1_nest-2_nest-3" "nest-4 help usage --inheritable --help --usage" 33 | "covademo_nest-1_nest-2_nest-3_nest-4" "help usage --help --usage" 34 | "covademo_struct-cmd" "inner-cmd help usage --int --str --str2 --flt --int2 --multi-int --multi-str --rgb-enum --struct-bool --struct-str --struct-int --help --usage" 35 | "covademo_struct-cmd_inner-cmd" "help usage --in-bool --in-float --h-string --help --usage" 36 | "covademo_union-cmd" "help usage --int --str --help --usage" 37 | "covademo_fn-cmd" "help usage --help --usage" 38 | "covademo_add-user" "help usage --fname --lname --age --admin --ref-ids --help --usage" 39 | ) 40 | # Generic function for command completions 41 | _covademo_completions() { 42 | local -a completions 43 | # Determine the current command context 44 | local context="covademo" 45 | for word in "${words[@]:1:$CURRENT-1}"; do 46 | if [[ -n $cmd_args[${context}_${word}] ]]; then 47 | context="${context}_${word}" 48 | fi 49 | done 50 | # Generate completions for the current context 51 | completions=(${(s: :)cmd_args[$context]}) 52 | if [[ -n $completions ]]; then 53 | _describe -t commands "covademo" completions && return 0 54 | fi 55 | } 56 | _covademo_completions "$@" -------------------------------------------------------------------------------- /examples/cova_demo_meta/tab_completions/covademo-completion.bash: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # Bash Completion Installation Instructions for covademo 8 | # 1. Place this script in a directory like /etc/bash_completion.d/ (Linux) 9 | # or /usr/local/etc/bash_completion.d/ (Mac, if using Homebrew and bash-completion) 10 | # 11 | # 2. Ensure the script has executable permissions: 12 | # chmod +x covademo-completion.bash 13 | # 14 | # 3. Source this script from your .bashrc or .bash_profile by adding: 15 | # . /path/to/covademo-completion.bash 16 | # 17 | # 4. Restart your terminal session or source your profile again: 18 | # source ~/.bashrc # or ~/.bash_profile 19 | 20 | 21 | _covademo_completions() { 22 | local cur prev 23 | COMPREPLY=() 24 | cur="${COMP_WORDS[COMP_CWORD]}" 25 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 26 | 27 | case "${prev}" in 28 | "sub-cmd") 29 | _covademo_sub-cmd_completions 30 | ;; 31 | "basic") 32 | _covademo_basic_completions 33 | ;; 34 | "nest-1") 35 | _covademo_nest-1_completions 36 | ;; 37 | "struct-cmd") 38 | _covademo_struct-cmd_completions 39 | ;; 40 | "union-cmd") 41 | _covademo_union-cmd_completions 42 | ;; 43 | "fn-cmd") 44 | _covademo_fn-cmd_completions 45 | ;; 46 | "add-user") 47 | _covademo_add-user_completions 48 | ;; 49 | "covademo") 50 | COMPREPLY=($(compgen -W "sub-cmd basic nest-1 struct-cmd union-cmd fn-cmd add-user help usage --string --int --float --file --ordinal --cardinal --toggle --bool --verbosity --help --usage" -- ${cur})) 51 | ;; 52 | *) 53 | COMPREPLY=($(compgen -f -- ${cur})) 54 | esac 55 | } 56 | 57 | _covademo_sub-cmd_completions() { 58 | local cur prev 59 | COMPREPLY=() 60 | cur="${COMP_WORDS[COMP_CWORD]}" 61 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 62 | 63 | case "${prev}" in 64 | "sub-cmd") 65 | COMPREPLY=($(compgen -W "help usage --nested_str --help --usage" -- ${cur})) 66 | ;; 67 | *) 68 | COMPREPLY=($(compgen -f -- ${cur})) 69 | esac 70 | } 71 | 72 | _covademo_basic_completions() { 73 | local cur prev 74 | COMPREPLY=() 75 | cur="${COMP_WORDS[COMP_CWORD]}" 76 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 77 | 78 | case "${prev}" in 79 | "basic") 80 | COMPREPLY=($(compgen -W "help usage --help --usage" -- ${cur})) 81 | ;; 82 | *) 83 | COMPREPLY=($(compgen -f -- ${cur})) 84 | esac 85 | } 86 | 87 | _covademo_nest-1_completions() { 88 | local cur prev 89 | COMPREPLY=() 90 | cur="${COMP_WORDS[COMP_CWORD]}" 91 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 92 | 93 | case "${prev}" in 94 | "nest-2") 95 | _covademonest-1_nest-2_completions 96 | ;; 97 | "nest-1") 98 | COMPREPLY=($(compgen -W "nest-2 help usage --help --usage" -- ${cur})) 99 | ;; 100 | *) 101 | COMPREPLY=($(compgen -f -- ${cur})) 102 | esac 103 | } 104 | 105 | _covademo_nest-1_nest-2_completions() { 106 | local cur prev 107 | COMPREPLY=() 108 | cur="${COMP_WORDS[COMP_CWORD]}" 109 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 110 | 111 | case "${prev}" in 112 | "nest-3") 113 | _covademo_nest-1nest-2_nest-3_completions 114 | ;; 115 | "nest-2") 116 | COMPREPLY=($(compgen -W "nest-3 help usage --help --usage" -- ${cur})) 117 | ;; 118 | *) 119 | COMPREPLY=($(compgen -f -- ${cur})) 120 | esac 121 | } 122 | 123 | _covademo_nest-1_nest-2_nest-3_completions() { 124 | local cur prev 125 | COMPREPLY=() 126 | cur="${COMP_WORDS[COMP_CWORD]}" 127 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 128 | 129 | case "${prev}" in 130 | "nest-4") 131 | _covademo_nest-1_nest-2nest-3_nest-4_completions 132 | ;; 133 | "nest-3") 134 | COMPREPLY=($(compgen -W "nest-4 help usage --inheritable --help --usage" -- ${cur})) 135 | ;; 136 | *) 137 | COMPREPLY=($(compgen -f -- ${cur})) 138 | esac 139 | } 140 | 141 | _covademo_nest-1_nest-2_nest-3_nest-4_completions() { 142 | local cur prev 143 | COMPREPLY=() 144 | cur="${COMP_WORDS[COMP_CWORD]}" 145 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 146 | 147 | case "${prev}" in 148 | "nest-4") 149 | COMPREPLY=($(compgen -W "help usage --help --usage" -- ${cur})) 150 | ;; 151 | *) 152 | COMPREPLY=($(compgen -f -- ${cur})) 153 | esac 154 | } 155 | 156 | _covademo_struct-cmd_completions() { 157 | local cur prev 158 | COMPREPLY=() 159 | cur="${COMP_WORDS[COMP_CWORD]}" 160 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 161 | 162 | case "${prev}" in 163 | "inner-cmd") 164 | _covademostruct-cmd_inner-cmd_completions 165 | ;; 166 | "struct-cmd") 167 | COMPREPLY=($(compgen -W "inner-cmd help usage --int --str --str2 --flt --int2 --multi-int --multi-str --rgb-enum --struct-bool --struct-str --struct-int --help --usage" -- ${cur})) 168 | ;; 169 | *) 170 | COMPREPLY=($(compgen -f -- ${cur})) 171 | esac 172 | } 173 | 174 | _covademo_struct-cmd_inner-cmd_completions() { 175 | local cur prev 176 | COMPREPLY=() 177 | cur="${COMP_WORDS[COMP_CWORD]}" 178 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 179 | 180 | case "${prev}" in 181 | "inner-cmd") 182 | COMPREPLY=($(compgen -W "help usage --in-bool --in-float --h-string --help --usage" -- ${cur})) 183 | ;; 184 | *) 185 | COMPREPLY=($(compgen -f -- ${cur})) 186 | esac 187 | } 188 | 189 | _covademo_union-cmd_completions() { 190 | local cur prev 191 | COMPREPLY=() 192 | cur="${COMP_WORDS[COMP_CWORD]}" 193 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 194 | 195 | case "${prev}" in 196 | "union-cmd") 197 | COMPREPLY=($(compgen -W "help usage --int --str --help --usage" -- ${cur})) 198 | ;; 199 | *) 200 | COMPREPLY=($(compgen -f -- ${cur})) 201 | esac 202 | } 203 | 204 | _covademo_fn-cmd_completions() { 205 | local cur prev 206 | COMPREPLY=() 207 | cur="${COMP_WORDS[COMP_CWORD]}" 208 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 209 | 210 | case "${prev}" in 211 | "fn-cmd") 212 | COMPREPLY=($(compgen -W "help usage --help --usage" -- ${cur})) 213 | ;; 214 | *) 215 | COMPREPLY=($(compgen -f -- ${cur})) 216 | esac 217 | } 218 | 219 | _covademo_add-user_completions() { 220 | local cur prev 221 | COMPREPLY=() 222 | cur="${COMP_WORDS[COMP_CWORD]}" 223 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 224 | 225 | case "${prev}" in 226 | "add-user") 227 | COMPREPLY=($(compgen -W "help usage --fname --lname --age --admin --ref-ids --help --usage" -- ${cur})) 228 | ;; 229 | *) 230 | COMPREPLY=($(compgen -f -- ${cur})) 231 | esac 232 | } 233 | 234 | 235 | complete -F _covademo_completions covademo -------------------------------------------------------------------------------- /examples/cova_demo_meta/tab_completions/covademo-completion.ps1: -------------------------------------------------------------------------------- 1 | # Requires PowerShell v5.1+ 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # PowerShell Completion Installation Instructions for covademo 8 | # 1. Load the completion script into your current PowerShell session: 9 | # . .\covademo-completion.ps1 10 | # 11 | # 2. Ensure your Execution Policy allows the script to be run. Example: 12 | # Set-ExecutionPolicy RemoteSigned 13 | # 14 | # 3. To ensure this completion script is loaded automatically in future sessions, 15 | # add the above sourcing command to your PowerShell profile: 16 | # Notepad $PROFILE 17 | # Add the line: . C:\path\to\covademo-completion.ps1 18 | # 19 | # 4. Restart your PowerShell session or source your profile again: 20 | # . $PROFILE 21 | 22 | 23 | function _covademo { 24 | param($wordToComplete, $commandAst) 25 | $suggestions = @( 26 | 'sub-cmd', 27 | 'basic', 28 | 'nest-1', 29 | 'struct-cmd', 30 | 'union-cmd', 31 | 'fn-cmd', 32 | 'add-user', 33 | 'help', 34 | 'usage', 35 | '--string', 36 | '--int', 37 | '--float', 38 | '--file', 39 | '--ordinal', 40 | '--cardinal', 41 | '--toggle', 42 | '--bool', 43 | '--verbosity', 44 | '--help', 45 | '--usage' 46 | ) 47 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 48 | } 49 | 50 | function _covademo-sub-cmd { 51 | param($wordToComplete, $commandAst) 52 | $suggestions = @( 53 | 'help', 54 | 'usage', 55 | '--nested_str', 56 | '--help', 57 | '--usage' 58 | ) 59 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 60 | } 61 | 62 | function _covademo-basic { 63 | param($wordToComplete, $commandAst) 64 | $suggestions = @( 65 | 'help', 66 | 'usage', 67 | '--help', 68 | '--usage' 69 | ) 70 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 71 | } 72 | 73 | function _covademo-nest-1 { 74 | param($wordToComplete, $commandAst) 75 | $suggestions = @( 76 | 'nest-2', 77 | 'help', 78 | 'usage', 79 | '--help', 80 | '--usage' 81 | ) 82 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 83 | } 84 | 85 | function _covademo-nest-1-nest-2 { 86 | param($wordToComplete, $commandAst) 87 | $suggestions = @( 88 | 'nest-3', 89 | 'help', 90 | 'usage', 91 | '--help', 92 | '--usage' 93 | ) 94 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 95 | } 96 | 97 | function _covademo-nest-1-nest-2-nest-3 { 98 | param($wordToComplete, $commandAst) 99 | $suggestions = @( 100 | 'nest-4', 101 | 'help', 102 | 'usage', 103 | '--inheritable', 104 | '--help', 105 | '--usage' 106 | ) 107 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 108 | } 109 | 110 | function _covademo-nest-1-nest-2-nest-3-nest-4 { 111 | param($wordToComplete, $commandAst) 112 | $suggestions = @( 113 | 'help', 114 | 'usage', 115 | '--help', 116 | '--usage' 117 | ) 118 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 119 | } 120 | 121 | function _covademo-struct-cmd { 122 | param($wordToComplete, $commandAst) 123 | $suggestions = @( 124 | 'inner-cmd', 125 | 'help', 126 | 'usage', 127 | '--int', 128 | '--str', 129 | '--str2', 130 | '--flt', 131 | '--int2', 132 | '--multi-int', 133 | '--multi-str', 134 | '--rgb-enum', 135 | '--struct-bool', 136 | '--struct-str', 137 | '--struct-int', 138 | '--help', 139 | '--usage' 140 | ) 141 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 142 | } 143 | 144 | function _covademo-struct-cmd-inner-cmd { 145 | param($wordToComplete, $commandAst) 146 | $suggestions = @( 147 | 'help', 148 | 'usage', 149 | '--in-bool', 150 | '--in-float', 151 | '--h-string', 152 | '--help', 153 | '--usage' 154 | ) 155 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 156 | } 157 | 158 | function _covademo-union-cmd { 159 | param($wordToComplete, $commandAst) 160 | $suggestions = @( 161 | 'help', 162 | 'usage', 163 | '--int', 164 | '--str', 165 | '--help', 166 | '--usage' 167 | ) 168 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 169 | } 170 | 171 | function _covademo-fn-cmd { 172 | param($wordToComplete, $commandAst) 173 | $suggestions = @( 174 | 'help', 175 | 'usage', 176 | '--help', 177 | '--usage' 178 | ) 179 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 180 | } 181 | 182 | function _covademo-add-user { 183 | param($wordToComplete, $commandAst) 184 | $suggestions = @( 185 | 'help', 186 | 'usage', 187 | '--fname', 188 | '--lname', 189 | '--age', 190 | '--admin', 191 | '--ref-ids', 192 | '--help', 193 | '--usage' 194 | ) 195 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 196 | } 197 | 198 | Register-ArgumentCompleter -CommandName 'covademo.exe' -ScriptBlock { 199 | param($wordToComplete, $commandAst, $cursorPos) 200 | 201 | $functionName = "_" + $($commandAst.Extent.Text.replace(' ', '-').replace(".exe", "")) 202 | if ($wordToComplete) { 203 | $functionName = $functionName.replace("-$wordToComplete", "") 204 | } 205 | 206 | # Check if the function exists and invoke it 207 | if (Get-Command -Name $functionName -ErrorAction SilentlyContinue) { 208 | & $functionName $wordToComplete $commandAst 209 | } else { 210 | # Fallback logic to show files in the current directory 211 | Get-ChildItem -Path '.' -File | Where-Object Name -like "*$wordToComplete*" | ForEach-Object { 212 | [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) 213 | } 214 | } 215 | } -------------------------------------------------------------------------------- /examples/example_structs.zig: -------------------------------------------------------------------------------- 1 | //! Example structs for covademo 2 | 3 | pub const add_user = struct { 4 | fname: ?[]const u8 = "(First)", 5 | lname: ?[]const u8 = "(Last)", 6 | age: ?u8 = 30, 7 | id: u16 = 12345, 8 | admin: ?bool = false, 9 | ref_ids: [3]?u8 = .{ 10, 58, 62 }, 10 | }; 11 | 12 | pub const rem_user = struct { 13 | id: u16, 14 | }; 15 | 16 | pub const fav_list = struct { 17 | cities: ?[5][]const u8, 18 | foods: ?[3][]const u8 = .{ "ramen", "chicken parm", "tangerines" }, 19 | }; 20 | -------------------------------------------------------------------------------- /examples/logger.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const log = std.log; 3 | const cova = @import("cova"); 4 | 5 | // We need to add any Enums we're using to our Value Config. 6 | pub const CommandT = cova.Command.Custom(.{ 7 | .val_config = .{ 8 | .custom_types = &.{ log.Level }, 9 | }, 10 | }); 11 | pub const setup_cmd = CommandT{ 12 | .name = "logger", 13 | .description = "A small demo of using the Log Level Enum as an Option.", 14 | .opts = &.{ 15 | .{ 16 | .name = "log_level", 17 | .description = "An Option using the `log.Level` Enum.", 18 | .long_name = "log-level", 19 | .mandatory = true, 20 | .val = CommandT.ValueT.ofType(log.Level, .{ 21 | .name = "log_level_val", 22 | .description = " This Value will handle then Enum." 23 | }) 24 | } 25 | }, 26 | }; 27 | 28 | pub fn main() !void { 29 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 30 | const alloc = gpa.allocator(); 31 | defer if (gpa.deinit() != .ok and gpa.detectLeaks()) log.err("Memory leak detected!", .{}); 32 | const stdout = std.io.getStdOut().writer(); 33 | 34 | var main_cmd = try setup_cmd.init(alloc, .{}); 35 | defer main_cmd.deinit(); 36 | var args_iter = try cova.ArgIteratorGeneric.init(alloc); 37 | defer args_iter.deinit(); 38 | 39 | cova.parseArgs(&args_iter, CommandT, main_cmd, stdout, .{}) catch |err| switch (err) { 40 | error.UsageHelpCalled => {}, 41 | else => return err, 42 | }; 43 | 44 | const main_opts = try main_cmd.getOpts(.{}); 45 | const log_lvl_opt = main_opts.get("log_level").?; 46 | const log_lvl = log_lvl_opt.val.getAs(log.Level) catch { 47 | log.err("The provided Log Level was invalid.", .{}); 48 | return; 49 | }; 50 | log.info("Provided Log Level: {s}", .{ @tagName(log_lvl) }); 51 | } 52 | -------------------------------------------------------------------------------- /examples/logger_meta/arg_templates/logger-template.json: -------------------------------------------------------------------------------- 1 | "Meta Info": { 2 | "name": "logger", 3 | "description": "A small demo of using the Log Level Enum as an Option.", 4 | "version": "0.10.2", 5 | "ver_date": "23 OCT 2024", 6 | "author": "00JCIV00", 7 | "copyright": "MIT License" 8 | }, 9 | "Arguments": { 10 | "name": "logger", 11 | "description": "A small demo of using the Log Level Enum as an Option.", 12 | "opts": [ 13 | { 14 | "name": "log_level", 15 | "long_name": "log-level", 16 | "description": "An Option using the `log.Level` Enum.", 17 | "type_name": "log.Level", 18 | "set_behavior": "Last", 19 | "max_entries": 1 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /examples/logger_meta/arg_templates/logger-template.kdl: -------------------------------------------------------------------------------- 1 | # This KDL template is formatted to match the `usage` tool as detailed here: https://sr.ht/~jdx/usage/ 2 | 3 | name "logger" 4 | bin "logger" 5 | about "A small demo of using the Log Level Enum as an Option." 6 | version "0.10.2" 7 | author "00JCIV00" 8 | 9 | flag "--log-level" help="An Option using the `log.Level` Enum." 10 | -------------------------------------------------------------------------------- /examples/logger_meta/help_docs/manpages/logger.1: -------------------------------------------------------------------------------- 1 | .TH logger 1 "23 OCT 2024" "0.10.2" 2 | 3 | .SH NAME 4 | .B logger 5 | 6 | .SH SYNOPSIS 7 | .B logger 8 | .RB [OPTIONS] 9 | 10 | .SH DESCRIPTION 11 | .B A small demo of using the Log Level Enum as an Option. 12 | .SH ARGUMENTS 13 | .SS OPTIONS 14 | .B log_level: 15 | [-null,--log-level "log_level_val (log.Level)"]: 16 | An Option using the `log.Level` Enum. 17 | 18 | 19 | .SH AUTHOR 20 | .B 00JCIV00 21 | 22 | .SH COPYRIGHT 23 | .B MIT License 24 | -------------------------------------------------------------------------------- /examples/logger_meta/help_docs/markdown/logger.md: -------------------------------------------------------------------------------- 1 | # logger 2 | A small demo of using the Log Level Enum as an Option. 3 | 4 | __Version:__ 0.10.2
5 | __Date:__ 23 OCT 2024
6 | __Author:__ 00JCIV00
7 | __Copyright:__ MIT License
8 | ___ 9 | 10 | ## Usage 11 | ```shell 12 | USAGE: 13 | logger ​​--log-level 14 | logger 15 | 16 | ``` 17 | 18 | ## Arguments 19 | ### Options 20 | - __log_level__: 21 | - `​​--log-level ` 22 | - An Option using the `log.Level` Enum. 23 | 24 | -------------------------------------------------------------------------------- /examples/logger_meta/tab_completions/_logger-completion.zsh: -------------------------------------------------------------------------------- 1 | #compdef logger 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # Zsh Completion Installation Instructions for logger 8 | # 1. Place this script in a directory specified in your $fpath, or a new one such as 9 | # ~/.zsh/completion/ 10 | # 11 | # 2. Ensure the script has executable permissions: 12 | # chmod +x _logger-completion.zsh 13 | # 14 | # 3. Add the script's directory to your $fpath in your .zshrc if not already included: 15 | # fpath=(~/.zsh/completion $fpath) 16 | # 17 | # 4. Enable and initialize completion in your .zshrc if you haven't already (oh-my-zsh does this automatically): 18 | # autoload -Uz compinit && compinit 19 | # 20 | # 5. Restart your terminal session or source your .zshrc again: 21 | # source ~/.zshrc 22 | 23 | 24 | # Associative array to hold Commands, Options, and their descriptions with arbitrary depth 25 | typeset -A cmd_args 26 | cmd_args=( 27 | "logger" "help usage --log-level --help --usage" 28 | ) 29 | # Generic function for command completions 30 | _logger_completions() { 31 | local -a completions 32 | # Determine the current command context 33 | local context="logger" 34 | for word in "${words[@]:1:$CURRENT-1}"; do 35 | if [[ -n $cmd_args[${context}_${word}] ]]; then 36 | context="${context}_${word}" 37 | fi 38 | done 39 | # Generate completions for the current context 40 | completions=(${(s: :)cmd_args[$context]}) 41 | if [[ -n $completions ]]; then 42 | _describe -t commands "logger" completions && return 0 43 | fi 44 | } 45 | _logger_completions "$@" -------------------------------------------------------------------------------- /examples/logger_meta/tab_completions/logger-completion.bash: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # Bash Completion Installation Instructions for logger 8 | # 1. Place this script in a directory like /etc/bash_completion.d/ (Linux) 9 | # or /usr/local/etc/bash_completion.d/ (Mac, if using Homebrew and bash-completion) 10 | # 11 | # 2. Ensure the script has executable permissions: 12 | # chmod +x logger-completion.bash 13 | # 14 | # 3. Source this script from your .bashrc or .bash_profile by adding: 15 | # . /path/to/logger-completion.bash 16 | # 17 | # 4. Restart your terminal session or source your profile again: 18 | # source ~/.bashrc # or ~/.bash_profile 19 | 20 | 21 | _logger_completions() { 22 | local cur prev 23 | COMPREPLY=() 24 | cur="${COMP_WORDS[COMP_CWORD]}" 25 | prev="${COMP_WORDS[COMP_CWORD - 1]}" 26 | 27 | case "${prev}" in 28 | "logger") 29 | COMPREPLY=($(compgen -W "help usage --log-level --help --usage" -- ${cur})) 30 | ;; 31 | *) 32 | COMPREPLY=($(compgen -f -- ${cur})) 33 | esac 34 | } 35 | 36 | 37 | complete -F _logger_completions logger -------------------------------------------------------------------------------- /examples/logger_meta/tab_completions/logger-completion.ps1: -------------------------------------------------------------------------------- 1 | # Requires PowerShell v5.1+ 2 | 3 | # This Tab Completion script was generated by the Cova Library. 4 | # Details at https://github.com/00JCIV00/cova 5 | 6 | 7 | # PowerShell Completion Installation Instructions for logger 8 | # 1. Load the completion script into your current PowerShell session: 9 | # . .\logger-completion.ps1 10 | # 11 | # 2. Ensure your Execution Policy allows the script to be run. Example: 12 | # Set-ExecutionPolicy RemoteSigned 13 | # 14 | # 3. To ensure this completion script is loaded automatically in future sessions, 15 | # add the above sourcing command to your PowerShell profile: 16 | # Notepad $PROFILE 17 | # Add the line: . C:\path\to\logger-completion.ps1 18 | # 19 | # 4. Restart your PowerShell session or source your profile again: 20 | # . $PROFILE 21 | 22 | 23 | function _logger { 24 | param($wordToComplete, $commandAst) 25 | $suggestions = @( 26 | 'help', 27 | 'usage', 28 | '--log-level', 29 | '--help', 30 | '--usage' 31 | ) 32 | return $suggestions | Where-Object { $_ -like "$wordToComplete*" } 33 | } 34 | 35 | Register-ArgumentCompleter -CommandName 'logger.exe' -ScriptBlock { 36 | param($wordToComplete, $commandAst, $cursorPos) 37 | 38 | $functionName = "_" + $($commandAst.Extent.Text.replace(' ', '-').replace(".exe", "")) 39 | if ($wordToComplete) { 40 | $functionName = $functionName.replace("-$wordToComplete", "") 41 | } 42 | 43 | # Check if the function exists and invoke it 44 | if (Get-Command -Name $functionName -ErrorAction SilentlyContinue) { 45 | & $functionName $wordToComplete $commandAst 46 | } else { 47 | # Fallback logic to show files in the current directory 48 | Get-ChildItem -Path '.' -File | Where-Object Name -like "*$wordToComplete*" | ForEach-Object { 49 | [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/generate.zig: -------------------------------------------------------------------------------- 1 | //! Functions to generate Help Docs, Tab Completion Scripts, and Argument Templates. 2 | 3 | // Standard 4 | const std = @import("std"); 5 | const fmt = std.fmt; 6 | const fs = std.fs; 7 | const log = std.log; 8 | const mem = std.mem; 9 | 10 | // Cova 11 | const utils = @import("utils.zig"); 12 | pub const help_docs = @import("generate/help_docs.zig"); 13 | pub const HelpDocsConfig = help_docs.HelpDocsConfig; 14 | pub const createHelpDoc = help_docs.createHelpDoc; 15 | pub const tab_completion = @import("generate/tab_completion.zig"); 16 | pub const TabCompletionConfig = tab_completion.TabCompletionConfig; 17 | pub const createTabCompletion = tab_completion.createTabCompletion; 18 | pub const arg_template = @import("generate/arg_template.zig"); 19 | pub const ArgTemplateConfig = arg_template.ArgTemplateConfig; 20 | pub const createArgTemplate = arg_template.createArgTemplate; 21 | 22 | /// Config for setting up all Meta Docs 23 | pub const MetaDocConfig = struct{ 24 | /// Specify which kinds of Meta Docs should be generated. 25 | kinds: []const MetaDocKind = &.{ .all }, 26 | /// Help Docs Config 27 | help_docs_config: ?HelpDocsConfig = .{}, 28 | /// Tab Completion Config 29 | tab_complete_config: ?TabCompletionConfig = .{}, 30 | /// Argument Template Config 31 | arg_template_config: ?ArgTemplateConfig = .{}, 32 | /// Command Type Name. 33 | /// This is the name of the Command Type declaration in the main source file. 34 | cmd_type_name: []const u8 = "CommandT", 35 | /// Setup Command Name. 36 | /// This is the name of the comptime Setup Command in the main source file. 37 | setup_cmd_name: []const u8 = "setup_cmd", 38 | 39 | /// Name of the program. 40 | /// Note, if this is left null, the provided CommandT's name will be used. 41 | name: ?[]const u8 = null, 42 | /// Description of the program. 43 | /// Note, if this is left null, the provided CommandT's description will be used. 44 | description: ?[]const u8 = null, 45 | /// Version of the program. 46 | version: ?[]const u8 = null, 47 | /// Date of the program version. 48 | ver_date: ?[]const u8 = null, 49 | /// Author of the program. 50 | author: ?[]const u8 = null, 51 | /// Copyright info. 52 | copyright: ?[]const u8 = null, 53 | 54 | /// Different Kinds of Meta Documents (Help Docs, Tab Completions, and Arg Templates) available. 55 | pub const MetaDocKind = enum { 56 | /// This is the same as adding All of the MetaDocKinds. 57 | all, 58 | /// Generate Manpages Help Docs. 59 | manpages, 60 | /// Generate Markdown Help Docs. 61 | markdown, 62 | /// Generate a Bash Tab Completion Script. 63 | bash, 64 | /// Generate a Zsh Tab Completion Script. 65 | zsh, 66 | /// Generate a PowerShell Tab Completion Script. 67 | ps1, 68 | /// Generate a JSON Argument Template. 69 | /// This is useful for parsing the main Command using external tools. 70 | json, 71 | /// Generate a KDL Argument Template for use with the [`usage`](https://github.com/jdx/usage) tool. 72 | kdl, 73 | }; 74 | }; 75 | 76 | -------------------------------------------------------------------------------- /src/generate/arg_template.zig: -------------------------------------------------------------------------------- 1 | //! Generate Argument Templates that can be easily parsed in external programs. 2 | 3 | // Standard 4 | const std = @import("std"); 5 | const fmt = std.fmt; 6 | const fs = std.fs; 7 | const json = std.json; 8 | const log = std.log; 9 | const mem = std.mem; 10 | 11 | // Cova 12 | const utils = @import("../utils.zig"); 13 | 14 | /// The Command Template Type, built from a Command Type. 15 | pub fn CommandTemplate(CommandT: type) type { 16 | return struct { 17 | const CmdT: type = CommandT; 18 | const OptTemplateT: type = OptionTemplate(CommandT.OptionT); 19 | const ValTemplateT: type = ValueTemplate(CommandT.ValueT); 20 | 21 | /// Command Name 22 | name: []const u8, 23 | /// Command Description 24 | description: []const u8, 25 | /// Command Aliases 26 | aliases: ?[]const []const u8, 27 | /// Command Group 28 | group: ?[]const u8, 29 | /// Command Examples 30 | examples: ?[]const []const u8, 31 | 32 | /// Sub-Commands 33 | sub_cmds: ?[]const @This() = null, 34 | /// Options 35 | opts: ?[]const OptionTemplate(CommandT.OptionT) = null, 36 | /// Values 37 | vals: ?[]const ValueTemplate(CommandT.ValueT) = null, 38 | 39 | 40 | /// Create a Template from a Command (`cmd`) using the provided Argument Template Config (`at_config`). 41 | pub fn from(comptime cmd: CommandT, comptime at_config: ArgTemplateConfig) @This() { 42 | return .{ 43 | .name = cmd.name, 44 | .description = cmd.description, 45 | .aliases = cmd.alias_names, 46 | .group = cmd.cmd_group, 47 | .examples = cmd.examples, 48 | .sub_cmds = comptime subCmds: { 49 | if (!at_config.include_cmds) break :subCmds null; 50 | const sub_cmds = cmd.sub_cmds orelse break :subCmds null; 51 | var cmd_tmplts: [sub_cmds.len]@This() = undefined; 52 | for (sub_cmds, cmd_tmplts[0..]) |sub_cmd, *tmplt| tmplt.* = from(sub_cmd, at_config); 53 | const cmd_tmplts_out = cmd_tmplts; 54 | break :subCmds cmd_tmplts_out[0..]; 55 | }, 56 | .opts = comptime setOpts: { 57 | if (!at_config.include_opts) break :setOpts null; 58 | const opts = cmd.opts orelse break :setOpts null; 59 | var opt_tmplts: [opts.len]OptTemplateT = undefined; 60 | for (opts, opt_tmplts[0..]) |opt, *tmplt| tmplt.* = OptTemplateT.from(opt); 61 | const opt_tmplts_out = opt_tmplts; 62 | break :setOpts opt_tmplts_out[0..]; 63 | }, 64 | .vals = comptime setvals: { 65 | if (!at_config.include_vals) break :setvals null; 66 | const vals = cmd.vals orelse break :setvals null; 67 | var val_tmplts: [vals.len]ValTemplateT = undefined; 68 | for (vals, val_tmplts[0..]) |val, *tmplt| tmplt.* = ValTemplateT.from(val); 69 | const val_tmplts_out = val_tmplts; 70 | break :setvals val_tmplts_out[0..]; 71 | }, 72 | }; 73 | } 74 | }; 75 | } 76 | 77 | /// The Option Template Type, built from an Option Type. 78 | pub fn OptionTemplate(OptionT: type) type { 79 | return struct { 80 | const OptT: type = OptionT; 81 | 82 | /// Option Name 83 | name: []const u8, 84 | /// Option Long Name 85 | long_name: ?[]const u8, 86 | /// Option Short Name 87 | short_name: ?u8, 88 | /// Option Description 89 | description: []const u8, 90 | /// Option Aliases 91 | aliases: ?[]const []const u8, 92 | /// Option Group 93 | group: ?[]const u8, 94 | 95 | /// Value Type Name 96 | type_name: []const u8, 97 | /// Value Type Alias 98 | type_alias: ?[]const u8 = null, 99 | /// Value Set Behavior 100 | set_behavior: []const u8, 101 | /// Value Max Entries 102 | max_entries: u8, 103 | 104 | /// Create a Template from an Option. 105 | pub fn from(comptime opt: OptionT) @This() { 106 | return .{ 107 | .name = opt.name, 108 | .long_name = opt.long_name, 109 | .short_name = opt.short_name, 110 | .description = opt.description, 111 | .aliases = opt.alias_long_names, 112 | .group = opt.opt_group, 113 | .type_name = opt.val.childType(), 114 | .type_alias = if (!mem.eql(u8, opt.val.childTypeName(), opt.val.childType())) opt.val.childTypeName() else null, 115 | .set_behavior = @tagName(opt.val.setBehavior()), 116 | .max_entries = opt.val.maxEntries(), 117 | }; 118 | } 119 | }; 120 | } 121 | 122 | /// The Value Template Type, built from a Value Type. 123 | pub fn ValueTemplate(ValueT: type) type { 124 | return struct { 125 | const ValT: type = ValueT; 126 | 127 | /// Value Name 128 | name: []const u8, 129 | /// Value Description 130 | description: []const u8, 131 | /// Value Group 132 | group: ?[]const u8, 133 | 134 | /// Value Type Name 135 | type_name: []const u8, 136 | /// Value Type Alias 137 | type_alias: ?[]const u8 = null, 138 | /// Value Set Behavior 139 | set_behavior: []const u8, 140 | /// Value Max Arguments 141 | max_entries: u8, 142 | 143 | /// Create a Template from a Value. 144 | pub fn from(comptime val: ValueT) @This() { 145 | return .{ 146 | .name = val.name(), 147 | .description = val.description(), 148 | .group = val.valGroup(), 149 | .type_name = val.childType(), 150 | .type_alias = if (!mem.eql(u8, val.childTypeName(), val.childType())) val.childTypeName() else null, 151 | .set_behavior = @tagName(val.setBehavior()), 152 | .max_entries = val.maxEntries(), 153 | }; 154 | } 155 | }; 156 | } 157 | 158 | /// Meta Info Template 159 | pub const MetaInfoTemplate = struct{ 160 | /// Name of the program. 161 | name: ?[]const u8 = null, 162 | /// Description of the program. 163 | description: ?[]const u8 = null, 164 | /// Version of the program. 165 | version: ?[]const u8 = null, 166 | /// Date of the program version. 167 | ver_date: ?[]const u8 = null, 168 | /// Author of the program. 169 | author: ?[]const u8 = null, 170 | /// Copyright info. 171 | copyright: ?[]const u8 = null, 172 | }; 173 | 174 | /// Config for creating Argument Templates with `createArgTemplate()`. 175 | pub const ArgTemplateConfig = struct{ 176 | /// Script Local Filepath 177 | /// This is the local path the file will be placed in. The file name will be "`name`-template.`template_kind`". 178 | local_filepath: []const u8 = "meta/arg_templates", 179 | 180 | /// Name of the program. 181 | /// Note, if this is left null, the provided CommandT's name will be used. 182 | name: ?[]const u8 = null, 183 | /// Description of the program. 184 | /// Note, if this is left null, the provided CommandT's description will be used. 185 | description: ?[]const u8 = null, 186 | /// Version of the program. 187 | version: ?[]const u8 = null, 188 | /// Date of the program version. 189 | ver_date: ?[]const u8 = null, 190 | /// Author of the program. 191 | author: ?[]const u8 = null, 192 | /// Copyright info. 193 | copyright: ?[]const u8 = null, 194 | 195 | /// Include Commands for Argument Templates. 196 | include_cmds: bool = true, 197 | /// Include Options for Argument Templates. 198 | include_opts: bool = true, 199 | /// Include Values for Argument Templates. 200 | include_vals: bool = true, 201 | 202 | /// Available Kinds of Argument Template formats. 203 | pub const TemplateKind = enum{ 204 | json, 205 | kdl, 206 | }; 207 | }; 208 | /// Create an Argument Template. 209 | pub fn createArgTemplate( 210 | comptime CommandT: type, 211 | comptime cmd: CommandT, 212 | comptime at_config: ArgTemplateConfig, 213 | comptime at_kind: ArgTemplateConfig.TemplateKind 214 | ) !void { 215 | const at_name = at_config.name orelse cmd.name; 216 | const at_description = at_config.description orelse cmd.description; 217 | const filepath = genFilepath: { 218 | comptime var path = if (at_config.local_filepath.len >= 0) at_config.local_filepath else "."; 219 | comptime { if (mem.indexOfScalar(u8, &.{ '/', '\\' }, path[path.len - 1]) == null) path = path ++ "/"; } 220 | try fs.cwd().makePath(path); 221 | break :genFilepath path ++ at_name ++ "-template." ++ @tagName(at_kind); 222 | }; 223 | var arg_template = try fs.cwd().createFile(filepath, .{}); 224 | const at_writer = arg_template.writer(); 225 | defer arg_template.close(); 226 | 227 | const meta_info_template = MetaInfoTemplate{ 228 | .name = at_name, 229 | .description = at_description, 230 | .version = at_config.version, 231 | .ver_date = at_config.ver_date, 232 | .author = at_config.author, 233 | .copyright = at_config.copyright, 234 | }; 235 | const cmd_template = CommandTemplate(CommandT).from(cmd, at_config); 236 | 237 | const at_ctx = ArgTemplateContext{ 238 | .include_cmds = at_config.include_cmds, 239 | .include_opts = at_config.include_opts, 240 | .include_vals = at_config.include_vals, 241 | }; 242 | 243 | switch (at_kind) { 244 | .json => { 245 | const json_opts_config = json.StringifyOptions{ 246 | .whitespace = .indent_4, 247 | .emit_null_optional_fields = false, 248 | }; 249 | try at_writer.print("\"Meta Info\": ", .{}); 250 | try json.stringify( 251 | meta_info_template, 252 | json_opts_config, 253 | at_writer, 254 | ); 255 | try at_writer.print(",\n\"Arguments\": ", .{}); 256 | try json.stringify( 257 | cmd_template, 258 | json_opts_config, 259 | at_writer, 260 | ); 261 | }, 262 | .kdl => { 263 | try at_writer.print("# This KDL template is formatted to match the `usage` tool as detailed here: https://sr.ht/~jdx/usage/\n\n", .{}); 264 | try at_writer.print( 265 | \\name "{s}" 266 | \\bin "{s}" 267 | \\about "{s}" 268 | \\ 269 | , .{ 270 | at_name, 271 | at_name, 272 | at_description, 273 | } 274 | ); 275 | if (at_config.version) |ver| try at_writer.print("version \"{s}\"\n", .{ ver }); 276 | if (at_config.author) |author| try at_writer.print("author \"{s}\"\n", .{ author }); 277 | try at_writer.print("\n", .{}); 278 | try argTemplateKDL( 279 | CommandT, 280 | cmd, 281 | at_writer, 282 | at_ctx, 283 | ); 284 | }, 285 | } 286 | log.info("Generated '{s}' Argument Template for '{s}' into '{s}'.", .{ 287 | @tagName(at_kind), 288 | cmd.name, 289 | filepath, 290 | }); 291 | } 292 | 293 | pub const ArgTemplateContext = struct{ 294 | /// Argument Index 295 | idx: u8 = 0, 296 | /// Add a spacer line 297 | add_line: bool = false, 298 | 299 | /// Include Commands for Argument Templates. 300 | include_cmds: bool = true, 301 | /// Include Options for Argument Templates. 302 | include_opts: bool = true, 303 | /// Include Values for Argument Templates. 304 | include_vals: bool = true, 305 | }; 306 | 307 | /// Writes a Argument Template in the KDL for the provided CommandT (`cmd`) to the given Writer (`at_writer`). 308 | /// This function passes the provided ArgumentTemplateContext (`at_ctx`) to track info through recursive calls. 309 | fn argTemplateKDL( 310 | comptime CommandT: type, 311 | comptime cmd: CommandT, 312 | at_writer: anytype, 313 | comptime at_ctx: ArgTemplateContext, 314 | ) !void { 315 | const sub_args: bool = ( 316 | (at_ctx.include_cmds and cmd.sub_cmds != null) or 317 | (at_ctx.include_opts and cmd.opts != null) or 318 | (at_ctx.include_vals and cmd.vals != null) or 319 | cmd.alias_names != null 320 | ); 321 | // if (sub_args and at_ctx.add_line) try at_writer.print("\n", .{}); 322 | if (at_ctx.add_line) try at_writer.print("\n", .{}); 323 | const indent = if (at_ctx.idx > 1) " " ** (at_ctx.idx - 1) else ""; 324 | const sub_indent = if (at_ctx.idx > 0) " " ** (at_ctx.idx) else ""; 325 | if (at_ctx.idx > 0) { 326 | try at_writer.print("{s}cmd \"{s}\" help=\"{s}\"{s}\n", .{ 327 | indent, 328 | cmd.name, 329 | cmd.description, 330 | if (sub_args) " {" else "", 331 | }); 332 | } 333 | 334 | var add_line = false; 335 | 336 | if (cmd.alias_names) |aliases| addAliases: { 337 | if (at_ctx.idx == 0) break :addAliases; 338 | inline for (aliases) |alias| try at_writer.print("{s}alias \"{s}\"\n", .{ sub_indent, alias }); 339 | add_line = true; 340 | } 341 | 342 | if (at_ctx.include_opts) addOpts: { 343 | const opts = cmd.opts orelse { 344 | add_line = false; 345 | break :addOpts; 346 | }; 347 | if (add_line) try at_writer.print("\n", .{}); 348 | // TODO Better handling of prefixes. Check if the usage tool supports alternate prefixes. 349 | inline for (opts) |opt| try at_writer.print("{s}flag \"{s}{s}\" help=\"{s}\"\n", .{ 350 | sub_indent, 351 | if (opt.short_name) |short| fmt.comptimePrint("-{c},", .{ short }) else "", 352 | if (opt.long_name) |long| fmt.comptimePrint("--{s}", .{ long }) else "", 353 | opt.description, 354 | }); 355 | add_line = true; 356 | } 357 | 358 | if (at_ctx.include_vals) addVals: { 359 | const vals = cmd.vals orelse { 360 | add_line = false; 361 | break :addVals; 362 | }; 363 | if (add_line) try at_writer.print("\n", .{}); 364 | inline for (vals) |val| try at_writer.print("{s}arg \"{s}\" help=\"{s}\"\n", .{ 365 | sub_indent, 366 | val.name(), 367 | val.description(), 368 | }); 369 | add_line = true; 370 | } 371 | 372 | if (at_ctx.include_cmds) addCmds: { 373 | const sub_cmds = cmd.sub_cmds orelse { 374 | add_line = false; 375 | break :addCmds; 376 | }; 377 | if (add_line) try at_writer.print("\n", .{}); 378 | inline for (sub_cmds, 0..) |sub_cmd, idx| { 379 | comptime var sub_ctx = at_ctx; 380 | sub_ctx.idx += 1; 381 | sub_ctx.add_line = idx > 0; 382 | try argTemplateKDL(CommandT, sub_cmd, at_writer, sub_ctx); 383 | } 384 | } 385 | 386 | if (at_ctx.idx > 0 and sub_args) try at_writer.print("{s}}}\n", .{ indent }); 387 | } 388 | -------------------------------------------------------------------------------- /src/generator.zig: -------------------------------------------------------------------------------- 1 | //! Meta Doc Generation for Cova-based programs 2 | //! This is meant to be built and run as a step in `build.zig`. 3 | 4 | // Standard library 5 | const std = @import("std"); 6 | const heap = std.heap; 7 | const json = std.json; 8 | const log = std.log; 9 | const mem = std.mem; 10 | const meta = std.meta; 11 | const Build = std.Build; 12 | // The Cova library is needed for the `generate` module. 13 | const cova = @import("cova"); 14 | const generate = cova.generate; 15 | const utils = cova.utils; 16 | 17 | /// This is a reference module for the program being built. Typically this is the main `.zig` file 18 | /// in a project that has both the `main()` function and `setup_cmd` Command. 19 | const program = @import("program"); 20 | /// This is a reference to the Build Options passed in from `build.zig`. 21 | const md_config = @import("md_config_opts"); 22 | /// Help Docs Config 23 | const help_docs_config = optsToConf(generate.HelpDocsConfig, @import("help_docs_config")); 24 | /// Tab Completion Config 25 | const tab_complete_config = optsToConf(generate.TabCompletionConfig, @import("tab_complete_config")); 26 | /// Argument Template Config 27 | const arg_template_config = optsToConf(generate.ArgTemplateConfig, @import("arg_template_config")); 28 | 29 | const meta_info: []const []const u8 = &.{ 30 | "version", 31 | "ver_date", 32 | "name", 33 | "description", 34 | "author", 35 | "copyright", 36 | }; 37 | /// Translate Build Options to Meta Doc Generation Configs. 38 | ///TODO Refactor this once Build Options support Types. 39 | fn optsToConf(comptime ConfigT: type, comptime conf_opts: anytype) ?ConfigT { 40 | if (!conf_opts.provided) return null; 41 | var conf = ConfigT{}; 42 | for (@typeInfo(ConfigT).@"struct".fields) |field| { 43 | if (std.mem.eql(u8, field.name, "provided")) continue; 44 | @field(conf, field.name) = @field(conf_opts, field.name); 45 | if ( 46 | ( 47 | @typeInfo(@TypeOf(@field(conf, field.name))) == .optional and 48 | @field(conf, field.name) != null 49 | ) or 50 | utils.indexOfEql([]const u8, meta_info, field.name) == null 51 | ) continue; 52 | @field(conf, field.name) = @field(md_config, field.name); 53 | } 54 | return conf; 55 | } 56 | 57 | pub fn main() !void { 58 | const doc_kinds: []const generate.MetaDocConfig.MetaDocKind = comptime docKinds: { 59 | var kinds: [md_config.kinds.len]generate.MetaDocConfig.MetaDocKind = undefined; 60 | for (md_config.kinds, kinds[0..]) |md_kind, *kind| kind.* = @enumFromInt(md_kind); 61 | if (kinds[0] != .all) { 62 | const kinds_out = kinds; 63 | break :docKinds kinds_out[0..]; 64 | } 65 | const mdk_info = @typeInfo(generate.MetaDocConfig.MetaDocKind); 66 | var kinds_list: [mdk_info.@"enum".fields[1..].len]generate.MetaDocConfig.MetaDocKind = undefined; 67 | for (mdk_info.@"enum".fields[1..], kinds_list[0..]) |field, *kind| kind.* = @enumFromInt(field.value); 68 | const kinds_out = kinds_list; 69 | break :docKinds kinds_out[0..]; 70 | }; 71 | 72 | const cmd_type_name = @field(program, md_config.cmd_type_name); 73 | const setup_cmd_name = @field(program, md_config.setup_cmd_name); 74 | 75 | log.info("\nStarting Meta Doc Generation...", .{}); 76 | inline for (doc_kinds[0..]) |kind| { 77 | switch (kind) { 78 | .manpages, .markdown => |help_doc| { 79 | if (help_docs_config) |hd_config| { 80 | try generate.createHelpDoc( 81 | cmd_type_name, 82 | setup_cmd_name, 83 | hd_config, 84 | meta.stringToEnum(generate.HelpDocsConfig.DocKind, @tagName(help_doc)).?, 85 | ); 86 | } 87 | else { 88 | log.warn("Missing Help Doc Configuration! Skipping.", .{}); 89 | continue; 90 | } 91 | }, 92 | .bash, .zsh, .ps1 => |shell| { 93 | if (tab_complete_config) |tc_config| { 94 | try generate.createTabCompletion( 95 | cmd_type_name, 96 | setup_cmd_name, 97 | tc_config, 98 | meta.stringToEnum(generate.TabCompletionConfig.ShellKind, @tagName(shell)).?, 99 | ); 100 | } 101 | else { 102 | log.warn("Missing Tab Completion Configuration! Skipping.", .{}); 103 | continue; 104 | } 105 | }, 106 | .json, .kdl => |template| { 107 | if (arg_template_config) |at_config| { 108 | try generate.createArgTemplate( 109 | cmd_type_name, 110 | setup_cmd_name, 111 | at_config, 112 | meta.stringToEnum(generate.ArgTemplateConfig.TemplateKind, @tagName(template)).?, 113 | ); 114 | } 115 | else { 116 | log.warn("Missing Argument Template Configuration! Skipping.", .{}); 117 | continue; 118 | } 119 | }, 120 | .all => {}, 121 | } 122 | } 123 | log.info("Finished Meta Doc Generation!", .{}); 124 | } 125 | -------------------------------------------------------------------------------- /src/utils.zig: -------------------------------------------------------------------------------- 1 | //! Utility Functions for the Cova Library. 2 | 3 | // Standard 4 | const std = @import("std"); 5 | const ascii = std.ascii; 6 | const mem = std.mem; 7 | const meta = std.meta; 8 | 9 | // Cova 10 | const Command = @import("Command.zig"); 11 | const Option = @import("Option.zig"); 12 | const Value = @import("Value.zig"); 13 | 14 | 15 | /// Display what is captured within a Command (`display_cmd`) after Cova parsing. 16 | pub fn displayCmdInfo( 17 | comptime CommandT: type, 18 | display_cmd: *const CommandT, 19 | alloc: mem.Allocator, 20 | writer: anytype, 21 | hide_unset: bool, 22 | ) !void { 23 | var cur_cmd: ?*const CommandT = display_cmd; 24 | while (cur_cmd) |cmd| { 25 | try writer.print("- Command: {s}\n", .{ cmd.name }); 26 | if (cmd.opts) |opts| { 27 | for (opts) |opt| { 28 | if (hide_unset and !opt.val.isSet()) continue; 29 | try displayValInfo(CommandT.ValueT, opt.val, opt.long_name, true, alloc, writer); 30 | } 31 | } 32 | if (cmd.vals) |vals| { 33 | for (vals) |val| { 34 | if (hide_unset and !val.isSet()) continue; 35 | try displayValInfo(CommandT.ValueT, val, val.name(), false, alloc, writer); 36 | } 37 | } 38 | try writer.print("\n", .{}); 39 | cur_cmd = cmd.sub_cmd; 40 | } 41 | } 42 | 43 | /// Display what is captured within an Option or Value after Cova parsing. 44 | /// Meant for use within `displayCmdInfo()`. 45 | fn displayValInfo( 46 | comptime ValueT: type, 47 | val: ValueT, 48 | name: ?[]const u8, 49 | isOpt: bool, 50 | alloc: mem.Allocator, 51 | writer: anytype, 52 | ) !void { 53 | const prefix = if (isOpt) "Opt" else "Val"; 54 | 55 | switch (meta.activeTag(val.generic)) { 56 | .string => { 57 | const str_vals = val.generic.string.getAllAlloc(alloc) catch noVal: { 58 | const no_val = alloc.dupe([]const u8, &.{ "" }) catch @panic("OOM"); 59 | break :noVal no_val; 60 | }; 61 | defer alloc.free(str_vals); 62 | const full_str = mem.join(alloc, "\" \"", str_vals) catch @panic("OOM"); 63 | defer alloc.free(full_str); 64 | try writer.print(" {s}: {?s}, Data: \"{s}\"\n", .{ 65 | prefix, 66 | name, 67 | full_str, 68 | }); 69 | }, 70 | inline else => |tag| { 71 | const tag_self = @field(val.generic, @tagName(tag)); 72 | if (tag_self.set_behavior == .Multi) { 73 | const raw_data: ?[]const @TypeOf(tag_self).ChildT = rawData: { 74 | if (tag_self.getAllAlloc(alloc) catch null) |data| break :rawData data; 75 | const data: ?@TypeOf(tag_self).ChildT = tag_self.get() catch null; 76 | if (data) |_data| { 77 | var data_slice = alloc.alloc(@TypeOf(tag_self).ChildT, 1) catch @panic("OOM"); 78 | data_slice[0] = _data; 79 | break :rawData data_slice; 80 | } 81 | break :rawData null; 82 | }; 83 | defer if (raw_data) |raw| alloc.free(raw); 84 | try writer.print(" {s}: {?s}, Data: {any}\n", .{ 85 | prefix, 86 | name, 87 | raw_data, 88 | }); 89 | } 90 | else { 91 | try writer.print(" {s}: {?s}, Data: {any}\n", .{ 92 | prefix, 93 | name, 94 | tag_self.get() catch null, 95 | }); 96 | } 97 | }, 98 | } 99 | } 100 | 101 | /// Find the Index of any Type, Scalar or Slice, (`needle`) within a Slice of that Type (`haystack`). (Why is this not in std.mem?!?!? Did I miss it?) 102 | pub fn indexOfEql(comptime T: type, haystack: []const T, needle: T) ?usize { 103 | @setEvalBranchQuota(10_000); 104 | switch (@typeInfo(T)) { 105 | .pointer => |ptr| { 106 | for (haystack, 0..) |hay, idx| if (mem.eql(ptr.child, hay, needle)) return idx; 107 | return null; 108 | }, 109 | inline else => return mem.indexOfScalar(T, haystack, needle), 110 | } 111 | } 112 | 113 | /// Find the Index of a String (`needle`) within a Slice of Strings `haystack`. (Why is this not in std.mem?!?!? Did I miss it?) 114 | pub fn indexOfEqlIgnoreCase(haystack: []const []const u8, needle: []const u8) ?usize { 115 | for (haystack, 0..) |hay, idx| if (ascii.eqlIgnoreCase(hay, needle)) return idx; 116 | return null; 117 | } 118 | --------------------------------------------------------------------------------