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..3b8439c 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] @@ -9,4 +10,3 @@ crate-type = ["cdylib"] [dependencies] weblog = { path = "../.." } -wasm-bindgen = "0.2" 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/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..2108093 --- /dev/null +++ b/src/console/web_sys.rs @@ -0,0 +1,347 @@ +#[doc(hidden)] +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. +// +// 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 1644e59..41b0b90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,14 +74,14 @@ //! ```toml //! 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; +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/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"] diff --git a/weblog-proc-macro/src/lib.rs b/weblog-proc-macro/src/lib.rs index 227a8d7..60f72e7 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, + iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::Console)), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + iter::once(ArgMode::PassThrough).chain(iter::repeat(ArgMode::Console)), + 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" @@ -203,29 +241,34 @@ pub fn console_time_stamp(input: TokenStream) -> TokenStream { quote_console_func( ConsoleFunc::fixed(name, 1), - iter::once(ArgMode::IntoJsValue), - input, + iter::once(ArgMode::Console), + 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, + iter::repeat(ArgMode::Console), + 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, + 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 1deb450..8388e64 100644 --- a/weblog-proc-macro/src/weblog_impl.rs +++ b/weblog-proc-macro/src/weblog_impl.rs @@ -4,7 +4,11 @@ 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, 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 @@ -14,7 +18,8 @@ use syn::{parse_macro_input, Expr, Result, Token}; // // * `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, @@ -22,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(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())); @@ -34,7 +39,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 +52,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])) @@ -60,16 +65,19 @@ pub fn quote_console_func( } } - (quote! { ::weblog::web_sys::console::#ident(#args) }).into() + (quote! { #crate_name::web_sys::console::#ident(#args) }).into() } +#[inline] fn quote_arg(arg: &Expr, arg_mode: ArgMode) -> TokenStream2 { match arg_mode { ArgMode::PassThrough => { quote! { #arg } } - ArgMode::IntoJsValue => { - quote! { &::std::convert::Into::<::wasm_bindgen::JsValue>::into(#arg) } + ArgMode::Console => { + quote! { + &#arg.to_console().into_inner() + } } } } @@ -104,7 +112,7 @@ impl ConsoleFunc { #[derive(Debug, Copy, Clone)] pub enum ArgMode { PassThrough, - IntoJsValue, + Console, } impl Default for ArgMode { @@ -113,12 +121,24 @@ impl Default for ArgMode { } } -// InArgs is a struct required during parsing of input arguments. -struct InArgs(Punctuated); +pub struct InputTokens { + pub crate_name: Ident, + pub args: Punctuated, +} -impl Parse for InArgs { +impl Parse for InputTokens { 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 }) } }