Compare commits

...

8 Commits

Author SHA1 Message Date
Rob Watson d7d7737c42 WIP: traits 2020-11-28 07:29:23 +01:00
Rob Watson eea445afc2 Remove dependency from example 2020-11-13 10:20:30 +01:00
Rob Watson 945e7ed8e6 Bump version to 0.4.0-beta.1 2020-11-13 10:20:15 +01:00
Rob Watson 95d1266752 Fix calling macros with zero arguments 2020-11-13 10:19:53 +01:00
Rob Watson d4d441d4e3 remove a magic number 2020-11-13 07:34:47 +01:00
Rob Watson 160130a29e WIP: support re-exporting macros 2020-11-12 17:48:09 +01:00
Rob Watson 348b03d4ea Re-export wasm-bindgen 2020-11-12 10:00:28 +01:00
Rob Watson dcd02f5702 Add badges 2020-10-19 17:59:26 +02:00
10 changed files with 536 additions and 107 deletions

View File

@ -1,10 +1,10 @@
[package] [package]
name = "weblog" name = "weblog"
version = "0.3.0" version = "0.4.0-beta.1"
authors = ["Rob Watson <rfwatson@users.noreply.github.com>"] authors = ["Rob Watson"]
edition = "2018" 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." 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"] keywords = ["wasm", "webassembly", "console", "log", "logging"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -20,6 +20,6 @@ std_web = ["stdweb"]
[dependencies] [dependencies]
stdweb = { version = ">= 0.4", optional = true } 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 } web-sys = { version = "0.3", features = ["console"], optional = true }
wasm-bindgen = { version = "0.2", optional = true } wasm-bindgen = { version = "0.2", optional = true }

View File

@ -1,5 +1,16 @@
# Weblog # Weblog
<!-- Version -->
<a href="https://crates.io/crates/weblog">
<img src="https://img.shields.io/crates/v/weblog.svg?style=flat-square"
alt="Crates.io version" />
</a>
<!-- Docs -->
<a href="https://docs.rs/weblog">
<img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
alt="docs.rs docs" />
</a>
weblog is a crate that defines a set of macros for calling `console.log()`, `console.error()` and other members of the browser's console API when targeting Wasm. weblog is a crate that defines a set of macros for calling `console.log()`, `console.error()` and other members of the browser's console API when targeting Wasm.
## Features ## Features

View File

@ -1,7 +1,8 @@
[package] [package]
name = "weblog-example-web-sys" name = "weblog-example-web-sys"
version = "0.3.0" version = "0.4.0-beta.1"
authors = ["Rob Watson <rfwatson@users.noreply.github.com>"] repository = "https://git.netflux.io/rob/weblog"
authors = ["Rob Watson"]
edition = "2018" edition = "2018"
[lib] [lib]
@ -9,4 +10,3 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
weblog = { path = "../.." } weblog = { path = "../.." }
wasm-bindgen = "0.2"

View File

@ -10,14 +10,20 @@ pub fn main() {
console_log!("Hello", "there", "world"); console_log!("Hello", "there", "world");
// Various types. // 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!( console_log!(
1.0,
2f64,
true, true,
"&str", "&str",
String::from("owned string"), String::from("owned string"),
Some("an option"), Some("an option"),
None as Option<i32>, (None as Option<i32>),
); );
// Various levels. // Various levels.

View File

@ -1,2 +1,4 @@
#[cfg(feature = "std_web")] #[cfg(feature = "std_web")]
pub mod std_web; pub mod std_web;
#[cfg(feature = "web_sys")]
pub mod web_sys;

347
src/console/web_sys.rs Normal file
View File

@ -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<T> ToConsole for Option<T>
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)*
}
};
}

View File

@ -74,14 +74,14 @@
//! ```toml //! ```toml
//! weblog = { version = "0.3", default-features = false, features = ["stdweb"] } //! weblog = { version = "0.3", default-features = false, features = ["stdweb"] }
//! ``` //! ```
//!
mod console; mod console;
#[cfg(feature = "web_sys")] pub trait ToConsole {
pub use weblog_proc_macro::*; fn to_console(self) -> ConsoleArg;
}
#[cfg(feature = "web_sys")]
pub use ::web_sys;
#[cfg(feature = "std_web")] #[cfg(feature = "std_web")]
pub use self::console::std_web::*; pub use self::console::std_web::*;
#[cfg(feature = "web_sys")]
pub use self::console::web_sys::*;

View File

@ -1,8 +1,8 @@
[package] [package]
name = "weblog-proc-macro" name = "weblog-proc-macro"
version = "0.3.0" version = "0.4.0-beta.1"
authors = ["Rob Watson <rfwatson@users.noreply.github.com>"] authors = ["Rob Watson"]
repository = "https://github.com/rfwatson/weblog" 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." 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" edition = "2018"
keywords = ["wasm", "webassembly", "console", "log", "logging"] keywords = ["wasm", "webassembly", "console", "log", "logging"]

