Repository: rksm/hot-lib-reloader-rs (language: rust)

Issue:
Allow specifying `crate = "something::hot_lib_reloader"` on hot_module! for when library is not a direct dependency
Hello,
In order to make it possible to re-export  `hot_lib_reloader` and use `hot_module` derive,  a new parameter `crate` should be added, to specify the crate name in the context where the proc-macro is called.

 I found other example of this in serde and tokio
 https://github.com/serde-rs/serde/blob/1fcda0ebdb2cfa050df42fded93ba07c14e4b683/test_suite/tests/test_serde_path.rs#L6
 
 https://docs.rs/tokio/latest/tokio/attr.main.html#rename-package
 


Discussion / hints:

Fix patch:
diff --git a/macro/src/hot_module/attribute.rs b/macro/src/hot_module/attribute.rs
index 582adaf..ebf7370 100644
--- a/macro/src/hot_module/attribute.rs
+++ b/macro/src/hot_module/attribute.rs
@@ -6,6 +6,7 @@ pub(crate) struct HotModuleAttribute {
     pub(crate) lib_name: syn::Expr,
     pub(crate) lib_dir: syn::Expr,
     pub(crate) file_watch_debounce_ms: syn::LitInt,
+    pub(crate) crate_name: syn::Path,
 }
 
 // Parses something like `#[hot(name = "lib")]`.
@@ -14,6 +15,7 @@ impl syn::parse::Parse for HotModuleAttribute {
         let mut lib_name = None;
         let mut lib_dir = None;
         let mut file_watch_debounce_ms = None;
+        let mut crate_name = None;
 
         let args = Punctuated::<syn::Expr, token::Comma>::parse_separated_nonempty(stream)?;
 
@@ -49,6 +51,19 @@ impl syn::parse::Parse for HotModuleAttribute {
                         continue;
                     }
 
+                    expr if expr_is_ident(&left, "crate") => {
+                        let span = expr.span().clone();
+                        let s = match match expr {
+                            syn::Expr::Lit(syn::ExprLit { lit, .. }) => lit,
+                            _ => return Err(Error::new(left.span(), "unexpected expression type")),
+                        } {
+                            syn::Lit::Str(s) => s,
+                            _ => return Err(Error::new(span, "unexpected expression type")),
+                        };
+                        crate_name = Some(s.parse::<syn::Path>().clone()?);
+                        continue;
+                    }
+
                     _ => return Err(Error::new(left.span(), "unexpected attribute name")),
                 },
 
@@ -82,10 +97,16 @@ impl syn::parse::Parse for HotModuleAttribute {
             Some(file_watch_debounce_ms) => file_watch_debounce_ms,
         };
 
+        let crate_name = match crate_name {
+            None => syn::parse_quote! { ::hot_lib_reloader },
+            Some(crate_name) => crate_name,
+        };
+
         Ok(HotModuleAttribute {
             lib_name,
             lib_dir,
             file_watch_debounce_ms,
+            crate_name,
         })
     }
 }
diff --git a/macro/src/hot_module/code_gen.rs b/macro/src/hot_module/code_gen.rs
index 06124f0..7497d9f 100644
--- a/macro/src/hot_module/code_gen.rs
+++ b/macro/src/hot_module/code_gen.rs
@@ -1,5 +1,5 @@
 use proc_macro2::Span;
-use syn::{token, Expr, FnArg, ItemFn, LitByteStr, LitStr, Result, VisPublic, Visibility};
+use syn::{token, Expr, Path, FnArg, ItemFn, LitByteStr, LitStr, Result, VisPublic, Visibility};
 use syn::{ForeignItemFn, LitInt};
 
 use crate::util::ident_from_pat;
