From efdde6103d9e9f7d3cb4677e4d4a7dca4cbc51bb Mon Sep 17 00:00:00 2001 From: Hugues Morisset Date: Fri, 3 Feb 2023 15:25:33 +0100 Subject: [PATCH] Add `crate` attribute to `hot_module` proc-macro Make it possible for users to specify what crate name is inside the generated module code. This allow usage with aliases crate name and re-export hot-lib-reloader-rs from another crate --- macro/src/hot_module/attribute.rs | 21 +++++++++++++++++++++ macro/src/hot_module/code_gen.rs | 19 ++++++++++--------- macro/src/hot_module/module_body.rs | 3 ++- tests/lib-loader-test.rs | 8 +++++--- 4 files changed, 38 insertions(+), 13 deletions(-) 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::::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::().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 { 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! { 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);