{"id":18158310,"url":"https://github.com/sam0x17/macro_magic","last_synced_at":"2025-04-09T18:23:01.576Z","repository":{"id":82773133,"uuid":"606307053","full_name":"sam0x17/macro_magic","owner":"sam0x17","description":"A collection of Rust proc macros that allow the exporting and importing of TokenStream2s of items in foreign contexts and files","archived":false,"fork":false,"pushed_at":"2023-10-10T16:53:22.000Z","size":346,"stargazers_count":38,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-04-24T18:42:07.668Z","etag":null,"topics":["proc-macro","proc-macro-attributes","rust-crate","rust-library","tokens"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/macro_magic","language":"Rust","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/sam0x17.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}},"created_at":"2023-02-25T04:59:36.000Z","updated_at":"2024-06-21T14:18:24.896Z","dependencies_parsed_at":null,"dependency_job_id":"000aa1c0-0ba4-446e-b03f-efaeb9f80566","html_url":"https://github.com/sam0x17/macro_magic","commit_stats":{"total_commits":311,"total_committers":3,"mean_commits":"103.66666666666667","dds":0.07395498392282962,"last_synced_commit":"3b21163ddc60fa580737220c9c3568809f773ba7"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sam0x17%2Fmacro_magic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sam0x17%2Fmacro_magic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sam0x17%2Fmacro_magic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sam0x17%2Fmacro_magic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sam0x17","download_url":"https://codeload.github.com/sam0x17/macro_magic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248085909,"owners_count":21045237,"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":["proc-macro","proc-macro-attributes","rust-crate","rust-library","tokens"],"created_at":"2024-11-02T07:06:17.343Z","updated_at":"2025-04-09T18:23:01.539Z","avatar_url":"https://github.com/sam0x17.png","language":"Rust","readme":"# Macro Magic 🪄\n\n[![Crates.io](https://img.shields.io/crates/v/macro_magic)](https://crates.io/crates/macro_magic)\n[![docs.rs](https://img.shields.io/docsrs/macro_magic?label=docs)](https://docs.rs/macro_magic/latest/macro_magic/)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/sam0x17/macro_magic/ci.yaml)](https://github.com/sam0x17/macro_magic/actions/workflows/ci.yaml?query=branch%3Amain)\n[![MIT License](https://img.shields.io/github/license/sam0x17/macro_magic)](https://github.com/sam0x17/macro_magic/blob/main/LICENSE)\n\nThis crate provides the `#[export_tokens]` macro and a number of companion macros, including\nthe `#[import_tokens_proc]` and `#[import_tokens_attr]` macros. When used in tandem with\n`#[export_tokens]`, these macros allow you to create regular and attribute proc macros in which\nyou can import and make use of the tokens of external/foreign items marked with\n`#[export_tokens]` in other modules, files, and even in other crates merely by referring to\nthem by name/path.\n\nAmong other things, the patterns introduced by `macro_magic` can be used to implement safe and\nefficient exportation and importation of item tokens within the same file, and even across file\nand crate boundaries. The only requirement is that you have control over (i.e. can add an attribute\nmacro to) the source code of both locations.\n\n`macro_magic` is designed to work with stable Rust, and is fully `no_std` compatible (in fact,\nthere is a unit test to ensure everything is `no_std` safe).\n\n## General Syntax\n\nYou can use `macro_magic` to build regular and attribute proc macros that look like this:\n\n```rust\n#[my_attribute(path::to::MyItem)]\ntrait SomeTrait {\n    // ..\n}\n```\n\nthis:\n\n```rust\ndo_something!(path::to::MyItem);\n```\n\nor even this:\n\n```rust\nlet foreign_tokens = my_macro!(path::to::MyItem);\nassert_eq!(foreign_tokens.to_string(), \"struct MyItem {...}\");\n```\n\nwhere `path::to::MyItem` is the path to an item that has been marked with `#[export_tokens]`.\n\nAll of this behavior is accomplished under the hood using proc macros that create\n`macro_rules`-based callbacks, but as a programmer this complexity is completely hidden from\nyou via simple attribute macros you can apply to your proc macros to imbue them with the power\nof importing the tokens for external items based on their path.\n\n## Attribute Example\n\nYou could write an attribute macro to \"inject\" the fields of one struct into\nanother as follows:\n\n```rust\n#[import_tokens_attr]\n#[proc_macro_attribute]\npub fn combine_structs(attr: TokenStream, tokens: TokenStream) -\u003e TokenStream {\n    let foreign_struct = parse_macro_input!(attr as ItemStruct);\n    let local_struct = parse_macro_input!(tokens as ItemStruct);\n    let Fields::Named(local_fields) = local_struct.fields else {\n        return Error::new(\n            local_struct.fields.span(),\n            \"unnamed fields are not supported\"\n        ).to_compile_error().into()\n    };\n    let Fields::Named(foreign_fields) = foreign_struct.fields else {\n        return Error::new(\n            foreign_struct.fields.span(),\n            \"unnamed fields are not supported\"\n        ).to_compile_error().into()\n    };\n    let local_fields = local_fields.named.iter();\n    let foreign_fields = foreign_fields.named.iter();\n    let attrs = local_struct.attrs;\n    let generics = local_struct.generics;\n    let ident = local_struct.ident;\n    let vis = local_struct.vis;\n    quote! {\n        #(#attrs)\n        *\n        #vis struct #ident\u003c#generics\u003e {\n            #(#local_fields),\n            *\n            ,\n            #(#foreign_fields),\n            *\n        }\n    }\n    .into()\n}\n```\n\nAnd then you could use the `#[combine_structs]` attribute as follows:\n\n```rust\n#[export_tokens]\nstruct ExternalStruct {\n    foo: u32,\n    bar: u64,\n    fizz: i64,\n}\n\n#[combine_structs(ExternalStruct)]\nstruct LocalStruct {\n    biz: bool,\n    baz: i32,\n}\n```\n\nWhich would result in the following expanded output for `LocalStruct`:\n\n```rust\nstruct LocalStruct {\n    foo: u32,\n    bar: u64,\n    fizz: i64,\n    biz: bool,\n    baz: i32,\n}\n```\n\nNote that the `attr` variable on the `combine_structs` proc macro, thanks to the powers of\n`#[import_tokens_attr]`, will receive the actual tokens for the `ExternalStruct` item, rather\nthan merely receiving the tokens for the path `ExternalStruct`.\n\nThis gives you the ability to write attribute macros that receive tokens for two items, one\nspecified by path via the first argument `attr`, as well as the tokens for the item the\nattribute is attached to via the 2nd argument `tokens`. The only requirement is that the item\nspecified by `attr` has been marked with `#[export_tokens]`.\n\n## Proc Macro Example\n\nYou could write a PHP/ruby/crystal-style verbatim import / `require` macro which blindly\nimports the tokens of the specified external module into the current context (with all the good\nand bad implications that would imply), like this:\n\n```rust\n#[import_tokens_proc]\n#[proc_macro]\npub fn require(tokens: TokenStream) -\u003e TokenStream {\n    let external_mod = parse_macro_input!(tokens as ItemMod);\n    let Some((_, stmts)) = external_mod.content else {\n        return Error::new(\n            external_mod.span(),\n            \"cannot import tokens from a file-based module since custom file-level \\\n            attributes are not yet supported by Rust\"\n        ).to_compile_error().into()\n    };\n    quote! {\n        #(#stmts)\n        *\n    }\n    .into()\n}\n```\n\nYou could then use the `require!` macro like this:\n\n```rust\n// in some external crate\n#[export_tokens]\nmod an_external_module {\n    fn my_cool_function() -\u003e u32 {\n        567\n    }\n\n    fn my_other_function() -\u003e u32 {\n        116\n    }\n}\n```\n\n```rust\n// in another crate where we will use the `require!` macro\nmod my_module {\n    use my_macros::require;\n\n    fn existing_stuff() {\n        println!(\"foo!\");\n    }\n\n    require!(external_crate::an_external_module);\n}\n```\n\nwhich would result in this expansion of `require!` within `my_module`:\n\n```rust\nmod my_module {\n    use my_macros::require;\n\n    fn existing_stuff() {\n        println!(\"foo!\");\n    }\n\n    fn my_cool_function() -\u003e u32 {\n        567\n    }\n\n    fn my_other_function() -\u003e u32 {\n        116\n    }\n}\n```\n\nNotice that this hypothetical `require!` macro is dangerous for two reasons:\n\n- Any types you may have brought into scope with `use` statements in the foreign module may or\n  may not be available in their new context without additional use statements.\n- If existing items in the module or context where you use the `require!` macro conflict with\n  something you are importing, you will get a compiler error (this is good, though).\n\nThese are just _some_ of the capabilities of `macro_magic` 🪄.\n\nSee the [`docs`](https://docs.rs/macro_magic/latest/macro_magic/) for more information.\n\n## Features\n\n### proc_support\n\nThe `proc_support` feature _must_ be enabled in proc macro crates that make use of any import\ntokens functionality, including `#[import_tokens_attr]`, `#[import_tokens_proc]` and\n`import_tokens!`. Otherwise these macros will not function correctly and will issue compiler\nerrors complaining about items not existing under `macro_magic::mm_core`. The\n`#[export_tokens]` macro does not require this feature to function correctly, so you can safely\nuse it without enabling this feature.\n\nThe reason for this feature gating is that things like `syn`, `quote`, `proc_macro2`, etc., are\nnot 100% `no_std` compatible and should only be enabled in proc macro crates. For this reason,\nyou _should not_ enable this feature in crates where you are merely using `#[export_tokens]`\nand nothing else within that crate.\n\n## Limitations\n\nOne thing that `macro_magic` _doesn't_ provide is the ability to build up state information\nacross multiple macro invocations, however this problem can be tackled effectively using the\n[outer macro pattern](https://www.youtube.com/watch?v=aEWbZxNCH0A) or in some cases using\nstatic atomics and mutexes in your proc macro crate (which we actually do in this crate to keep\ntrack of unique identifiers).\n\n## Breaking Changes\n\n- **0.4x** removed `#[use_attr]` and `#[use_proc]` (they are no longer needed with the new\n  self-calling macro style that has been adopted in 0.4x) and also removed the ability to\n  access `#[export_tokens]` invocations in inaccessible locations like inside of functions and\n  across module permission boundaries like in an inaccessible private module. This feature may\n  be re-added in the future if there is interest, however removing it allowed us to consolidate\n  naming of our `macro_rules!` declarations and remove the need for `#[use_attr]` /\n  `#[use_proc]`.\n- **0.2x** removed and/or re-wrote a number of features that relied on a non-future-proof\n  behavior of writing/reading files in the `OUT_DIR`. Versions \u003e= 0.2.0 are completely safe and\n  no longer contain this behavior, however features that provided the ability to enumerate all\n  the `#[export_tokens]` calls in a namespace have been removed. The proper way to do this is\n  with the outer macro pattern or with global state mutexes/atomics in your proc macro crate,\n  as mentioned above.\n\nMore detailed historical change information can be found in\n[releases](https://github.com/sam0x17/docify/releases).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsam0x17%2Fmacro_magic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsam0x17%2Fmacro_magic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsam0x17%2Fmacro_magic/lists"}