@@ -8,13 +8,14 @@ pub(crate) fn generate_lib_loader_items(
     lib_dir: &Expr,
     lib_name: &Expr,
     file_watch_debounce_ms: &LitInt,
+    crate_name: &Path,
     span: Span,
 ) -> Result<proc_macro2::TokenStream> {
     let result = quote::quote_spanned! {span=>
-        static mut LIB_CHANGE_NOTIFIER: Option<::std::sync::Arc<::std::sync::RwLock<::hot_lib_reloader::LibReloadNotifier>>> = None;
+        static mut LIB_CHANGE_NOTIFIER: Option<::std::sync::Arc<::std::sync::RwLock<#crate_name::LibReloadNotifier>>> = None;
         static LIB_CHANGE_NOTIFIER_INIT: ::std::sync::Once = ::std::sync::Once::new();
 
-        fn __lib_notifier() -> ::std::sync::Arc<::std::sync::RwLock<::hot_lib_reloader::LibReloadNotifier>> {
+        fn __lib_notifier() -> ::std::sync::Arc<::std::sync::RwLock<#crate_name::LibReloadNotifier>> {
             LIB_CHANGE_NOTIFIER_INIT.call_once(|| {
                 let notifier = ::std::sync::Arc::new(::std::sync::RwLock::new(Default::default()));
                 // Safety: guarded by Once, will only be called one time.
@@ -28,7 +29,7 @@ pub(crate) fn generate_lib_loader_items(
             unsafe { LIB_CHANGE_NOTIFIER.as_ref().cloned().unwrap() }
         }
 
-        fn __lib_loader_subscription() -> ::hot_lib_reloader::LibReloadObserver {
+        fn __lib_loader_subscription() -> #crate_name::LibReloadObserver {
             // Make sure that LIB_LOADER_INIT ran and the change messages are
             // live, otherwise we would not get lib updates if none of the hot
             // functions are called.
@@ -39,7 +40,7 @@ pub(crate) fn generate_lib_loader_items(
                 .subscribe()
         }
 
-        static mut LIB_LOADER: Option<::std::sync::Arc<::std::sync::RwLock<::hot_lib_reloader::LibReloader>>> = None;
+        static mut LIB_LOADER: Option<::std::sync::Arc<::std::sync::RwLock<#crate_name::LibReloader>>> = None;
         static LIB_LOADER_INIT: ::std::sync::Once = ::std::sync::Once::new();
 
         // version counter that counts the reloads
@@ -47,9 +48,9 @@ pub(crate) fn generate_lib_loader_items(
         // for simple queries
         static WAS_UPDATED: ::std::sync::atomic::AtomicBool = ::std::sync::atomic::AtomicBool::new(false);
 
-        fn __lib_loader() -> ::std::sync::Arc<::std::sync::RwLock<::hot_lib_reloader::LibReloader>> {
+        fn __lib_loader() -> ::std::sync::Arc<::std::sync::RwLock<#crate_name::LibReloader>> {
             LIB_LOADER_INIT.call_once(|| {
-                let mut lib_loader = ::hot_lib_reloader::LibReloader::new(#lib_dir, #lib_name, Some(::std::time::Duration::from_millis(#file_watch_debounce_ms)))
+                let mut lib_loader = #crate_name::LibReloader::new(#lib_dir, #lib_name, Some(::std::time::Duration::from_millis(#file_watch_debounce_ms)))
                     .expect("failed to create hot reload loader");
 
                 let change_rx = lib_loader.subscribe_to_file_changes();
@@ -72,14 +73,14 @@ pub(crate) fn generate_lib_loader_items(
                                 if let Ok(mut lib_loader) = lib_loader_for_update.try_write() {
                                     if let Some(first_lock_attempt) = first_lock_attempt {
                                         let duration: ::std::time::Duration = first_lock_attempt - ::std::time::Instant::now();
-                                        ::hot_lib_reloader::LibReloader::log_info(&format!("...got write lock after {}ms!", duration.as_millis()));
+                                        #crate_name::LibReloader::log_info(&format!("...got write lock after {}ms!", duration.as_millis()));
                                     }
                                     let _ = !lib_loader.update().expect("hot lib update()");
                                     break;
                                 }
                                 if first_lock_attempt.is_none() {
                                     first_lock_attempt = Some(::std::time::Instant::now());
-                                    ::hot_lib_reloader::LibReloader::log_info("trying to get a write lock...");
+                                    #crate_name::LibReloader::log_info("trying to get a write lock...");
                                 }
                                 ::std::thread::sleep(::std::time::Duration::from_millis(1));
                             }
diff --git a/macro/src/hot_module/module_body.rs b/macro/src/hot_module/module_body.rs
index a8ced6c..58397fa 100644
--- a/macro/src/hot_module/module_body.rs
+++ b/macro/src/hot_module/module_body.rs
@@ -234,13 +234,14 @@ impl quote::ToTokens for HotModule {
             lib_name,
             lib_dir,
             file_watch_debounce_ms,
+            crate_name,
         } = match hot_module_args {
             None => panic!("Expected to have macro attributes"),
             Some(attributes) => attributes,
         };
 
         let lib_loader =
-            generate_lib_loader_items(lib_dir, lib_name, file_watch_debounce_ms, tokens.span())
+            generate_lib_loader_items(lib_dir, lib_name, file_watch_debounce_ms, crate_name, tokens.span())
                 .expect("error generating hot lib loader helpers");
 
         let module_def = quote::quote! {


Test patch:
diff --git a/tests/lib-loader-test.rs b/tests/lib-loader-test.rs
index 2f079c2..24867d6 100644
--- a/tests/lib-loader-test.rs
+++ b/tests/lib-loader-test.rs
@@ -1,11 +1,13 @@
 mod common;
 
-#[hot_lib_reloader::hot_module(dylib = "lib_for_testing", file_watch_debounce = 50)]
+use hot_lib_reloader as hlibr_crate_alias;
+
+#[hlibr_crate_alias::hot_module(dylib = "lib_for_testing", file_watch_debounce = 50, crate = "super::hlibr_crate_alias")]
 mod hot_lib {
     hot_functions_from_file!("tests/lib_for_testing/src/lib.rs");
 
     #[lib_change_subscription]
-    pub fn subscribe() -> hot_lib_reloader::LibReloadObserver {}
+    pub fn subscribe() -> super::hlibr_crate_alias::LibReloadObserver {}
 
     #[lib_version]
     pub fn version() -> usize {}
@@ -57,7 +59,7 @@ fn test() {
             // wait for reload to be completed
             lib_observer.wait_for_reload();
 
-            // make rue lib is new
+            // make sure lib is new
             let n = hot_lib::do_more_stuff(Box::new(hot_lib::do_stuff));
             assert_eq!(n, 7);
             assert_eq!(hot_lib::version(), 1);