The custom-print
crate helps you to define print
, println
and dbg
macros
in wasm and customize them for other targets without any dependencies.
This crate helps you to define print
-like macros, dbg
and panic_hook
on wasm32-unknown-unknown
target without wasm-bindgen
dependency.
Also, it can be used on another targets to override default std write
-like macros,
add try_
macros variants, or to specify panic hook function.
It works on stable Rust,
supports no-alloc
and no-std
environments and has no dependencies.
In most cases it is suggested to use macros
[define_macros
], [define_macro
] or [define_init_panic_hook
].
These macros define macros or functions with the specified names that use
[FmtWriter
], [FmtTryWriter
], [ConcatWriter
], [ConcatTryWriter
],
[IoWriter
] or [IoTryWriter
] with the specified closure, unsafe function or extern function.
First, add the following to your Cargo.toml
:
toml
[dependencies]
custom-print = "1.0.0"
This crate depends on the standard library by default.
To use this crate in a #![no_std]
context but with heap-allocations enabled,
use default-features = false
in your Cargo.toml
as shown below:
toml
[dependencies.custom-print]
version = "1.0.0"
default-features = false
features = ["alloc"]
An example with an extern functions that takes a UTF-8 chars pointer and byte length
with no std
prelude:
```rust
extern crate std;
customprint::definemacros!({ print, println }, concat, extern "C" fn consolelog(: *const u8, : usize)); customprint::definemacros!({ eprint, eprintln, dbg }, concat, extern "C" fn consolewarn(: *const u8, _: usize)); customprint::defineinitpanichook!( concat, extern "C" fn consoleerror(_: *const u8, _: usize));
fn main() { initpanichook(); println!("println"); print!("print"); eprintln!("eprintln"); eprint!("eprint"); dbg!("dbg"); } ```
An example with a closure that takes an [str
] reference
in no_std
and no_alloc
context:
```rust
customprint::definemacros!({ print, println }, fmt, |_value: &str| { /* ... */ });
fn main() { println!("println"); print!("print"); } ```
An example with a function that takes a [c_char
] pointer and overriding
[std::print
] and [std::println
] functions:
```rust
fn write(value: *const std::os::raw::cchar) { /* ... */ }
customprint::definemacros!({ cprint, cprintln }, concat, crate::write); macrorules! print { ($($args:tt)*) => { cprint!($($args)*); } } macrorules! println { ($($args:tt)) => { cprintln!($($args)); } }
fn main() { println!("println"); print!("print"); } ```
Macro generation macros support specifying custom attributes, so you can re-export the generated macros and use them in other crates: ```rust // foo crate:
customprint::definemacros!( #[macroexport] { print, println, cprint, cprintln }, fmt, |value: &str| { /* ... */ } );
fn func() { cprintln!("cprintln"); cprint!("cprint"); }
// bar crate:
fn main() { foo::println!("println"); foo::print!("print"); } ```
You can find more usage examples in the tests and examples repository folders.
The example with [define_macros
] and [define_init_panic_hook
] with extern functions:
```rust
extern crate std;
customprint::definemacros!({ print, println, tryprintln }, concat, extern "C" fn consolelog(: *const u8, _: usize)); customprint::definemacros!({ eprint, eprintln, dbg }, concat, extern "C" fn consolewarn(: *const u8, _: usize)); customprint::defineinitpanichook!( concat, extern "C" fn consoleerror(_: *const u8, _: usize));
fn main() {
initpanichook();
println!("Greetings from println");
let _ = tryprintln!("Greetings from tryprintln");
eprintln!("Greetings from eprintln");
let _ = dbg!("Greetings from dbg");
}
partially expands to:
rust
fn initpanichook() {
fn panichook(info: &::std::panic::PanicInfo<'>) {
::core::writeln!(
::customprint::ConcatWriter::fromclosure({
extern "C" { fn consoleerror(: *const u8, : usize); }
|arg1: *const u8, arg2: usize| unsafe { consoleerror(arg1, arg2) }
}),
"{}",
info
).expect("failed writing panic info");
}
::std::panic::sethook(::std::boxed::Box::new(panichook))
}
fn main() { initpanichook();
::core::writeln!(
::custom_print::ConcatWriter::from_closure({
extern "C" { fn console_log(_: *const u8, _: usize); }
|arg1: *const u8, arg2: usize| unsafe { console_log(arg1, arg2) }
}),
"Greetings from println"
).expect("failed writing");
let _ = ::core::writeln!(
::custom_print::ConcatTryWriter::from_closure({
extern "C" { fn console_log(_: *const u8, _: usize); }
|arg1: *const u8, arg2: usize| unsafe { console_log(arg1, arg2) }
}),
"Greetings from try_println"
);
::core::writeln!(
::custom_print::ConcatWriter::from_closure({
extern "C" { fn console_warn(_: *const u8, _: usize); }
|arg1: *const u8, arg2: usize| unsafe { console_warn(arg1, arg2) }
}),
"Greetings from eprintln"
).expect("failed writing");
let _ = ::custom_print::dbgwrite!(
::core::writeln,
::custom_print::ConcatWriter::from_closure({
extern "C" { fn console_error(_: *const u8, _: usize); }
|arg1: *const u8, arg2: usize| unsafe { console_error(arg1, arg2) }
}),
expect,
":?",
"Greetings from dbg"
);
} ```
[API Documentation]
alloc
(implied by std
so enabled by default):
Enables [WriteStringFn
] and [ConcatWriter
] types.std
(enabled by default):
Enables [IoWriter
], {Try}Write{CStr|CString|CCharPtr}Fn
,
[define_panic_hook
] and [define_init_panic_hook
].web-log
]
provides print
, println
, eprint
, eprintln
,
requires wasm-bindgen.wasm-rs-dbg
]
provides dbg
,
requires web-sys.console_log
]
provides logging with trace
, debug
, warn
, error
etc.,
requires log and web-sys.console_error_panic_hook
]
provides panic_hook and panic hook set functions,
requires wasm-bindgen.Errors like
`println` is ambiguous (macro-expanded name vs less macro-expanded name
from outer scope during import/macro resolution)
occur because of the inability to overwrite standard rust macros
in [textual scope] with macro-expanded macros.
Use can use proxy macros to replace std
macros in [textual scope]:
rust
custom_print::define_macro!(cprintln, once: write_fn);
macro_rules! println { ($($args:tt)*) => { cprintln!($($args)*); } }
Alternatively, use can override macro in the [path-based scope]:
rust
custom_print::define_macro!(cprintln, once: write_fn);
use cprintln as println;
See [define_macro
] for more details.
It looks like you have overridden print
-like macros in the [path-based scope],
but you have not overridden them in submodules.
Use proxy macros as it shown above
or do not forget to override it in submodules.
rust
custom_print::define_macro!(cprintln, once: write_fn);
use cprintln as println;
mod submodule {
use cprintln as println;
}
You can always use [cargo expand
] to find out where the problem is.
[closure]: IntoWriteFn<_>
is not satisfiedErrors like:
the trait bound `...: IntoWriteFn<_>` is not satisfied
or
the trait bound `...: IntoTryWriteFn<_>` is not satisfied
,
with note: required by ``...Writer::<F1>::from_closure
``
errors occur because of the inability to determine
the appropriate type of wrapper for the closure.
Specify closure arguments if you haven't already,
or use helper closure that takes acceptable arguments (&str
, &[u8]
, etc.)
and convert them to the arguments your function requires.
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.