unix_print/
lib.rs

1//! A no_std, no_libc stdout/stderr printer with a similar interface as Rust std's (e)print(ln)-macros
2#![no_std]
3#![warn(clippy::all)]
4#![warn(clippy::pedantic)]
5use sc::syscall;
6
7
8/// Should be private but needs to be exposed for the macros to work
9pub const __STDOUT: usize = 1;
10
11/// Should be private but needs to be exposed for the macros to work
12pub const __STDERR: usize = 2;
13
14/// Corresponds to std's `print!`-macro
15#[macro_export]
16macro_rules! unix_print {
17    ($($arg:tt)*) => {
18        let mut __unix_print_writer = $crate::UnixWriter::stdout();
19        let _ = core::fmt::Write::write_fmt(&mut __unix_print_writer, format_args!($($arg)*));
20    }
21}
22
23/// Corresponds to std's `println!`-macro
24#[macro_export]
25macro_rules! unix_println {
26    () => {
27        let _ = $crate::__write_to_handle($crate::__STDOUT, "\n".as_bytes());
28    };
29    ($($arg:tt)*) => {
30        let mut __unix_print_writer = $crate::UnixWriter::stdout();
31        let _ = core::fmt::Write::write_fmt(&mut __unix_print_writer, format_args!($($arg)*));
32        let _ = __unix_print_writer.write_newline();
33
34    }
35}
36
37/// Corresponds to std's `eprint!`-macro
38#[macro_export]
39macro_rules! unix_eprint {
40    ($($arg:tt)*) => {
41        let mut __unix_print_writer = $crate::UnixWriter::stderr();
42        let _ = core::fmt::Write::write_fmt(&mut __unix_print_writer, format_args!($($arg)*));
43    }
44}
45
46/// Corresponds to std's `eprintln!`-macro
47#[macro_export]
48macro_rules! unix_eprintln {
49    () => {
50        let _ = $crate::__write_to_handle($crate::__STDERR, "\n".as_bytes());
51    };
52    ($($arg:tt)*) => {
53        let mut __unix_print_writer = $crate::UnixWriter::stderr();
54        let _ = core::fmt::Write::write_fmt(&mut __unix_print_writer, format_args!($($arg)*));
55        let _ = __unix_print_writer.write_newline();
56
57    }
58}
59
60/// Corresponds to std's `dbg!`-macro
61#[macro_export]
62macro_rules! unix_dbg {
63    () => {
64        $crate::unix_eprintln!("[{}:{}]", core::file!(), core::line!())
65    };
66    ($val:expr $(,)?) => {
67        match $val {
68            tmp => {
69                $crate::unix_eprintln!("[{}:{}] {} = {:#?}",
70                    core::file!(), core::line!(), core::stringify!($val), &tmp);
71                tmp
72            }
73        }
74    };
75    ($($val:expr),+ $(,)?) => {
76        ($($crate::dbg!($val)),+,)
77    };
78}
79
80/// Should be private but needs to be exposed for the macro to work
81#[allow(clippy::cast_possible_wrap)]
82#[must_use]
83pub fn __write_to_handle(fd: usize, msg: &[u8]) -> isize {
84    unsafe {
85        syscall!(WRITE, fd, msg.as_ptr(), msg.len()) as isize
86    }
87}
88
89#[allow(clippy::cast_sign_loss)]
90fn try_print(fd: usize, msg: &str) -> core::fmt::Result {
91    let buf = msg.as_bytes();
92    let len = buf.len();
93    let mut flushed = 0;
94    loop {
95        let res = __write_to_handle(fd, &buf[flushed..]);
96        match res.cmp(&0) {
97            core::cmp::Ordering::Less => return Err(core::fmt::Error),
98            core::cmp::Ordering::Equal => return Ok(()),
99            core::cmp::Ordering::Greater => {
100                // Greater than zero
101                flushed += res as usize;
102                if flushed >= len {
103                    return Ok(())
104                }
105            }
106        }
107    }
108}
109
110pub struct UnixWriter(usize);
111
112impl UnixWriter {
113    #[must_use]
114    pub fn stdout() -> Self {
115        Self(__STDOUT)
116    }
117
118    #[must_use]
119    pub fn stderr() -> Self {
120        Self(__STDERR)
121    }
122
123    /// # Errors
124    /// Will return an error if the underlying syscall fails
125    pub fn write_newline(&self) -> core::fmt::Result {
126        try_print(self.0, "\n")
127    }
128}
129
130impl core::fmt::Write for UnixWriter {
131    fn write_str(&mut self, s: &str) -> core::fmt::Result {
132        try_print(self.0, s)
133    }
134}
135
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_prints() {
143        unix_println!("-- First");
144        unix_print!("My first");
145        unix_print!(" two messages");
146        unix_print!(" were cutoff but it's fine");
147        unix_println!();
148        unix_println!("-- Second\nHello there {}", "me");
149    }
150
151    #[test]
152    fn test_eprints() {
153        unix_eprintln!("-- First");
154        unix_eprint!("My first");
155        unix_eprint!(" two messages");
156        unix_eprint!(" were cutoff but it's fine");
157        unix_eprintln!();
158        unix_eprintln!("-- Second\nHello there {}", "me");
159    }
160
161    #[test]
162    fn test_dbgs() {
163        unix_dbg!();
164        let val = 5;
165        let res = unix_dbg!(val) - 5;
166        assert_eq!(0, res);
167    }
168
169}