hot_lib_reloader_macro/lib.rs
1mod hot_module;
2mod util;
3
4/// This macro is the top-level interface for making a dynamic Rust library
5/// hot-reloadable. The attribute macro will insert code into the module it
6/// accompanies that will do several things:
7///
8/// 1. In the context of that module a global
9/// [`hot_lib_reloader::LibReloader`](https://docs.rs/hot-lib-reloader/latest/hot_lib_reloader/struct.LibReloader.html)
10/// instance is maintained that loads the library specified by the `dylib`
11/// argument and provides access to its symbols.
12///
13/// 2. A thread is started that drives the `LibReloader`: It waits for library
14/// file changes and then
15/// [updates](https://docs.rs/hot-lib-reloader/latest/hot_lib_reloader/struct.LibReloader.html#method.update)
16/// the library.
17///
18/// 3. Allows access to a
19/// [`hot_lib_reloader::LibReloadNotifier`](https://docs.rs/hot-lib-reloader/latest/hot_lib_reloader/struct.LibReloadNotifier.html)
20/// that can be used to get events about library changes. See the
21/// `#[lib_change_subscription]` attribute below.
22///
23/// In addition, the module can contain normal items. You can define functions,
24/// types etc normally and you can import and export from other modules. In
25/// particular re-exporting all items from the target library can make it easy
26/// to create a 1:1 replacement with static modules from that library.
27///
28/// A few pseudo-macros can appear in the modules context:
29///
30/// ```ignore
31/// // The `dylib` attribute should be the name of the library to hot-reload,
32/// // typically the crate name.
33/// #[hot_module(dylib = "lib")]
34/// mod foo {
35///
36/// // reads `#[unsafe(no_mangle)]` public functions from `file.rs` and generates
37/// // forwarding functions in the context of this module that have the exact
38/// // same signatures. Those generated functions will automatically use the
39/// // newest version of the library.
40/// hot_functions_from_file!("path/to/file.rs");
41///
42/// // As an alternative to `hot_functions_from_file!` you can manually
43/// // declare functions that the library should export and for which hot-reload
44/// // implementations should be generated. It is more tedious but plays nicer
45/// // with tools like rust-analalyzer and auto completion.
46/// #[hot_function]
47/// pub fn do_stuff(arg: &str) -> u32 { /*generated*/ }
48///
49/// // Same as `hot_function` but as a block, multiple declarations are allowed.
50/// #[hot_functions]
51/// extern "Rust" {
52/// pub fn do_stuff(arg: &str) -> u32;
53/// }
54///
55/// // To get access to a `LibReloadObserver` you can create an empty function
56/// // with a `#[lib_change_subscription]` attribute.
57/// #[lib_change_subscription]
58/// pub fn subscribe() -> hot_lib_reloader::LibReloadObserver {}
59/// }
60/// ```
61///
62/// In case you get errors when using the macro or are generally curious, run
63/// `cargo expand` to see the generated code.
64#[proc_macro_attribute]
65pub fn hot_module(
66 args: proc_macro::TokenStream,
67 item: proc_macro::TokenStream,
68) -> proc_macro::TokenStream {
69 let args = syn::parse_macro_input!(args as hot_module::HotModuleAttribute);
70 let mut module = syn::parse_macro_input!(item as hot_module::HotModule);
71 module.hot_module_args = Some(args);
72
73 (quote::quote! { #module }).into()
74}