{"id":14990711,"url":"https://github.com/thehonesthare/zkinder","last_synced_at":"2025-06-14T18:34:15.572Z","repository":{"id":253468763,"uuid":"843253655","full_name":"TheHonestHare/zkinder","owner":"TheHonestHare","description":"A pattern matching library for zig","archived":false,"fork":false,"pushed_at":"2024-09-02T13:16:17.000Z","size":32,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T22:22:11.506Z","etag":null,"topics":["zig","zig-library","zig-package"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheHonestHare.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-08-16T05:34:00.000Z","updated_at":"2025-01-20T00:48:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"2d4b1cd9-dc14-4865-974b-addf45cd9057","html_url":"https://github.com/TheHonestHare/zkinder","commit_stats":{"total_commits":19,"total_committers":2,"mean_commits":9.5,"dds":"0.21052631578947367","last_synced_commit":"46e060d8a95a5b15bc0d0c423e8083431f434150"},"previous_names":["thehonesthare/zkinder"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheHonestHare%2Fzkinder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheHonestHare%2Fzkinder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheHonestHare%2Fzkinder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheHonestHare%2Fzkinder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheHonestHare","download_url":"https://codeload.github.com/TheHonestHare/zkinder/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248507866,"owners_count":21115688,"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":["zig","zig-library","zig-package"],"created_at":"2024-09-24T14:20:38.640Z","updated_at":"2025-04-12T02:43:44.563Z","avatar_url":"https://github.com/TheHonestHare.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"A library that implements pattern matching in fully userland zig. Tested on zig version 0.14.0-dev.1391+e084c46ed but should probably work on master\n\n## Code example\n```zig\nconst ki = @import(\"zkinder\");\nconst bind = ki.bind;\nconst __ = ki.__;\n\nconst thing: struct {\n    age: u32,\n    birth: u64,\n    pos: struct { x: u8, y: u8 } \n} = .{.age = 3, .birth = 2, .pos = .{.x = 90, .y = 20}};\n\nconst m = ki.match(\u0026thing);\n// bind puts the value of the variable in the result\nconst res = m.arm(.{.age = 3, .birth = bind(\"hello\"), .pos = .{.x = bind(\"x\"), .y = 20}});\ntry std.testing.expectEqual(2, res.?.hello);\ntry std.testing.expectEqual(90, res.?.x);\n\nconst res2 = m.arm(.{.age = 3, .birth = bind(\"hello\"), .pos = bind(\"pos\")});\ntry std.testing.expectEqual(2, res2.?.hello);\ntry std.testing.expectEqual(90, res2.?.pos.x);\ntry std.testing.expectEqual(20, res2.?.pos.y);\n\n// this arm doesn't match because age = 2, will return null\nconst res3 = m.arm(.{.age = 2, .birth = bind(\"hello\"), .pos = bind(\"pos\")});\ntry std.testing.expectEqual(null, res3);\n\n// __ will match on anything\nconst res4 = m.arm(.{.age = __, .birth = __, .pos = .{.x = __, .y = bind(\"y\")}});\ntry std.testing.expectEqual(20, res4.?.y);\n```\n## Current features:\n- arbitrary nested patterns\n  - structs\n  - union(enum) (TODO: more testing)\n  - enums (TODO: more testing)\n  - ints, floats, bools (TODO: more testing)\n  - optionals (TODO: more testing)\n    - match on double optionals using `nonnull`\n  - arrays\n    - can even match on a subarray using `ref_rest` (see \"custom array matchers\")\n  - single item pointers (patterns match on the child type)\n  - slices\n    - support same features as arrays, plus different length patterns\n- `matching` for no exhaustive checking, single arm\n- extracting values out via `bind`\n- matching aginst anything via `__`\n- matching on integer ranges via `range`\n- extracting values out only if it matches a predicate with `bind_if`\n- support for creating your own match predicates\n  - both `bind` and `__` use no special casing, you could implement yourself\n  - any custom matcher must be of type `fn (comptime type) CustomMatcher`\n  - the impl will take in this type, pass in the type it needs to bind to and collect all the needed captures\n  - TODO: make like 10x more ergonomic\n## Planned features:\n- matching on vectors, others maybe?\n- optimize this so its not just willy nilly checking basically\n- exhaustive patterns (hardmode)\n- unreachable pattern errors (if I feel like it)\n- more helper match predicates such as `partial`, `ref`,\n- safety check for ensuring a match only ever matches on one branch\n\n## Custom matchers:\nA custom matcher is any function of the form `fn (comptime type) Matcher`, where the input is the type it is being matched against. `bind(\u003cname\u003e)` and `__` are both custom matchers implementable you could just as easily implement\n\nFor example, in the pattern,\n\n`.{.age = 2, .birth = __, .pos = __}`\n\n`__` is actually a function which will take the types of `thing.birth` and `thing.pos` respectively. \n\n`Matcher` is defined as such:\n```zig\npub const Matcher = struct {\n    captures: type,\n    tryBind: fn (self: Matcher, val_ptr: anytype, out_ptr: anytype) bool,\n};\n```\n`captures` is a struct type where each field is an output the matcher produces. This is needed for features like `bind`. All fields are combined, using comptime, into one giant struct that is returned from the arm.\n\n`tryBind` is where all the magic happens.\n- `self` allows you to access the `captures` field\n- `val_ptr` is a pointer to the field being matched against. The fields type will be the same type that was passed into the custom matcher.\n- `out_ptr` lets you write to the `out` of the arm, if you had any captures. For example, `out_ptr.hello = 2` would write 2 to the `hello` field of the output. You can only write to fields that were already declared as captures\n- return `true` if the value in `val_ptr` matches whatever criteria you want, or `false` if not\n\n## Custom array matchers:\nThese are similar to custom matchers, but they only work in array or slice patterns. `ref_rest(\u003cname\u003e)` is a custom array matcher. Instead of having to have a pattern for the entire length of the array, you can match over part of it.\n\nYou can only have custom array matcher per array/slice pattern\n\n```test \"match: arrays\" {\n    const list = [_]u8{ 0, 9, 100, 140 };\n    const m = comptime match(\u0026list);\n    const res = m.arm(.{ 0, 9, 100, bind(\"num\") });\n    const res2 = m.arm(.{ ref_rest(\"first2\"), 100, 140 });\n    const res3 = m.arm(.{ bind(\"head\"), ref_rest(\"tail\") });\n    const res4 = m.arm(.{ 1, ref_rest(\"tail\") });\n    const res5 = m.arm(.{ 0, ref_rest(\"middle\"), 140 });\n\n    try std.testing.expectEqual(140, res.?.num);\n    try std.testing.expectEqualSlices(u8, \u0026.{ 0, 9 }, res2.?.first2);\n    try std.testing.expectEqual(0, res3.?.head);\n    try std.testing.expectEqualSlices(u8, \u0026.{ 9, 100, 140 }, res3.?.tail);\n    try std.testing.expectEqual(null, res4);\n    try std.testing.expectEqualSlices(u8, \u0026.{ 9, 100 }, res5.?.middle);\n}\n```\n\nTo make your own, you need a thing of type `fn (comptime type, comptime ?usize) SubSliceMatcher`. The first parameter is the child type of the array or slice, and the second is the length of the array, or null if it is a slice\nSubSliceMatcher has a `captures` field similar to a custom matcher, as well as a `tryBind` fn. The only difference is that instead of `val_ptr`, it will take `subslice`,\n\nIf the matched against type is an array, `subslice` will be a pointer to the subarray that is not matched against in the rest of the pattern\nIf the matched againt type is a slice, `subslice` will be the subslice that is not matched against in the rest of the pattern\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthehonesthare%2Fzkinder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthehonesthare%2Fzkinder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthehonesthare%2Fzkinder/lists"}