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