custom_print/
lib.rs

1//! The `custom-print` crate helps you to define `print`, `println` and `dbg` macros
2//! in wasm and customize them for other targets without any dependencies.
3//!
4//! # About
5//!
6//! This crate helps you to define `print`-like macros, `dbg` and `panic_hook`
7//! on `wasm32-unknown-unknown` target without `wasm-bindgen` dependency.
8//! Also, it can be used on another targets to override default std `write`-like macros,
9//! add `try_` macros variants, or to specify panic hook function.
10//! It works on stable Rust,
11//! supports `no-alloc` and `no-std` environments and has no dependencies.
12//!
13//! In most cases it is suggested to use macros
14//! [`define_macros`], [`define_macro`] or [`define_init_panic_hook`].
15//! These macros define macros or functions with the specified names that use
16//! [`FmtWriter`], [`FmtTryWriter`], [`ConcatWriter`], [`ConcatTryWriter`],
17//! [`IoWriter`] or [`IoTryWriter`] with the specified closure, unsafe function or extern function.
18//!
19//! # Usage
20//!
21//! First, add the following to your `Cargo.toml`:
22//!
23//! ```toml
24//! [dependencies]
25//! custom-print = "1.0.0"
26//! ```
27//!
28//! This crate depends on the standard library by default.
29//! To use this crate in a `#![no_std]` context but with heap-allocations enabled,
30//! use `default-features = false` in your `Cargo.toml` as shown below:
31//!
32//! ```toml
33//! [dependencies.custom-print]
34//! version = "1.0.0"
35//! default-features = false
36//! features = ["alloc"]
37//! ```
38//!
39//! # Examples
40//!
41//! An example with an extern functions that takes a UTF-8 chars pointer and byte length
42//! with no `std` prelude:
43#![cfg_attr(feature = "alloc", doc = " ```rust")]
44#![cfg_attr(not(feature = "alloc"), doc = " ```rust,compile_fail")]
45//! #![no_std]
46//! extern crate std;
47//!
48//! # pub mod ffi {
49//! #     #[no_mangle] pub extern "C" fn console_log(_: *const u8, _: usize) {}
50//! #     #[no_mangle] pub extern "C" fn console_warn(_: *const u8, _: usize) {}
51//! #     #[no_mangle] pub extern "C" fn console_error(_: *const u8, _: usize) {}
52//! # }
53//! #
54//! custom_print::define_macros!({ print, println },
55//!     concat, extern "C" fn console_log(_: *const u8, _: usize));
56//! custom_print::define_macros!({ eprint, eprintln, dbg },
57//!     concat, extern "C" fn console_warn(_: *const u8, _: usize));
58//! custom_print::define_init_panic_hook!(
59//!     concat, extern "C" fn console_error(_: *const u8, _: usize));
60//!
61//! fn main() {
62//!     init_panic_hook();
63//!     println!("println");
64//!     print!("print");
65//!     eprintln!("eprintln");
66//!     eprint!("eprint");
67//!     dbg!("dbg");
68//! }
69//! ```
70//!
71//! An example with a closure that takes an [`str`] reference
72//! in `no_std` and `no_alloc` context:
73#![cfg_attr(feature = "std", doc = " ```rust")]
74#![cfg_attr(not(feature = "std"), doc = " ```rust,compile_fail")]
75//! #![no_std]
76//! custom_print::define_macros!({ print, println }, fmt, |_value: &str| { /* ... */ });
77//!
78//! # fn main() { // avoid clippy::needless_doctest_main false positive
79//! fn main() {
80//!     println!("println");
81//!     print!("print");
82//! }
83//! # main();
84//! # }
85//! ```
86//!
87//! An example with a function that takes a [`c_char`] pointer and overriding
88//! [`std::print`] and [`std::println`] functions:
89#![cfg_attr(feature = "std", doc = " ```rust")]
90#![cfg_attr(not(feature = "std"), doc = " ```rust,compile_fail")]
91//! fn write(_value: *const std::os::raw::c_char) { /* ... */ }
92//!
93//! custom_print::define_macros!({ cprint, cprintln }, concat, crate::write);
94//! macro_rules! print { ($($args:tt)*) => { cprint!($($args)*); } }
95//! macro_rules! println { ($($args:tt)*) => { cprintln!($($args)*); } }
96//!
97//! fn main() {
98//!     println!("println");
99//!     print!("print");
100//! }
101//! ```
102//!
103//! Macro generation macros support specifying custom attributes, so you can
104//! re-export the generated macros and use them in other crates:
105//! ```rust
106//! // foo crate:
107//!
108//! custom_print::define_macros!(
109//!     #[macro_export] { print, println, cprint, cprintln },
110//!     fmt, |_value: &str| { /* ... */ }
111//! );
112//!
113//! # fn main() {
114//! fn func() {
115//!     cprintln!("cprintln");
116//!     cprint!("cprint");
117//! }
118//!
119//! // bar crate:
120//!
121//! fn main() {
122//! #   /*
123//!     foo::println!("println");
124//!     foo::print!("print");
125//! #   */
126//! }
127//! # func();
128//! # main();
129//! # }
130//! ```
131//!
132//! You can find more usage examples in the tests and examples repository folders.
133//!
134//! # Macro expansion
135//!
136//! The example with [`define_macros`] and [`define_init_panic_hook`] with extern functions:
137#![cfg_attr(feature = "alloc", doc = " ```rust")]
138#![cfg_attr(not(feature = "alloc"), doc = " ```rust,compile_fail")]
139//! #![no_std]
140//! extern crate std;
141//!
142//! # pub mod ffi {
143//! #     #[no_mangle] pub extern "C" fn console_log(_: *const u8, _: usize) {}
144//! #     #[no_mangle] pub extern "C" fn console_warn(_: *const u8, _: usize) {}
145//! #     #[no_mangle] pub extern "C" fn console_error(_: *const u8, _: usize) {}
146//! # }
147//! #
148//! custom_print::define_macros!({ print, println, try_println },
149//!     concat, extern "C" fn console_log(_: *const u8, _: usize));
150//! custom_print::define_macros!({ eprint, eprintln, dbg },
151//!     concat, extern "C" fn console_warn(_: *const u8, _: usize));
152//! custom_print::define_init_panic_hook!(
153//!     concat, extern "C" fn console_error(_: *const u8, _: usize));
154//!
155//! fn main() {
156//!     init_panic_hook();
157//!     println!("Greetings from println");
158//!     let _ = try_println!("Greetings from try_println");
159//!     eprintln!("Greetings from eprintln");
160//!     let _ = dbg!("Greetings from dbg");
161//! }
162//! ```
163//! partially expands to:
164#![cfg_attr(feature = "alloc", doc = " ```rust")]
165#![cfg_attr(not(feature = "alloc"), doc = " ```rust,compile_fail")]
166//! # pub mod ffi {
167//! #     #[no_mangle] pub extern "C" fn console_log(_: *const u8, _: usize) {}
168//! #     #[no_mangle] pub extern "C" fn console_warn(_: *const u8, _: usize) {}
169//! #     #[no_mangle] pub extern "C" fn console_error(_: *const u8, _: usize) {}
170//! # }
171//! #
172//! fn init_panic_hook() {
173//!     fn panic_hook(info: &::std::panic::PanicInfo<'_>) {
174//!         ::core::writeln!(
175//!             ::custom_print::ConcatWriter::from_closure({
176//!                 extern "C" { fn console_error(_: *const u8, _: usize); }
177//!                 |arg1: *const u8, arg2: usize| unsafe { console_error(arg1, arg2) }
178//!             }),
179//!             "{}",
180//!             info
181//!         ).expect("failed writing panic info");
182//!     }
183//!     ::std::panic::set_hook(::std::boxed::Box::new(panic_hook))
184//! }
185//!
186//! fn main() {
187//!     init_panic_hook();
188//!
189//!     ::core::writeln!(
190//!         ::custom_print::ConcatWriter::from_closure({
191//!             extern "C" { fn console_log(_: *const u8, _: usize); }
192//!             |arg1: *const u8, arg2: usize| unsafe { console_log(arg1, arg2) }
193//!         }),
194//!         "Greetings from println"
195//!     ).expect("failed writing");
196//!
197//!     let _ = ::core::writeln!(
198//!         ::custom_print::ConcatTryWriter::from_closure({
199//!             extern "C" { fn console_log(_: *const u8, _: usize); }
200//!             |arg1: *const u8, arg2: usize| unsafe { console_log(arg1, arg2) }
201//!         }),
202//!         "Greetings from try_println"
203//!     );
204//!
205//!     ::core::writeln!(
206//!         ::custom_print::ConcatWriter::from_closure({
207//!             extern "C" { fn console_warn(_: *const u8, _: usize); }
208//!             |arg1: *const u8, arg2: usize| unsafe { console_warn(arg1, arg2) }
209//!         }),
210//!         "Greetings from eprintln"
211//!     ).expect("failed writing");
212//!
213//!     let _ = ::custom_print::dbgwrite!(
214//!         ::core::writeln,
215//!         ::custom_print::ConcatWriter::from_closure({
216//!             extern "C" { fn console_error(_: *const u8, _: usize); }
217//!             |arg1: *const u8, arg2: usize| unsafe { console_error(arg1, arg2) }
218//!         }),
219//!         expect,
220//!         ":?",
221//!         "Greetings from dbg"
222//!     );
223//! }
224//! ```
225//!
226//! # Feature Flags
227//!
228//! - `alloc` (implied by `std` so enabled by default):
229//!   Enables [`WriteStringFn`] and [`ConcatWriter`] types.
230//! - `std` (enabled by default):
231//!   Enables [`IoWriter`], `{Try}Write{CStr|CString|CCharPtr}Fn`,
232//!   [`define_panic_hook`] and [`define_init_panic_hook`].
233//!
234//! # Similar crates
235//!
236//! - [`web-log`]
237//!   provides `print`, `println`, `eprint`, `eprintln`,
238//!   requires wasm-bindgen.
239//! - [`wasm-rs-dbg`]
240//!   provides `dbg`,
241//!   requires web-sys.
242//! - [`console_log`]
243//!   provides logging with `trace`, `debug`, `warn`, `error` etc.,
244//!   requires log and web-sys.
245//! - [`console_error_panic_hook`]
246//!   provides panic_hook and panic hook set functions,
247//!   requires wasm-bindgen.
248//!
249//! # Troubleshooting
250//!
251//! ## Macro name is ambiguous
252//!
253//! Errors like
254//! `` `println` is ambiguous (macro-expanded name vs less macro-expanded name
255//! from outer scope during import/macro resolution)``
256//! occur because of the inability to overwrite standard rust macros
257//! in [textual scope] with macro-expanded macros.
258//!
259//! Use can use proxy macros to replace `std` macros in [textual scope]:
260//! ```rust
261//! custom_print::define_macro!(cprintln, once: write_fn);
262//! macro_rules! println { ($($args:tt)*) => { cprintln!($($args)*); } }
263//! ```
264//!
265//! Alternatively, use can override macro in the [path-based scope]:
266//! ```rust
267//! custom_print::define_macro!(cprintln, once: write_fn);
268//! use cprintln as println;
269//! ```
270//!
271//! See [`define_macro`] for more details.
272//!
273//! ## Println, dbg and others do nothing in submodules
274//!
275//! It looks like you have overridden `print`-like macros in the [path-based scope],
276//! but you have not overridden them in submodules.
277//! Use proxy macros as it shown [above](#macro_name_is_ambiguous)
278//! or do not forget to override it in submodules.
279//! ```rust
280//! custom_print::define_macro!(cprintln, once: write_fn);
281//! use cprintln as println;
282//! mod submodule {
283//!     use cprintln as println;
284//! }
285//! ```
286//!
287//! You can always use [`cargo expand`] to find out where the problem is.
288//!
289//! ## The trait bound `[closure]: IntoWriteFn<_>` is not satisfied
290//!
291//! Errors like:
292//! ``the trait bound `...: IntoWriteFn<_>` is not satisfied`` or
293//! ``the trait bound `...: IntoTryWriteFn<_>` is not satisfied``,
294//! with `note: required by ``...Writer::<F1>::from_closure` ``
295//! errors occur because of the inability to determine
296//! the appropriate type of wrapper for the closure.
297//!
298//! Specify closure arguments if you haven't already,
299//! or use helper closure that takes acceptable arguments (`&str`, `&[u8]`, etc.)
300//! and convert them to the arguments your function requires.
301//!
302//! # License
303//!
304//! Licensed under either of
305//!
306//! - Apache License, Version 2.0
307//!   ([LICENSE-APACHE](https://github.com/zheland/custom-print/blob/master/LICENSE-APACHE) or
308//!   [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0))
309//! - MIT license
310//!   ([LICENSE-MIT](https://github.com/zheland/custom-print/blob/master/LICENSE-MIT) or
311//!   [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT))
312//!
313//! at your option.
314//!
315//! ## Contribution
316//!
317//! Unless you explicitly state otherwise, any contribution intentionally submitted
318//! for inclusion in the work by you, as defined in the Apache-2.0 license,
319//! shall be dual licensed as above, without any
320//! additional terms or conditions.
321//!
322//! [`str`]: https://doc.rust-lang.org/std/str/index.html
323//! [`c_char`]: https://doc.rust-lang.org/std/os/raw/type.c_char.html
324//! [`std::print`]: https://doc.rust-lang.org/std/macro.print.html
325//! [`std::println`]: https://doc.rust-lang.org/std/macro.println.html
326//! [`define_macro`]: macro.define_macro.html
327//! [`define_macros`]: macro.define_macros.html
328//! [`define_panic_hook`]: macro.define_panic_hook.html
329//! [`define_init_panic_hook`]: macro.define_init_panic_hook.html
330//! [`WriteStringFn`]: struct.WriteStringFn.html
331//! [`FmtWriter`]: struct.FmtWriter.html
332//! [`FmtTryWriter`]: struct.FmtTryWriter.html
333//! [`ConcatWriter`]: struct.ConcatWriter.html
334//! [`ConcatTryWriter`]: struct.ConcatTryWriter.html
335//! [`IoWriter`]: struct.IoWriter.html
336//! [`IoTryWriter`]: struct.IoTryWriter.html
337//! [`web-log`]: https://crates.io/crates/web-log
338//! [`wasm-rs-dbg`]: https://crates.io/crates/wasm-rs-dbg
339//! [`console_log`]: https://crates.io/crates/console_log
340//! [`console_error_panic_hook`]: https://crates.io/crates/console_error_panic_hook
341//! [`cargo expand`]: https://crates.io/crates/cargo-expand
342//! [textual scope]: https://doc.rust-lang.org/reference/macros-by-example.html#scoping-exporting-and-importing
343//! [path-based scope]: https://doc.rust-lang.org/reference/macros-by-example.html#scoping-exporting-and-importing
344
345#![warn(
346    clippy::all,
347    rust_2018_idioms,
348    missing_copy_implementations,
349    missing_debug_implementations,
350    single_use_lifetimes,
351    missing_docs,
352    trivial_casts,
353    unused_import_braces,
354    unused_qualifications,
355    unused_results
356)]
357#![no_std]
358
359#[cfg(feature = "alloc")]
360extern crate alloc;
361
362#[cfg(feature = "std")]
363extern crate std;
364
365#[cfg(feature = "alloc")]
366mod concat_try_writer;
367#[cfg(feature = "alloc")]
368mod concat_writer;
369mod flush;
370mod flush_fn;
371mod fmt_try_writer;
372mod fmt_writer;
373mod into_try_write_fn;
374mod into_write_fn;
375#[cfg(feature = "std")]
376mod io_try_writer;
377#[cfg(feature = "std")]
378mod io_writer;
379mod macros;
380mod never_error;
381mod write_bytes;
382mod write_fns;
383mod write_str;
384
385#[cfg(feature = "alloc")]
386pub use concat_try_writer::{ConcatTryWriter, IntoConcatWriteResult};
387#[cfg(feature = "alloc")]
388pub use concat_writer::{ConcatWriter, ExpectConcatWriteResult};
389pub use flush::Flush;
390pub use flush_fn::FlushFn;
391pub use fmt_try_writer::{FmtTryWriter, IntoFmtWriteResult};
392pub use fmt_writer::{ExpectFmtWriteResult, FmtWriter};
393pub use into_try_write_fn::IntoTryWriteFn;
394pub use into_write_fn::IntoWriteFn;
395#[cfg(feature = "std")]
396pub use io_try_writer::{IntoIoFlushResult, IntoIoWriteResult, IoTryWriter};
397#[cfg(feature = "std")]
398pub use io_writer::{ExpectIoFlushResult, ExpectIoWriteResult, IoWriter};
399pub use never_error::NeverError;
400pub use write_bytes::WriteBytes;
401#[cfg(feature = "alloc")]
402pub use write_fns::WriteStringFn;
403#[cfg(feature = "std")]
404pub use write_fns::{
405    TryWriteCCharPtrFn, TryWriteCStrFn, TryWriteCStringFn, WriteCCharPtrFn, WriteCStrFn,
406    WriteCStringFn,
407};
408pub use write_fns::{WriteBytesFn, WriteLenPtrFn, WritePtrLenFn, WriteStrFn};
409pub use write_str::{WriteStr, WriteStrAsBytes};