Crates.io | include_proc_macro |
lib.rs | include_proc_macro |
version | 1.0.6 |
source | src |
created_at | 2024-06-20 10:03:20.174822 |
updated_at | 2024-06-24 09:37:35.471547 |
description | A simple shorthand for including proc-macro source files in the module tree for external tooling like IDEs or other similar purposes. |
homepage | https://github.com/orgrinrt/include_proc_macro |
repository | https://github.com/orgrinrt/include_proc_macro |
max_upload_size | |
id | 1277822 |
size | 19,508 |
A simple shorthand for including proc-macro source files in the module tree for external tooling like IDEs or other similar purposes.
The include_proc_macro
crate provides a macro designed for easy integration with external tooling, particularly
when working with procedural macros. It's extremely simple, just wraps an include!
macro call with some sugar, and is
primarily useful to reduce boilerplate and prettify proc-macro code.
Note:
include_proc_macro
is intended to be added as adependency
, though it is only a dev-time dependency. The macro itself returns nothing outside of debug assertions, and the compiler is intelligent enough to ignore it, so in practice it achieves the same, though it has to be available.
The macro checks if debug assertions are enabled (#[cfg(debug_assertions)]
). If debug assertions are enabled, it
includes a targeted .rs file from the Cargo project's root directory (obtained through the CARGO_MANIFEST_DIR
environment variable) in the module tree. Simple as that.
In Rust:
// lib.rs
include_proc_macro::include_proc_macro!("sample");
The above command includes sample.rs
from the root of the Cargo project during a debug assertion (development time).
It simply expands to:
#[cfg(debug_assertions)]
include!(
concat!(
env!("CARGO_MANIFEST_DIR"),
"/",
"sample", // <-- arg
".rs"
)
);
The main parameter of the macro is:
$file_name
: a string literal representing the name of the procedural macro source file (.rs) to be included in
the module tree during development time (this situationally helps enabling certain advanced IDE features).You'll want to use the macro in a location that your development tools recognize as part of the module tree. In many
cases, this means using it in the lib.rs
file of the procedural macro crate.
Note: Please remember that you should not use or depend on the procedural macro code being exposed beyond the confines of its crate. This configuration is designed to function in the reverse direction: it introduces features like auto-completion into the procedural macro crate during development. That's the reason why we only include them during debug assertions.
For some IDEs or other kinds of programming environments, understanding the module tree is crucial as it improves the developer experience by providing features like auto-completion and stable syncing. This is situational, and the way these are handled vary, but sometimes proc-macros can be a problem.
Simply put, in Rust, the module tree is a hierarchical representation of your code's organization. Every Rust file can act as a module, with submodules nested within, creating a tree-like structure that evolves alongside your project.
Procedural macro crates do exist in this module tree. However, their source files often live in the root of the
crate and not necessarily inside a lib.rs
file. Furthermore, they aren't necessarily explicitly declared as modules.
These deviations from typical Rust code can cause difficulties for development tools that rely on a "conventional"
project structure.
This isn't a universal issue, as it relies heavily on how each tool attempts to interpret procedural macros.
One practical workaround is to include the procedural macro code inside a lib.rs
file within the procedural macro
crate, particularly during dev time, so as to not cause problems on release (or other non-debug) builds. This decision
might make the macros more
accessible to certain development
tools, potentially improving discovery, auto-completion, syntax highlighting, and documentation support.
This is what this crate does.
Alternatively, you could use the #[path]
attribute along a module definition to point to the source file(s) in
the cargo root, which in practice achieves pretty much the same. Some IDEs and environments also just simply work
well with proc-macros, so a workaround
isn't necessary in the first place.
While the solution provided by this crate is simple and effective, it’s not one-size-fits-all. The best method depends on multiple factors like your tooling setup, personal preference, and the specific needs of your project. Still, the existence of this crate provides a simple solution to the problem; to connect your procedural macro crate with the rest of your code, making it more discoverable by external tools.
Additionally, include_proc_macro provides two convenient shorthand aliases, here!
and named!
:
include_proc_macro::here!("sample");
Please note that using these aliases will yield the same result as directly using include_proc_macro. They are included for convenience and for prettier code (i.e for when you want to / have to use fully qualified paths).
Note: Seeing as (unused) macros do not introduce a compile-time or runtime overhead, and the namespace pollution is both minimal and unlikely to clash or otherwise cause problems, having these aliases seems okay to me. However, if it turns out to be undesirable, we'll hide these behind a feature flag.
Whether you use this project, have learned something from it, or just like it, please consider supporting it by buying me a coffee, so I can dedicate more time on open-source projects like this :)
You can check out the full license here
This project is licensed under the terms of the MIT license.