View File

@ -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; extern crate proc_macro2;
mod weblog_impl; mod weblog_impl;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use std::iter; 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. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/assert)
#[proc_macro] #[proc_macro]
pub fn console_assert(input: TokenStream) -> TokenStream { pub fn console_assert(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("assert_with_condition_and_data", 1), 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)),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.clear()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/clear)
#[proc_macro] #[proc_macro]
pub fn console_clear(input: TokenStream) -> TokenStream { 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. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/count)
#[proc_macro] #[proc_macro]
pub fn console_count(input: TokenStream) -> TokenStream { 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" "count"
} else { } else {
"count_with_label" "count_with_label"
@ -37,15 +49,17 @@ pub fn console_count(input: TokenStream) -> TokenStream {
quote_console_func( quote_console_func(
ConsoleFunc::fixed(name, 1), ConsoleFunc::fixed(name, 1),
iter::once(ArgMode::PassThrough), iter::once(ArgMode::PassThrough),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.countReset()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/countReset)
#[proc_macro] #[proc_macro]
pub fn console_count_reset(input: TokenStream) -> TokenStream { 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" "count_reset"
} else { } else {
"count_reset_with_label" "count_reset_with_label"
@ -54,103 +68,121 @@ pub fn console_count_reset(input: TokenStream) -> TokenStream {
quote_console_func( quote_console_func(
ConsoleFunc::fixed(name, 1), ConsoleFunc::fixed(name, 1),
iter::once(ArgMode::PassThrough), iter::once(ArgMode::PassThrough),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.debug()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/debug)
#[proc_macro] #[proc_macro]
pub fn console_debug(input: TokenStream) -> TokenStream { pub fn console_debug(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("debug", 0), ConsoleFunc::variadic("debug", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.dir()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/dir)
#[proc_macro] #[proc_macro]
pub fn console_dir(input: TokenStream) -> TokenStream { pub fn console_dir(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("dir", 0), ConsoleFunc::variadic("dir", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.dirxml()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/dirxml)
#[proc_macro] #[proc_macro]
pub fn console_dirxml(input: TokenStream) -> TokenStream { pub fn console_dirxml(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("dirxml", 0), ConsoleFunc::variadic("dirxml", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.error()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/error)
#[proc_macro] #[proc_macro]
pub fn console_error(input: TokenStream) -> TokenStream { pub fn console_error(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("error", 0), ConsoleFunc::variadic("error", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.exception()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/exception)
#[proc_macro] #[proc_macro]
pub fn console_exception(input: TokenStream) -> TokenStream { pub fn console_exception(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("exception", 0), ConsoleFunc::variadic("exception", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.info()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/info)
#[proc_macro] #[proc_macro]
pub fn console_info(input: TokenStream) -> TokenStream { pub fn console_info(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("info", 0), ConsoleFunc::variadic("info", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.log()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/log)
#[proc_macro] #[proc_macro]
pub fn console_log(input: TokenStream) -> TokenStream { pub fn console_log(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("log", 0), ConsoleFunc::variadic("log", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.table()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/table)
#[proc_macro] #[proc_macro]
pub fn console_table(input: TokenStream) -> TokenStream { pub fn console_table(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("table", 0), ConsoleFunc::variadic("table", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.time()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/time)
#[proc_macro] #[proc_macro]
pub fn console_time(input: TokenStream) -> TokenStream { 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" "time"
} else { } else {
"time_with_label" "time_with_label"
@ -159,15 +191,17 @@ pub fn console_time(input: TokenStream) -> TokenStream {
quote_console_func( quote_console_func(
ConsoleFunc::fixed(name, 1), ConsoleFunc::fixed(name, 1),
iter::once(ArgMode::PassThrough), iter::once(ArgMode::PassThrough),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.timeEnd()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd)
#[proc_macro] #[proc_macro]
pub fn console_time_end(input: TokenStream) -> TokenStream { 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" "time_end"
} else { } else {
"time_end_with_label" "time_end_with_label"
@ -176,26 +210,30 @@ pub fn console_time_end(input: TokenStream) -> TokenStream {
quote_console_func( quote_console_func(
ConsoleFunc::fixed(name, 1), ConsoleFunc::fixed(name, 1),
iter::once(ArgMode::PassThrough), iter::once(ArgMode::PassThrough),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.timeLog()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeLog)
#[proc_macro] #[proc_macro]
pub fn console_time_log(input: TokenStream) -> TokenStream { pub fn console_time_log(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("time_log_with_label_and_data", 1), 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)),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.timeStamp()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeStamp)
#[proc_macro] #[proc_macro]
pub fn console_time_stamp(input: TokenStream) -> TokenStream { 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" "time_stamp"
} else { } else {
"time_stamp_with_data" "time_stamp_with_data"
@ -203,29 +241,34 @@ pub fn console_time_stamp(input: TokenStream) -> TokenStream {
quote_console_func( quote_console_func(
ConsoleFunc::fixed(name, 1), ConsoleFunc::fixed(name, 1),
iter::once(ArgMode::IntoJsValue), iter::once(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.trace()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/trace)
#[proc_macro] #[proc_macro]
pub fn console_trace(input: TokenStream) -> TokenStream { pub fn console_trace(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("trace", 0), ConsoleFunc::variadic("trace", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }
/// Call the browser's `console.warn()` function. #[doc(hidden)]
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console/warn)
#[proc_macro] #[proc_macro]
pub fn console_warn(input: TokenStream) -> TokenStream { pub fn console_warn(input: TokenStream) -> TokenStream {
let InputTokens { crate_name, args } = parse_macro_input!(input as InputTokens);
quote_console_func( quote_console_func(
ConsoleFunc::variadic("warn", 0), ConsoleFunc::variadic("warn", 0),
iter::repeat(ArgMode::IntoJsValue), iter::repeat(ArgMode::Console),
input, crate_name,
args,
) )
} }

View File

@ -4,7 +4,11 @@ use quote::quote;
use std::cmp; use std::cmp;
use syn::parse::{Parse, ParseStream}; use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated; 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 // 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 // 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. // * `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. // * `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( pub fn quote_console_func(
ConsoleFunc { ConsoleFunc {
name, name,
@ -22,9 +27,9 @@ pub fn quote_console_func(
is_variadic, is_variadic,
}: ConsoleFunc, }: ConsoleFunc,
mut arg_modes: impl Iterator<Item = ArgMode>, mut arg_modes: impl Iterator<Item = ArgMode>,
input: TokenStream, crate_name: Ident,
in_args: Punctuated<Expr, Token![,]>,
) -> TokenStream { ) -> TokenStream {
let InArgs(in_args) = parse_macro_input!(input as InArgs);
let mut out_args = in_args let mut out_args = in_args
.iter() .iter()
.map(|arg| quote_arg(arg, arg_modes.next().unwrap_or_default())); .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 num_variadic = cmp::max(num_provided - num_fixed, 0);
let ident = { 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) format!("{}_{}", name, num_variadic)
} else { } else {
name name
@ -47,7 +52,7 @@ pub fn quote_console_func(
for _ in 0..num_fixed { for _ in 0..num_fixed {
args.push(out_args.next().unwrap()); args.push(out_args.next().unwrap());
} }
if num_variadic > 7 { if num_variadic > MAX_VARIADIC_ARGS {
let variadic_args = out_args.collect::<Punctuated<_, Token![,]>>(); let variadic_args = out_args.collect::<Punctuated<_, Token![,]>>();
let ary = quote! { let ary = quote! {
&::std::iter::FromIterator::from_iter(::std::iter::IntoIterator::into_iter(::std::vec![#variadic_args])) &::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 { fn quote_arg(arg: &Expr, arg_mode: ArgMode) -> TokenStream2 {
match arg_mode { match arg_mode {
ArgMode::PassThrough => { ArgMode::PassThrough => {
quote! { #arg } quote! { #arg }
} }
ArgMode::IntoJsValue => { ArgMode::Console => {
quote! { &::std::convert::Into::<::wasm_bindgen::JsValue>::into(#arg) } quote! {
&#arg.to_console().into_inner()
}
} }
} }
} }
@ -104,7 +112,7 @@ impl ConsoleFunc {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ArgMode { pub enum ArgMode {
PassThrough, PassThrough,
IntoJsValue, Console,
} }
impl Default for ArgMode { impl Default for ArgMode {
@ -113,12 +121,24 @@ impl Default for ArgMode {
} }
} }
// InArgs is a struct required during parsing of input arguments. pub struct InputTokens {
struct InArgs(Punctuated<Expr, Token![,]>); pub crate_name: Ident,
pub args: Punctuated<Expr, Token![,]>,
}
impl Parse for InArgs { impl Parse for InputTokens {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let in_args: Punctuated<Expr, Token![,]> = Punctuated::parse_terminated(&input)?; input.parse::<Token![#]>()?;
Ok(Self(in_args)) input.parse::<Token![!]>()?;
let bracketed;
bracketed!(bracketed in input);
bracketed.parse::<Token![crate]>()?;
bracketed.parse::<Token![ = ]>()?;
let crate_name = bracketed.parse::<Ident>()?;
let args: Punctuated<Expr, Token![,]> = Punctuated::parse_terminated(&input)?;
Ok(Self { crate_name, args })
} }
} }