Skip to main content

libc_print/
lib.rs

1//! Implements `println!`, `eprintln!` and `dbg!` on top of the `libc `crate without requiring
2//! the use of an allocator.
3//!
4//! Allows you to use these macros in a #!\[no_std\] context, or in a situation where the
5//! traditional Rust streams might not be available (ie: at process shutdown time).
6//!
7//! [`libc_writeln`] and [`libc_ewriteln`] are provided for cases where you may not wish
8//! to pull in the overhead of the formatter code and simply wish to print C-style strings.
9//!
10//! ## Usage
11//!
12//! Exactly as you'd use `println!`, `eprintln!` and `dbg!`.
13//!
14//! ```rust
15//! # use libc_print::*;
16//! // Use the default `libc_`-prefixed macros:
17//! # fn test1()
18//! # {
19//! libc_println!("Hello {}!", "stdout");
20//! libc_eprintln!("Hello {}!", "stderr");
21//! let a = 2;
22//! let b = libc_dbg!(a * 2) + 1;
23//! assert_eq!(b, 5);
24//! # }
25//! ```
26//!
27//! Or you can import aliases to `std` names:
28//!
29//! ```rust
30//! use libc_print::std_name::{println, eprintln, dbg};
31//!
32//! # fn test2()
33//! # {
34//! println!("Hello {}!", "stdout");
35//! eprintln!("Hello {}!", "stderr");
36//! let a = 2;
37//! let b = dbg!(a * 2) + 1;
38//! assert_eq!(b, 5);
39//! # }
40//! ```
41
42#![no_std]
43#![warn(unsafe_op_in_unsafe_fn)]
44
45use core::convert::TryFrom;
46
47// These constants are used by the macros but we don't want to expose
48// them to library users.
49#[doc(hidden)]
50pub const __LIBC_NEWLINE: &str = "\n";
51#[doc(hidden)]
52pub const __LIBC_STDOUT: i32 = 1;
53#[doc(hidden)]
54pub const __LIBC_STDERR: i32 = 2;
55
56#[doc(hidden)]
57pub struct __LibCWriter(i32);
58
59impl core::fmt::Write for __LibCWriter {
60    #[inline]
61    fn write_str(&mut self, s: &str) -> core::fmt::Result {
62        __libc_println(self.0, s)
63    }
64}
65
66impl __LibCWriter {
67    #[inline]
68    pub fn new(handle: i32) -> __LibCWriter {
69        __LibCWriter(handle)
70    }
71
72    #[inline]
73    pub fn write_fmt(&mut self, args: core::fmt::Arguments) -> core::fmt::Result {
74        core::fmt::Write::write_fmt(self, args)
75    }
76
77    #[inline]
78    pub fn write_str(&mut self, s: &str) -> core::fmt::Result {
79        __libc_println(self.0, s)
80    }
81
82    #[inline]
83    pub fn write_nl(&mut self) -> core::fmt::Result {
84        __libc_println(self.0, __LIBC_NEWLINE)
85    }
86}
87
88#[doc(hidden)]
89#[inline]
90pub fn __libc_println(handle: i32, msg: &str) -> core::fmt::Result {
91    let msg = msg.as_bytes();
92
93    let mut written = 0;
94    while written < msg.len() {
95        match unsafe { libc_write(handle, &msg[written..]) } {
96            // Ignore errors
97            None | Some(0) => break,
98            Some(res) => written += res,
99        }
100    }
101
102    Ok(())
103}
104
105#[cfg(not(any(all(target_family = "wasm", target_os = "unknown"), target_os = "none")))]
106mod write {
107    pub(crate) use libc::write;
108}
109
110#[cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "none"))]
111mod write {
112    // The user is required to provide this
113    unsafe extern "C" {
114        pub(crate) fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize;
115    }
116}
117
118unsafe fn libc_write(handle: i32, bytes: &[u8]) -> Option<usize> {
119    usize::try_from(unsafe { write::write(handle as _, bytes.as_ptr() as _, bytes.len() as _) })
120        .ok()
121}
122
123/// Macro for printing to the standard output, with a newline.
124///
125/// Does not panic on failure to write - instead silently ignores errors.
126///
127/// See [`println!`](https://doc.rust-lang.org/std/macro.println.html) for
128/// full documentation.
129///
130/// You may wish to `use libc_print::std_name::*` to use a replacement
131/// `println!` macro instead of this longer name.
132#[macro_export]
133macro_rules! libc_println {
134    () => { $crate::libc_println!("") };
135    ($($arg:tt)*) => {
136        {
137            #[allow(unused_must_use)]
138            {
139                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
140                stm.write_fmt(format_args!($($arg)*));
141                stm.write_nl();
142            }
143        }
144    };
145}
146
147/// Macro for printing to the standard output.
148///
149/// Does not panic on failure to write - instead silently ignores errors.
150///
151/// See [`print!`](https://doc.rust-lang.org/std/macro.print.html) for
152/// full documentation.
153///
154/// You may wish to `use libc_print::std_name::*` to use a replacement
155/// `print!` macro instead of this longer name.
156#[macro_export]
157macro_rules! libc_print {
158    ($($arg:tt)*) => {
159        {
160            #[allow(unused_must_use)]
161            {
162                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
163                stm.write_fmt(format_args!($($arg)*));
164            }
165        }
166    };
167}
168
169/// Macro for printing to the standard error, with a newline.
170///
171/// Does not panic on failure to write - instead silently ignores errors.
172///
173/// See [`eprintln!`](https://doc.rust-lang.org/std/macro.eprintln.html) for
174/// full documentation.
175///
176/// You may wish to `use libc_print::std_name::*` to use a replacement
177/// `eprintln!` macro instead of this longer name.
178#[macro_export]
179macro_rules! libc_eprintln {
180    () => { $crate::libc_eprintln!("") };
181    ($($arg:tt)*) => {
182        {
183            #[allow(unused_must_use)]
184            {
185                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
186                stm.write_fmt(format_args!($($arg)*));
187                stm.write_nl();
188            }
189        }
190    };
191}
192
193/// Macro for printing to the standard error.
194///
195/// Does not panic on failure to write - instead silently ignores errors.
196///
197/// See [`eprint!`](https://doc.rust-lang.org/std/macro.eprint.html) for
198/// full documentation.
199///
200/// You may wish to `use libc_print::std_name::*` to use a replacement
201/// `eprint!` macro instead of this longer name.
202#[macro_export]
203macro_rules! libc_eprint {
204    ($($arg:tt)*) => {
205        {
206            #[allow(unused_must_use)]
207            {
208                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
209                stm.write_fmt(format_args!($($arg)*));
210            }
211        }
212    };
213}
214
215/// Macro for printing a static string to the standard output.
216///
217/// Does not panic on failure to write - instead silently ignores errors.
218#[macro_export]
219macro_rules! libc_write {
220    ($arg:expr) => {
221        #[allow(unused_must_use)]
222        {
223            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
224            stm.write_str($arg);
225        }
226    };
227}
228
229/// Macro for printing a static string to the standard error.
230///
231/// Does not panic on failure to write - instead silently ignores errors.
232#[macro_export]
233macro_rules! libc_ewrite {
234    ($arg:expr) => {{
235        #[allow(unused_must_use)]
236        {
237            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
238            stm.write_str($arg);
239        }
240    }};
241}
242
243/// Macro for printing a static string to the standard output, with a newline.
244///
245/// Does not panic on failure to write - instead silently ignores errors.
246#[macro_export]
247macro_rules! libc_writeln {
248    ($arg:expr) => {
249        #[allow(unused_must_use)]
250        {
251            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
252            stm.write_str($arg);
253            stm.write_nl();
254        }
255    };
256}
257
258/// Macro for printing a static string to the standard error, with a newline.
259///
260/// Does not panic on failure to write - instead silently ignores errors.
261#[macro_export]
262macro_rules! libc_ewriteln {
263    ($arg:expr) => {{
264        #[allow(unused_must_use)]
265        {
266            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
267            stm.write_str($arg);
268            stm.write_nl();
269        }
270    }};
271}
272
273/// Prints and returns the value of a given expression for quick and dirty
274/// debugging.
275///
276/// An example:
277///
278/// ```rust
279/// let a = 2;
280/// let b = dbg!(a * 2) + 1;
281/// //      ^-- prints: [src/main.rs:2] a * 2 = 4
282/// assert_eq!(b, 5);
283/// ```
284///
285/// See [dbg!](https://doc.rust-lang.org/std/macro.dbg.html) for full documentation.
286///
287/// You may wish to `use libc_print::std_name::*` to use a replacement
288/// `dbg!` macro instead of this longer name.
289#[macro_export]
290macro_rules! libc_dbg {
291    () => {
292        $crate::libc_eprintln!("[{}:{}]", ::core::file!(), ::core::line!())
293    };
294    ($val:expr $(,)?) => {
295        match $val {
296            tmp => {
297                $crate::libc_eprintln!("[{}:{}] {} = {:#?}", ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
298                tmp
299            }
300        }
301    };
302    ($($val:expr),+ $(,)?) => {
303        ($($crate::libc_dbg!($val)),+,)
304    };
305}
306
307/// This package contains the `libc_print` macros, but using the stdlib names
308/// such as `println!`, `print!`, etc.
309pub mod std_name {
310    pub use super::libc_dbg as dbg;
311    pub use super::libc_eprint as eprint;
312    pub use super::libc_eprintln as eprintln;
313    pub use super::libc_print as print;
314    pub use super::libc_println as println;
315
316    #[cfg(test)]
317    mod tests_std_name {
318        use super::{eprintln, println};
319
320        #[test]
321        fn test_stdout() {
322            println!("stdout fd = {}", crate::__LIBC_STDOUT);
323        }
324
325        #[test]
326        fn test_stderr() {
327            eprintln!("stderr fd = {}", crate::__LIBC_STDERR);
328        }
329    }
330}
331
332#[cfg(test)]
333mod tests {
334    #[test]
335    fn test_stdout() {
336        super::libc_println!("stdout fd = {}", super::__LIBC_STDOUT);
337    }
338
339    #[test]
340    fn test_stderr() {
341        super::libc_eprintln!("stderr fd = {}", super::__LIBC_STDERR);
342    }
343
344    #[test]
345    fn test_stdout_write() {
346        super::libc_writeln!("stdout!");
347    }
348
349    #[test]
350    fn test_stderr_write() {
351        super::libc_ewriteln!("stderr!");
352    }
353
354    #[test]
355    fn test_dbg() {
356        let a = 2;
357        let b = libc_dbg!(a * 2) + 1;
358        assert_eq!(b, 5);
359    }
360
361    #[test]
362    fn test_in_closure_expression() {
363        use super::std_name::*;
364        // https://github.com/mmastrac/rust-libc-print/issues/86
365        let _ = Result::<(), ()>::Ok(()).unwrap_or_else(|err| eprintln!("error: {:?}", err));
366    }
367}