From 348b03d4eac176a053c0856ef20e5fcc4492e850 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Thu, 12 Nov 2020 10:00:28 +0100 Subject: [PATCH 1/7] Re-export wasm-bindgen --- src/lib.rs | 3 +++ weblog-proc-macro/src/weblog_impl.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1644e59..922cf68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,5 +83,8 @@ pub use weblog_proc_macro::*; #[cfg(feature = "web_sys")] pub use ::web_sys; +#[cfg(feature = "web_sys")] +pub use ::wasm_bindgen; + #[cfg(feature = "std_web")] pub use self::console::std_web::*; diff --git a/weblog-proc-macro/src/weblog_impl.rs b/weblog-proc-macro/src/weblog_impl.rs index 1deb450..b983458 100644 --- a/weblog-proc-macro/src/weblog_impl.rs +++ b/weblog-proc-macro/src/weblog_impl.rs @@ -69,7 +69,7 @@ fn quote_arg(arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { quote! { #arg } } ArgMode::IntoJsValue => { - quote! { &::std::convert::Into::<::wasm_bindgen::JsValue>::into(#arg) } + quote! { &::std::convert::Into::<::weblog::wasm_bindgen::JsValue>::into(#arg) } } } } -- 2.40.1 From 160130a29e18e66c4bbdf6b59dc39c72f5c1b3a0 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Thu, 12 Nov 2020 17:48:09 +0100 Subject: [PATCH 2/7] WIP: support re-exporting macros --- src/console/mod.rs | 2 + src/console/web_sys.rs | 280 +++++++++++++++++++++++++++ src/lib.rs | 9 +- weblog-proc-macro/src/weblog_impl.rs | 30 ++- 4 files changed, 305 insertions(+), 16 deletions(-) create mode 100644 src/console/web_sys.rs diff --git a/src/console/mod.rs b/src/console/mod.rs index c80f14c..7594c1b 100644 --- a/src/console/mod.rs +++ b/src/console/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature = "std_web")] pub mod std_web; +#[cfg(feature = "web_sys")] +pub mod web_sys; diff --git a/src/console/web_sys.rs b/src/console/web_sys.rs new file mode 100644 index 0000000..0ce70db --- /dev/null +++ b/src/console/web_sys.rs @@ -0,0 +1,280 @@ +#[doc(hidden)] +pub use ::wasm_bindgen; +#[doc(hidden)] +pub use ::web_sys; + +// It is necessary to expose these wrapper macros in order to pass the $crate +// meta-variable to the proc macro. This in turn allows the macros to be +// re-exported from third-party crates without breakage. +// +// TODO: a declarative macro to remove the duplication here would be nice. + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_assert as __weblog_console_assert; + +#[macro_export] +/// Call the browser's `console.assert()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/assert) +macro_rules! console_assert { + ($($input:tt)*) => { + $crate::__weblog_console_assert! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_clear as __weblog_console_clear; + +#[macro_export] +/// Call the browser's `console.clear()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/clear) +macro_rules! console_clear { + ($($input:tt)*) => { + $crate::__weblog_console_clear! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_count as __weblog_console_count; + +#[macro_export] +/// Call the browser's `console.count()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/count) +macro_rules! console_count { + ($($input:tt)*) => { + $crate::__weblog_console_count! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_count_reset as __weblog_console_count_reset; + +#[macro_export] +/// Call the browser's `console.count_reset()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/count_reset) +macro_rules! console_count_reset { + ($($input:tt)*) => { + $crate::__weblog_console_count_reset! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_debug as __weblog_console_debug; + +#[macro_export] +/// Call the browser's `console.debug()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/debug) +macro_rules! console_debug { + ($($input:tt)*) => { + $crate::__weblog_console_debug! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_dir as __weblog_console_dir; + +#[macro_export] +/// Call the browser's `console.dir()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/dir) +macro_rules! console_dir { + ($($input:tt)*) => { + $crate::__weblog_console_dir! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_dirxml as __weblog_console_dirxml; + +#[macro_export] +/// Call the browser's `console.dirxml()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/dirxml) +macro_rules! console_dirxml { + ($($input:tt)*) => { + $crate::__weblog_console_dirxml! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_error as __weblog_console_error; + +#[macro_export] +/// Call the browser's `console.error()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/error) +macro_rules! console_error { + ($($input:tt)*) => { + $crate::__weblog_console_error! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_exception as __weblog_console_exception; + +#[macro_export] +/// Call the browser's `console.exception()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/exception) +macro_rules! console_exception { + ($($input:tt)*) => { + $crate::__weblog_console_exception! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_info as __weblog_console_info; + +#[macro_export] +/// Call the browser's `console.info()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/info) +macro_rules! console_info { + ($($input:tt)*) => { + $crate::__weblog_console_info! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_log as __weblog_console_log; + +#[macro_export] +/// Call the browser's `console.log()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) +macro_rules! console_log { + ($($input:tt)*) => { + $crate::__weblog_console_log! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_table as __weblog_console_table; + +#[macro_export] +/// Call the browser's `console.table()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/table) +macro_rules! console_table { + ($($input:tt)*) => { + $crate::__weblog_console_table! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_time as __weblog_console_time; + +#[macro_export] +/// Call the browser's `console.time()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) +macro_rules! console_time { + ($($input:tt)*) => { + $crate::__weblog_console_time! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_time_end as __weblog_console_time_end; + +#[macro_export] +/// Call the browser's `console.time_end()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/time_end) +macro_rules! console_time_end { + ($($input:tt)*) => { + $crate::__weblog_console_time_end! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_time_log as __weblog_console_time_log; + +#[macro_export] +/// Call the browser's `console.time_log()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/time_log) +macro_rules! console_time_log { + ($($input:tt)*) => { + $crate::__weblog_console_time_log! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_time_stamp as __weblog_console_time_stamp; + +#[macro_export] +/// Call the browser's `console.time_stamp()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/time_stamp) +macro_rules! console_time_stamp { + ($($input:tt)*) => { + $crate::__weblog_console_time_stamp! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_trace as __weblog_console_trace; + +#[macro_export] +/// Call the browser's `console.trace()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/trace) +macro_rules! console_trace { + ($($input:tt)*) => { + $crate::__weblog_console_trace! { + #![crate = $crate] + $($input)* + } + }; +} + +#[doc(hidden)] +pub use ::weblog_proc_macro::console_warn as __weblog_console_warn; + +#[macro_export] +/// Call the browser's `console.warn()` function. +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/warn) +macro_rules! console_warn { + ($($input:tt)*) => { + $crate::__weblog_console_warn! { + #![crate = $crate] + $($input)* + } + }; +} diff --git a/src/lib.rs b/src/lib.rs index 922cf68..bd53771 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,16 +75,11 @@ //! weblog = { version = "0.3", default-features = false, features = ["stdweb"] } //! ``` //! + mod console; #[cfg(feature = "web_sys")] -pub use weblog_proc_macro::*; - -#[cfg(feature = "web_sys")] -pub use ::web_sys; - -#[cfg(feature = "web_sys")] -pub use ::wasm_bindgen; +pub use self::console::web_sys::*; #[cfg(feature = "std_web")] pub use self::console::std_web::*; diff --git a/weblog-proc-macro/src/weblog_impl.rs b/weblog-proc-macro/src/weblog_impl.rs index b983458..814f5bf 100644 --- a/weblog-proc-macro/src/weblog_impl.rs +++ b/weblog-proc-macro/src/weblog_impl.rs @@ -4,7 +4,7 @@ use quote::quote; use std::cmp; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::{parse_macro_input, Expr, Result, Token}; +use syn::{bracketed, parse_macro_input, Expr, Result, Token}; // quote_console_func builds and quotes a call to the browser's console API, based on the // provided console function and argument types and the input token stream provider by @@ -24,10 +24,10 @@ pub fn quote_console_func( mut arg_modes: impl Iterator, input: TokenStream, ) -> TokenStream { - let InArgs(in_args) = parse_macro_input!(input as InArgs); + let InArgs(crate_name, in_args) = parse_macro_input!(input as InArgs); let mut out_args = in_args .iter() - .map(|arg| quote_arg(arg, arg_modes.next().unwrap_or_default())); + .map(|arg| quote_arg(&crate_name, arg, arg_modes.next().unwrap_or_default())); let num_provided = out_args.len(); let num_fixed = cmp::min(num_provided, params_fixed); @@ -60,16 +60,18 @@ pub fn quote_console_func( } } - (quote! { ::weblog::web_sys::console::#ident(#args) }).into() + (quote! { #crate_name::web_sys::console::#ident(#args) }).into() } -fn quote_arg(arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { +fn quote_arg(crate_name: &TokenStream2, arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { match arg_mode { ArgMode::PassThrough => { quote! { #arg } } ArgMode::IntoJsValue => { - quote! { &::std::convert::Into::<::weblog::wasm_bindgen::JsValue>::into(#arg) } + quote! { + &::std::convert::Into::<#crate_name::wasm_bindgen::JsValue>::into(#arg) + } } } } @@ -114,11 +116,21 @@ impl Default for ArgMode { } // InArgs is a struct required during parsing of input arguments. -struct InArgs(Punctuated); +struct InArgs(TokenStream2, Punctuated); impl Parse for InArgs { fn parse(input: ParseStream) -> Result { - let in_args: Punctuated = Punctuated::parse_terminated(&input)?; - Ok(Self(in_args)) + input.parse::()?; + input.parse::()?; + + let bracketed; + bracketed!(bracketed in input); + bracketed.parse::()?; + bracketed.parse::()?; + + let crate_name = bracketed.parse()?; + let args: Punctuated = Punctuated::parse_terminated(&input)?; + + Ok(Self(crate_name, args)) } } -- 2.40.1 From d4d441d4e30652e222dacb65bbb4b3327b4e79e2 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 13 Nov 2020 07:34:47 +0100 Subject: [PATCH 3/7] remove a magic number --- weblog-proc-macro/src/weblog_impl.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/weblog-proc-macro/src/weblog_impl.rs b/weblog-proc-macro/src/weblog_impl.rs index 814f5bf..572469d 100644 --- a/weblog-proc-macro/src/weblog_impl.rs +++ b/weblog-proc-macro/src/weblog_impl.rs @@ -6,6 +6,10 @@ use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::{bracketed, parse_macro_input, Expr, Result, Token}; +// The maximum number of variadic arguments accepted by web-sys function +// groups such as `console::log_n`: +const MAX_VARIADIC_ARGS: usize = 7; + // quote_console_func builds and quotes a call to the browser's console API, based on the // provided console function and argument types and the input token stream provider by // the macro caller. @@ -34,7 +38,7 @@ pub fn quote_console_func( let num_variadic = cmp::max(num_provided - num_fixed, 0); let ident = { - let func_name = if is_variadic && num_variadic <= 7 { + let func_name = if is_variadic && num_variadic <= MAX_VARIADIC_ARGS { format!("{}_{}", name, num_variadic) } else { name @@ -47,7 +51,7 @@ pub fn quote_console_func( for _ in 0..num_fixed { args.push(out_args.next().unwrap()); } - if num_variadic > 7 { + if num_variadic > MAX_VARIADIC_ARGS { let variadic_args = out_args.collect::>(); let ary = quote! { &::std::iter::FromIterator::from_iter(::std::iter::IntoIterator::into_iter(::std::vec![#variadic_args])) -- 2.40.1 From 95d1266752b5c0a719893935d89c599b93972f00 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 13 Nov 2020 10:19:53 +0100 Subject: [PATCH 4/7] Fix calling macros with zero arguments --- weblog-proc-macro/src/lib.rs | 163 +++++++++++++++++---------- weblog-proc-macro/src/weblog_impl.rs | 23 ++-- 2 files changed, 116 insertions(+), 70 deletions(-) diff --git a/weblog-proc-macro/src/lib.rs b/weblog-proc-macro/src/lib.rs index 227a8d7..c701011 100644 --- a/weblog-proc-macro/src/lib.rs +++ b/weblog-proc-macro/src/lib.rs @@ -1,34 +1,46 @@ +//! weblog-proc-macro contains procedural macro definitions for the +//! [`weblog`](https://crates.io/crates/weblog) crate. See its documentation for more information. extern crate proc_macro2; mod weblog_impl; use proc_macro::TokenStream; use std::iter; -use weblog_impl::{quote_console_func, ArgMode, ConsoleFunc}; +use syn::parse_macro_input; +use weblog_impl::{quote_console_func, ArgMode, ConsoleFunc, InputTokens}; -/// Call the browser's `console.assert()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/assert) +#[doc(hidden)] #[proc_macro] pub fn console_assert(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("assert_with_condition_and_data", 1), iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::IntoJsValue)), - input, + crate_name, + args, ) } -/// Call the browser's `console.clear()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/clear) +#[doc(hidden)] #[proc_macro] pub fn console_clear(input: TokenStream) -> TokenStream { - quote_console_func(ConsoleFunc::fixed("clear", 0), iter::empty(), input) + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + + quote_console_func( + ConsoleFunc::fixed("clear", 0), + iter::empty(), + crate_name, + args, + ) } -/// Call the browser's `console.count()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/count) +#[doc(hidden)] #[proc_macro] pub fn console_count(input: TokenStream) -> TokenStream { - let name = if input.is_empty() { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + + let name = if args.is_empty() { "count" } else { "count_with_label" @@ -37,15 +49,17 @@ pub fn console_count(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::fixed(name, 1), iter::once(ArgMode::PassThrough), - input, + crate_name, + args, ) } -/// Call the browser's `console.countReset()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/countReset) +#[doc(hidden)] #[proc_macro] pub fn console_count_reset(input: TokenStream) -> TokenStream { - let name = if input.is_empty() { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + + let name = if args.is_empty() { "count_reset" } else { "count_reset_with_label" @@ -54,103 +68,121 @@ pub fn console_count_reset(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::fixed(name, 1), iter::once(ArgMode::PassThrough), - input, + crate_name, + args, ) } -/// Call the browser's `console.debug()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/debug) +#[doc(hidden)] #[proc_macro] pub fn console_debug(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("debug", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.dir()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/dir) +#[doc(hidden)] #[proc_macro] pub fn console_dir(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("dir", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.dirxml()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/dirxml) +#[doc(hidden)] #[proc_macro] pub fn console_dirxml(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("dirxml", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.error()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/error) +#[doc(hidden)] #[proc_macro] pub fn console_error(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("error", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.exception()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/exception) +#[doc(hidden)] #[proc_macro] pub fn console_exception(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("exception", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.info()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/info) +#[doc(hidden)] #[proc_macro] pub fn console_info(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("info", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.log()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) +#[doc(hidden)] #[proc_macro] pub fn console_log(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("log", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.table()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/table) +#[doc(hidden)] #[proc_macro] pub fn console_table(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("table", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.time()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) +#[doc(hidden)] #[proc_macro] pub fn console_time(input: TokenStream) -> TokenStream { - let name = if input.is_empty() { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + + let name = if args.is_empty() { "time" } else { "time_with_label" @@ -159,15 +191,17 @@ pub fn console_time(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::fixed(name, 1), iter::once(ArgMode::PassThrough), - input, + crate_name, + args, ) } -/// Call the browser's `console.timeEnd()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd) +#[doc(hidden)] #[proc_macro] pub fn console_time_end(input: TokenStream) -> TokenStream { - let name = if input.is_empty() { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + + let name = if args.is_empty() { "time_end" } else { "time_end_with_label" @@ -176,26 +210,30 @@ pub fn console_time_end(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::fixed(name, 1), iter::once(ArgMode::PassThrough), - input, + crate_name, + args, ) } -/// Call the browser's `console.timeLog()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeLog) +#[doc(hidden)] #[proc_macro] pub fn console_time_log(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("time_log_with_label_and_data", 1), iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::IntoJsValue)), - input, + crate_name, + args, ) } -/// Call the browser's `console.timeStamp()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeStamp) +#[doc(hidden)] #[proc_macro] pub fn console_time_stamp(input: TokenStream) -> TokenStream { - let name = if input.is_empty() { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + + let name = if args.is_empty() { "time_stamp" } else { "time_stamp_with_data" @@ -204,28 +242,33 @@ pub fn console_time_stamp(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::fixed(name, 1), iter::once(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.trace()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/trace) +#[doc(hidden)] #[proc_macro] pub fn console_trace(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("trace", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } -/// Call the browser's `console.warn()` function. -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/warn) +#[doc(hidden)] #[proc_macro] pub fn console_warn(input: TokenStream) -> TokenStream { + let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens); + quote_console_func( ConsoleFunc::variadic("warn", 0), iter::repeat(ArgMode::IntoJsValue), - input, + crate_name, + args, ) } diff --git a/weblog-proc-macro/src/weblog_impl.rs b/weblog-proc-macro/src/weblog_impl.rs index 572469d..cbec3a3 100644 --- a/weblog-proc-macro/src/weblog_impl.rs +++ b/weblog-proc-macro/src/weblog_impl.rs @@ -4,7 +4,7 @@ use quote::quote; use std::cmp; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::{bracketed, parse_macro_input, Expr, Result, Token}; +use syn::{bracketed, Expr, Result, Token}; // The maximum number of variadic arguments accepted by web-sys function // groups such as `console::log_n`: @@ -18,7 +18,8 @@ const MAX_VARIADIC_ARGS: usize = 7; // // * `func`: an object containing the target function name and parameter configuration. // * `arg_modes`: an Iterator that will be called once per argument parsed from the input tokens. -// * `input`: the raw input tokens, as provided by the macro caller. +// * `crate_name`: an identifier that resolves to the name of the calling crate. +// * `in_args`: zero or more punctuated expressions passed as arguments by the caller. pub fn quote_console_func( ConsoleFunc { name, @@ -26,9 +27,9 @@ pub fn quote_console_func( is_variadic, }: ConsoleFunc, mut arg_modes: impl Iterator, - input: TokenStream, + crate_name: Ident, + in_args: Punctuated, ) -> TokenStream { - let InArgs(crate_name, in_args) = parse_macro_input!(input as InArgs); let mut out_args = in_args .iter() .map(|arg| quote_arg(&crate_name, arg, arg_modes.next().unwrap_or_default())); @@ -67,7 +68,7 @@ pub fn quote_console_func( (quote! { #crate_name::web_sys::console::#ident(#args) }).into() } -fn quote_arg(crate_name: &TokenStream2, arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { +fn quote_arg(crate_name: &Ident, arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { match arg_mode { ArgMode::PassThrough => { quote! { #arg } @@ -119,10 +120,12 @@ impl Default for ArgMode { } } -// InArgs is a struct required during parsing of input arguments. -struct InArgs(TokenStream2, Punctuated); +pub struct InputTokens { + pub crate_name: Ident, + pub args: Punctuated, +} -impl Parse for InArgs { +impl Parse for InputTokens { fn parse(input: ParseStream) -> Result { input.parse::()?; input.parse::()?; @@ -132,9 +135,9 @@ impl Parse for InArgs { bracketed.parse::()?; bracketed.parse::()?; - let crate_name = bracketed.parse()?; + let crate_name = bracketed.parse::()?; let args: Punctuated = Punctuated::parse_terminated(&input)?; - Ok(Self(crate_name, args)) + Ok(Self { crate_name, args }) } } -- 2.40.1 From 945e7ed8e6269c53f7d94989bfbe783623bdc225 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 13 Nov 2020 10:20:15 +0100 Subject: [PATCH 5/7] Bump version to 0.4.0-beta.1 --- Cargo.toml | 8 ++++---- examples/web-sys/Cargo.toml | 5 +++-- weblog-proc-macro/Cargo.toml | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1979c42..07169ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "weblog" -version = "0.3.0" -authors = ["Rob Watson "] +version = "0.4.0-beta.1" +authors = ["Rob Watson"] edition = "2018" description = "weblog is a crate that defines a set of macros for calling `console.log()` and other members of the browser's console API when targeting Wasm." -repository = "https://github.com/rfwatson/weblog" +repository = "https://git.netflux.io/rob/weblog" keywords = ["wasm", "webassembly", "console", "log", "logging"] license = "MIT OR Apache-2.0" @@ -20,6 +20,6 @@ std_web = ["stdweb"] [dependencies] stdweb = { version = ">= 0.4", optional = true } -weblog-proc-macro = { path = "./weblog-proc-macro", version = "0.3.0", optional = true } +weblog-proc-macro = { path = "./weblog-proc-macro", version = "0.4.0-beta.1", optional = true } web-sys = { version = "0.3", features = ["console"], optional = true } wasm-bindgen = { version = "0.2", optional = true } diff --git a/examples/web-sys/Cargo.toml b/examples/web-sys/Cargo.toml index 226ae6c..a265d67 100644 --- a/examples/web-sys/Cargo.toml +++ b/examples/web-sys/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "weblog-example-web-sys" -version = "0.3.0" -authors = ["Rob Watson "] +version = "0.4.0-beta.1" +repository = "https://git.netflux.io/rob/weblog" +authors = ["Rob Watson"] edition = "2018" [lib] diff --git a/weblog-proc-macro/Cargo.toml b/weblog-proc-macro/Cargo.toml index 7b5a543..518bb88 100644 --- a/weblog-proc-macro/Cargo.toml +++ b/weblog-proc-macro/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "weblog-proc-macro" -version = "0.3.0" -authors = ["Rob Watson "] -repository = "https://github.com/rfwatson/weblog" +version = "0.4.0-beta.1" +authors = ["Rob Watson"] +repository = "https://git.netflux.io/rob/weblog" description = "weblog is a crate that defines a set of macros for calling `console.log()` and other members of the browser's console API when targeting Wasm." edition = "2018" keywords = ["wasm", "webassembly", "console", "log", "logging"] -- 2.40.1 From eea445afc28a122812ca289779c86ebb21d1388b Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 13 Nov 2020 10:20:30 +0100 Subject: [PATCH 6/7] Remove dependency from example --- examples/web-sys/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/web-sys/Cargo.toml b/examples/web-sys/Cargo.toml index a265d67..3b8439c 100644 --- a/examples/web-sys/Cargo.toml +++ b/examples/web-sys/Cargo.toml @@ -10,4 +10,3 @@ crate-type = ["cdylib"] [dependencies] weblog = { path = "../.." } -wasm-bindgen = "0.2" -- 2.40.1 From d7d7737c42bfdebe408b3431a0cb8b7700e9adce Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Thu, 26 Nov 2020 09:20:49 +0100 Subject: [PATCH 7/7] WIP: traits --- examples/web-sys/src/lib.rs | 12 +++-- src/console/web_sys.rs | 67 ++++++++++++++++++++++++++++ src/lib.rs | 8 ++-- weblog-proc-macro/src/lib.rs | 26 +++++------ weblog-proc-macro/src/weblog_impl.rs | 11 ++--- 5 files changed, 100 insertions(+), 24 deletions(-) diff --git a/examples/web-sys/src/lib.rs b/examples/web-sys/src/lib.rs index 06dfc76..68e9323 100644 --- a/examples/web-sys/src/lib.rs +++ b/examples/web-sys/src/lib.rs @@ -10,14 +10,20 @@ pub fn main() { console_log!("Hello", "there", "world"); // Various types. + console_log!("f32", 1f32, "f64", 2f64); + console_log!("u8", 3u8, "i8", 4i8); + console_log!("u16", 5u16, "i16", 6i16); + console_log!("u32", 7u32, "i32", 8i8); + //console_log!("u64", 9u64, "i64", 10i64); + console_log!("usize", 11usize, "isize", 12isize); + + // More types. console_log!( - 1.0, - 2f64, true, "&str", String::from("owned string"), Some("an option"), - None as Option, + (None as Option), ); // Various levels. diff --git a/src/console/web_sys.rs b/src/console/web_sys.rs index 0ce70db..2108093 100644 --- a/src/console/web_sys.rs +++ b/src/console/web_sys.rs @@ -3,6 +3,73 @@ pub use ::wasm_bindgen; #[doc(hidden)] pub use ::web_sys; +use ::wasm_bindgen::JsValue; + +pub struct ConsoleArg(JsValue); + +impl ConsoleArg { + pub fn into_inner(self) -> JsValue { + self.0 + } +} + +use crate::ToConsole; + +macro_rules! into_jsvalue { + ($($n:ident),*) => ($( + impl ToConsole for $n { + #[inline] + fn to_console(self) -> ConsoleArg { + ConsoleArg(self.into()) + } + } + )*) +} + +// TODO u64 and f64? +// TODO: impl<'a, T> From<&'a T> for JsValue where T: JsCast ? + +into_jsvalue!(bool, f32, f64, i8, i16, i32, u8, u16, u32, String); + +impl<'a> ToConsole for &'a String { + fn to_console(self) -> ConsoleArg { + ConsoleArg(self.into()) + } +} + +impl<'a> ToConsole for &'a str { + fn to_console(self) -> ConsoleArg { + ConsoleArg(self.into()) + } +} + +impl ToConsole for Option +where + T: ToConsole, +{ + fn to_console(self) -> ConsoleArg { + match self { + Some(s) => s.to_console(), + None => ConsoleArg(JsValue::undefined()), + } + } +} + +impl ToConsole for usize { + fn to_console(self) -> ConsoleArg { + // usize/isize is always 32-bit in Wasm. + // But perhaps this should have a guard of some kind. + ConsoleArg((self as u32).into()) + } +} + +impl ToConsole for isize { + fn to_console(self) -> ConsoleArg { + // As above. + ConsoleArg((self as i32).into()) + } +} + // It is necessary to expose these wrapper macros in order to pass the $crate // meta-variable to the proc macro. This in turn allows the macros to be // re-exported from third-party crates without breakage. diff --git a/src/lib.rs b/src/lib.rs index bd53771..41b0b90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,12 +74,14 @@ //! ```toml //! weblog = { version = "0.3", default-features = false, features = ["stdweb"] } //! ``` -//! mod console; -#[cfg(feature = "web_sys")] -pub use self::console::web_sys::*; +pub trait ToConsole { + fn to_console(self) -> ConsoleArg; +} #[cfg(feature = "std_web")] pub use self::console::std_web::*; +#[cfg(feature = "web_sys")] +pub use self::console::web_sys::*; diff --git a/weblog-proc-macro/src/lib.rs b/weblog-proc-macro/src/lib.rs index c701011..60f72e7 100644 --- a/weblog-proc-macro/src/lib.rs +++ b/weblog-proc-macro/src/lib.rs @@ -16,7 +16,7 @@ pub fn console_assert(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("assert_with_condition_and_data", 1), - iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::IntoJsValue)), + iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::Console)), crate_name, args, ) @@ -80,7 +80,7 @@ pub fn console_debug(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("debug", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -93,7 +93,7 @@ pub fn console_dir(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("dir", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -106,7 +106,7 @@ pub fn console_dirxml(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("dirxml", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -119,7 +119,7 @@ pub fn console_error(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("error", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -132,7 +132,7 @@ pub fn console_exception(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("exception", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -145,7 +145,7 @@ pub fn console_info(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("info", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -158,7 +158,7 @@ pub fn console_log(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("log", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -171,7 +171,7 @@ pub fn console_table(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("table", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -222,7 +222,7 @@ pub fn console_time_log(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("time_log_with_label_and_data", 1), - iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::IntoJsValue)), + iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::Console)), crate_name, args, ) @@ -241,7 +241,7 @@ pub fn console_time_stamp(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::fixed(name, 1), - iter::once(ArgMode::IntoJsValue), + iter::once(ArgMode::Console), crate_name, args, ) @@ -254,7 +254,7 @@ pub fn console_trace(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("trace", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) @@ -267,7 +267,7 @@ pub fn console_warn(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::variadic("warn", 0), - iter::repeat(ArgMode::IntoJsValue), + iter::repeat(ArgMode::Console), crate_name, args, ) diff --git a/weblog-proc-macro/src/weblog_impl.rs b/weblog-proc-macro/src/weblog_impl.rs index cbec3a3..8388e64 100644 --- a/weblog-proc-macro/src/weblog_impl.rs +++ b/weblog-proc-macro/src/weblog_impl.rs @@ -32,7 +32,7 @@ pub fn quote_console_func( ) -> TokenStream { let mut out_args = in_args .iter() - .map(|arg| quote_arg(&crate_name, arg, arg_modes.next().unwrap_or_default())); + .map(|arg| quote_arg(arg, arg_modes.next().unwrap_or_default())); let num_provided = out_args.len(); let num_fixed = cmp::min(num_provided, params_fixed); @@ -68,14 +68,15 @@ pub fn quote_console_func( (quote! { #crate_name::web_sys::console::#ident(#args) }).into() } -fn quote_arg(crate_name: &Ident, arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { +#[inline] +fn quote_arg(arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { match arg_mode { ArgMode::PassThrough => { quote! { #arg } } - ArgMode::IntoJsValue => { + ArgMode::Console => { quote! { - &::std::convert::Into::<#crate_name::wasm_bindgen::JsValue>::into(#arg) + &#arg.to_console().into_inner() } } } @@ -111,7 +112,7 @@ impl ConsoleFunc { #[derive(Debug, Copy, Clone)] pub enum ArgMode { PassThrough, - IntoJsValue, + Console, } impl Default for ArgMode { -- 2.40.1