{"id":21736392,"url":"https://github.com/kyza/mitzvah","last_synced_at":"2025-07-25T10:33:34.080Z","repository":{"id":200907899,"uuid":"706507144","full_name":"Kyza/mitzvah","owner":"Kyza","description":"An alternative to `syn`.","archived":false,"fork":false,"pushed_at":"2023-10-18T05:15:07.000Z","size":16,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"trunk","last_synced_at":"2025-03-21T00:29:51.151Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","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/Kyza.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}},"created_at":"2023-10-18T05:00:09.000Z","updated_at":"2023-10-18T13:42:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"86505c77-7aa6-4cf7-a772-d31938d6ec70","html_url":"https://github.com/Kyza/mitzvah","commit_stats":null,"previous_names":["kyza/mitzvah"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Kyza/mitzvah","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fmitzvah","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fmitzvah/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fmitzvah/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fmitzvah/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kyza","download_url":"https://codeload.github.com/Kyza/mitzvah/tar.gz/refs/heads/trunk","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fmitzvah/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266990934,"owners_count":24017732,"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","status":"online","status_checked_at":"2025-07-25T02:00:09.625Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-26T05:18:38.216Z","updated_at":"2025-07-25T10:33:34.021Z","avatar_url":"https://github.com/Kyza.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mitzvah\r\n\r\n### Noun\r\n\r\n/ˈmɪtsvə/; Hebrew: מִצְוָה\r\n\r\n1. ~~Any of the 613 commandments of Jewish law.~~\r\n2. **An act of kindness, a good deed.**\r\n\r\nAn alternative to ~~`syn`~~ sin.\r\n\r\n## Features\r\n\r\n- [ ] Primitives\r\n  - [x] `Ident`\r\n  - [x] `Literal`\r\n    - [x] `LiteralKind`\r\n    - [x] `.suffix()`\r\n    - [x] `.kind()`\r\n  - [x] `Punct`\r\n  - [x] `Group`\r\n  - [x] `TokenTree`\r\n- [ ] Helper Tokens\r\n  - [ ] `Literal`Kinds\r\n  - [x] `MultiPunct`\r\n  - [ ] `Path`\r\n  - [ ] `Expr`\r\n  - [ ] `Fn`\r\n- [ ] `trait Macro`\r\n- [ ] Macro creation helper macros.\r\n\r\n## Why?\r\n\r\nI wanted to make my own opinionated library to create proc macros with \r\nwhile learning more about how the internals work without the abstractions \r\nof libraries.\r\n\r\n`mitzvah` is made to be close to `proc_macro`; the main functionality only \r\napplies traits to `proc_macro`'s primitives and `TokenStream` to extend \r\nfunctionality to feel like `syn`.\r\n\r\nTo create a new token `impl Token` on a struct, and you'll be able to use \r\nit in the extended `TokenStream::parse::\u003cCustomToken\u003e()` function.\r\n\r\n`mitzvah` also comes with some pre-built helper tokens such as all the \r\n`Literal`Kinds, `MultiPunct`, and `Path`.\r\n\r\n## What did you learn?\r\n\r\n### `proc_macro` is Lackluster\r\n\r\nRust has two places where it declares tokens.\r\n\r\n1. The root of `proc_macro`.\r\n2. Inside `proc_macro::bridge` (I'll call this `bridge` for brevity).\r\n\r\n`bridge` contains all the useful data, while `proc_macro` is a wrapper \r\nover the tokens defined there, and it has the internal `bridge` private.\r\n\r\nLet's take a look at token's definition in `proc_macro` compared to \r\nits definition in `bridge`.\r\n\r\n```rs\r\n// proc_macro\r\n\r\n#[derive(Clone)]\r\npub struct Literal(\r\n\t// Notice this is private.\r\n\tbridge::Literal\u003cbridge::client::Span, bridge::client::Symbol\u003e\r\n);\r\n\r\n// proc_macro::bridge\r\n\r\n// Notice the internal version has `Eq` and `PartialEq` while the wrapper \r\n// does not.\r\n#[derive(Clone, Eq, PartialEq)]\r\npub struct Literal\u003cSpan, Symbol\u003e {\r\n\t// The kind of literal such as `Str` and `Integer`.\r\n\tpub kind: LitKind,\r\n\t// The actual data such as `\"1.0f64\"`. \r\n\tpub symbol: Symbol,\r\n\t// The suffix if there is one such as `\"usize\"`.\r\n\tpub suffix: Option\u003cSymbol\u003e,\r\n\t\r\n\t// This is the only thing that you can access from `proc_macro`.\r\n\tpub span: Span,\r\n}\r\n```\r\n\r\n`bridge` has significantly more data--most of it being important parts--but \r\n`proc_macro` only exposes the `Span` from it.\r\n\r\nThis means if you want to only parse a token more specific than just \r\n`Literal` (like a string literal), you need to *reparse* data *from a \r\nstring* that was \u003cu\u003ealready parsed internally\u003c/u\u003e. This is obviously \r\nboth slower *and* more prone to bugs/inconsistencies.\r\n\r\n`bridge` can actually be accessed through an unstable feature called \r\n`proc_macro_internals`, but since `proc_macro` is what gets passed to you \r\nand because there's no way to convert between the two, it's useless.\r\n\r\nMy conclusion is Rust's built-in `proc_macro` module lacks the information \r\nneeded to effectively and safely build a macro without using external \r\nlibraries.\r\n\r\nWhat makes this extra disappointing is the data needed to solve this \r\nproblem already exists, but it's hidden behind private fields and \r\nincomplete-feeling wrappers.\r\n\r\nIn a world where this glorious data is exposed, a library like `syn` could \r\nbe reduced significantly to only include more complex tokens such as \r\n`Path` which aren't in `proc_macro` instead of having to re-implement all \r\nthe primitives just to provide a decent developer experience.\r\n\r\nNaturally, this sort of cut would make the library faster in both \r\ncompiletime and runtime.\r\n\r\n## What about testing?\r\n\r\nWhile I'd love to have testing tokens well-supported, `proc_macro` doesn't \r\nsupport being run in non-{proc macro} crates.\r\n\r\nTo solve this, `mitzvah` includes the feature `proc-macro2` which will \r\n`impl Token` on the primitives there instead. It also marks `syn` and \r\n`proc-macro2` as `optional` dependencies, so it will only [download, \r\ncompile, and use] them when the feature is set.\r\n\r\nYou can use the following in your `Config.toml` to automatically enable\r\n`feature = \"proc-macro2\"` for tests, but not for your macros in the real \r\nworld.\r\n```toml\r\n[dependencies]\r\nmitzvah = \"..\"\r\n\r\n[dev-dependencies]\r\nmitzvah = { version = \"..\", features = [\"proc-macro2\"] }\r\n```\r\n\r\nThe token primitives and more for the selected proc macro implementation are \r\nre-exported at `mitzvah::pm`, so you should use this to ensure testing \r\nworks properly.\r\n\r\nThis is far from ideal--especially considering the differences between \r\n`proc_macro` and `proc-macro2`--but it enables the testing of tokens you \r\ncreate with `mitzvah`.\r\n\r\n### Why not *just* use `proc-macro2`?\r\n\r\n`proc-macro2` is awesome, but the point of `mitzvah` is to be an extension \r\nof `proc_macro` rather than a wrapper over it--hence only including \r\nextension traits on the primitives and brand-new more complex tokens.\r\n\r\n`proc-macro2` actually uses \r\n[a runtime check](https://docs.rs/proc-macro2/1.0.69/src/proc_macro2/detection.rs.html#7-16) \r\nto determine whether it's running in a proc macro, while `mitzvah` \r\ndetermines its fallback at compiletime. It's likely optimized into atoms, \r\nbut in the end `compiletime \u003e runtime`.\r\n\r\nWith the way `mitzvah` works it could even provide its own feature-locked \r\nproc macro implementation, but that's way out of my scope.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyza%2Fmitzvah","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyza%2Fmitzvah","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyza%2Fmitzvah/lists"}