{"id":21699531,"url":"https://github.com/bitlytwiser/snek","last_synced_at":"2025-06-30T02:33:33.574Z","repository":{"id":264602147,"uuid":"860991875","full_name":"BitlyTwiser/snek","owner":"BitlyTwiser","description":"Snek - A simple CLI parser to build CLI applications in Zig","archived":false,"fork":false,"pushed_at":"2025-01-05T19:53:07.000Z","size":307,"stargazers_count":25,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-12T13:13:36.192Z","etag":null,"topics":["cli","snek","zig","zig-library","zig-package","ziglang"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BitlyTwiser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-21T17:52:35.000Z","updated_at":"2025-01-20T21:29:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"9fa8874e-9922-459d-8371-29926c23170d","html_url":"https://github.com/BitlyTwiser/snek","commit_stats":null,"previous_names":["bitlytwiser/snek"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/BitlyTwiser/snek","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitlyTwiser%2Fsnek","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitlyTwiser%2Fsnek/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitlyTwiser%2Fsnek/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitlyTwiser%2Fsnek/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BitlyTwiser","download_url":"https://codeload.github.com/BitlyTwiser/snek/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitlyTwiser%2Fsnek/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262699003,"owners_count":23350223,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","snek","zig","zig-library","zig-package","ziglang"],"created_at":"2024-11-25T20:09:59.256Z","updated_at":"2025-06-30T02:33:33.528Z","avatar_url":"https://github.com/BitlyTwiser.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e \n\n\u003cimg src=\"/assets/logo.png\" width=\"450\" height=\"500\"\u003e\n\n\n# 🐍snek🐍\nA simple CLI parser building CLI applications in Zig\n\n# Contents\n[Usage](#usage) |\n[Building the CLI](#build-your-cli) |\n[Examples](#examples) |\n[Optionals](#optionals) |\n[Default Values](#default-values) |\n[Help Menu](#help-menu) |\n[What is not supported](#what-is-not-supported)\n\n\u003c/div\u003e\n\n\n### Usage\nAdd snek to your Zig project with Zon:\n```\nzig fetch --save https://github.com/BitlyTwiser/snek/archive/refs/tags/v0.1.1.tar.gz\n```\n\nAdd the following to build.zig file:\n```\n    const snek = b.dependency(\"snek\", .{});\n    exe.root_module.addImport(\"snek\", snek.module(\"snek\"));\n```\n\n### Build your CLI\nSnek builds dynamic (yet simple) CLI's using zigs meta programming to infer the struct fields, the expected types, then insert the incoming data from the stdin arguments and serialize that data into the given struct mapping the data values to the given fields and marshalling the data into the proper type.\n\n```\n    const T = struct {\n        bool_test: bool,\n        word: []const u8,\n        test_opt: ?u32,\n        test_default: []const u8 = \"I am static if not set by user\",\n    };\n    var snek = try Snek(T).init(std.heap.page_allocator);\n    const parsed = try snek.parse();\n\n    // Do Stuff with the fields of the struct after parsing\n    std.debug.print(\"{any}\", .{parsed});\n```\n\nWhen the user goes to interact with the application, they can now utilize the flags you have established to run specific commands.\n\nSee the [completed](#example---full-execution) and functional example below or checkout the [main file](./src/main.zig) in the repo for a full working example as well.\n\n#### Items to note:\n1. If the user does not supply a value and the field is *not* otional, that is a failure case and a message is displayed to the user\n2. If there is a default value on the field of the struct and a vale is not passed for that field, it is treated as an *optional* case and will use the static value (i.e. no error message and value is set)\n3. Simple structs only for now, no recursive struct fields at the moment. (i.e. no embeded structs)\n4. If the users passed the wrong *type* which differes from what is expeected (i.e. the type of the struct field), this is an error case and a message will be displayed to the user.\n5. If you want to handle the errors yourself, the CliError struct is public, so you can catch errors on the `parse()` call\n```\n    const T = struct {\n        bool_test: bool,\n        word: []const u8,\n        test_opt: ?u32,\n        test_default: []const u8 = \"I am static if not set by user\",\n    };\n    var snek = try Snek(T).init(std.heap.page_allocator);\n    // Adjust to actually use value of course\n    _ = snek.parse() catch |err| {\n        switch(e) {\n            ... do stuff with the Errors\n        }\n    }\n\n```\n\n\n#### Examples\n\nUsing the above struct as a  reference, here are a few examples of calling the CLI:\n##### Example - Help Command\n```\n./\u003cyourappname\u003e -help\n\n# or\n\n./\u003cyourappname\u003e -h\n```\n\nNote: As you can see, the optionals are just that, *optional*. They are not required by your users and can be checked in the calling code in the standard ways that Zig handles optionals.\nThis is a design decisions allowing flexibility over the CLI to not lock users into using every flag etc..\n##### Example - Optionals\n````\n./\u003cyourappname\u003e -bool_test=true -word=\"I am a word!\"\n````\n\n##### Example - Defaults:\n```\n./\u003cyourappname\u003e -bool_test=true -word=\"I am a word!\"\n\n# or to override the default field\n\n\n./\u003cyourappname\u003e -bool_test=true -word=\"I am a word!\" -test_defaults=\"I am a different word!\"\n```\n\n\n##### Example - Full execution\n```\nconst std = @import(\"std\");\nconst snek = @import(\"lib.zig\").Snek;\n\n// Binary is also compiled for showcasing how to use the API\nconst T = struct {\n    name: []const u8,\n    location: u32,\n    exists: bool,\n    necessary: ?bool,\n    filled_optional: ?[]const u8,\n    default_name: []const u8 = \"test default name\",\n};\n\n// Example command after compilation:\n// ./zig-out/bin/snek -name=\"test mctest\" -location=420 -exists=true\npub fn main() !void {\n    var cli = try snek(T).init(std.heap.page_allocator);\n    const parsed_cli = try cli.parse();\n\n    // Necessary is skipped here to showcase optional values being ignored\n    std.debug.print(\"Name: {s}\\n Location: {d}\\n Exists: {any}\\n Defualt value: {s}\\n Filled Optional: {s}\\n\", .{ parsed_cli.name, parsed_cli.location, parsed_cli.exists, parsed_cli.default_name, parsed_cli.filled_optional orelse \"badvalue\" });\n}\n```\nCompile with `zig build` then run the cli command:\n```\n./zig-out/bin/snek -name=\"test mctest\" -location=420 -exists=true\n```\n\n#### Optionals\nUsing zig optionals, you can set selected flags to be ignored on the CLI, thus giving flexibilitiy on the behalf of the CLI creator to use or not use selected flags at their whimsy\n\n#### Default Values\nYou can use struct defaut values to set a static value if one is not parsed. This can be useful for certain flags for conditional logic branching later in program execution.\n\n### Help Menu\nSnek dynaically builds the help menu for your users. By calling the `help()` function, you can display how to use your CLI:\n```\n    const T = struct {\n        bool_test: bool,\n        word: []const u8,\n        test_opt: ?u32,\n    };\n    var snek = try Snek(T).init(std.heap.page_allocator);\n    const parsed = try snek.help();\n\n```\nOutput:\n```\nCLI Flags Help Menu\n---------\n-bool_test=Bool (optional: false)\n-word=Pointer (optional: false)\n-test_opt=Optional (optional: true)\n---------\n```\n\n\nAlternatively, if users call -help as the *first* arguments in the CLI, it will also display the help menu.\n```\n./\u003cyourappname\u003e -help\n\n# or\n\n./\u003cyourappname\u003e -h\n```\n\nThis will display the help menu and skip *all other parsing*. So its important to note that this is effectively an exit case for the parser and your program. \nYou should build your application to support this.\n\n\n### What is *not* supported\n\n##### Recursive struct types for sub-command fields\nAt this time, no recursive flags are supported, i.e. you cannot use a slice of structs as a field in the primary CLI interface struct and have those fields parsed as sub-command fields.\nPerhaps, if this is requested, we could work that into the application. It seemed slightly messy and unecessray for a simple CLI builder, but perhaps expansion will be necessary there if its requested :)\n\n[Top](#usage)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitlytwiser%2Fsnek","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitlytwiser%2Fsnek","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitlytwiser%2Fsnek/lists"}