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}