1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(windows), no_std)]
3
4#[cfg(feature = "log")]
5pub mod log;
6
7use core::cmp::min;
8use core::fmt::{self, Write};
9#[cfg(windows)]
10use {
11 core::ptr::null_mut,
12 io_lifetimes::BorrowedHandle,
13 std::io::IsTerminal,
14 windows_sys::Win32::Foundation::{GetLastError, SetLastError, HANDLE},
15 windows_sys::Win32::Storage::FileSystem::WriteFile,
16 windows_sys::Win32::System::Console::STD_ERROR_HANDLE,
17 windows_sys::Win32::System::Console::{GetStdHandle, WriteConsoleW},
18};
19
20#[cfg(windows)]
22const BUF_LEN: usize = 4096;
23
24struct Writer {
25 pos: usize,
26
27 #[cfg(unix)]
29 buf: [u8; rustix::pipe::PIPE_BUF],
30
31 #[cfg(windows)]
32 buf: [u8; BUF_LEN],
33
34 #[cfg(windows)]
36 console_buf: [u16; BUF_LEN],
37
38 #[cfg(windows)]
39 console_pos: usize,
40
41 #[cfg(feature = "errno")]
42 #[cfg(unix)]
43 saved_errno: errno::Errno,
44
45 #[cfg(windows)]
46 saved_error: u32,
47}
48
49impl Writer {
50 fn new() -> Self {
51 Self {
52 pos: 0,
53
54 #[cfg(unix)]
55 buf: [0_u8; rustix::pipe::PIPE_BUF],
56
57 #[cfg(windows)]
58 buf: [0_u8; BUF_LEN],
59
60 #[cfg(windows)]
61 console_buf: [0_u16; BUF_LEN],
62
63 #[cfg(windows)]
64 console_pos: 0,
65
66 #[cfg(feature = "errno")]
67 #[cfg(unix)]
68 saved_errno: errno::errno(),
69
70 #[cfg(windows)]
71 saved_error: unsafe { GetLastError() },
72 }
73 }
74
75 fn flush(&mut self) {
76 #[cfg(unix)]
77 {
78 self.flush_buf().unwrap();
79 }
80
81 #[cfg(windows)]
82 {
83 let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE) };
84 if unsafe { BorrowedHandle::borrow_raw(stderr as _) }.is_terminal() {
85 self.flush_console(stderr)
86 } else {
87 self.flush_buf()
88 }
89 .unwrap();
90 }
91 }
92
93 fn flush_buf(&mut self) -> fmt::Result {
94 let mut s = &self.buf[..self.pos];
95
96 #[cfg(unix)]
100 let stderr = unsafe { rustix::stdio::stderr() };
101
102 #[cfg(windows)]
103 let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE) };
104
105 while !s.is_empty() {
106 #[cfg(unix)]
107 match rustix::io::write(stderr, s) {
108 Ok(n) => s = &s[n..],
109 Err(rustix::io::Errno::INTR) => (),
110 Err(_) => return Err(fmt::Error),
111 }
112
113 #[cfg(windows)]
114 unsafe {
115 let mut n = 0;
116 if WriteFile(
117 stderr,
118 s.as_ptr().cast(),
119 min(s.len(), u32::MAX as usize) as u32,
120 &mut n,
121 null_mut(),
122 ) == 0
123 {
124 return Err(fmt::Error);
125 }
126 s = &s[n as usize..];
127 }
128 }
129
130 self.pos = 0;
131 Ok(())
132 }
133
134 #[cfg(windows)]
135 fn flush_console(&mut self, handle: HANDLE) -> fmt::Result {
136 let mut s = &self.console_buf[..self.console_pos];
138 while !s.is_empty() {
139 unsafe {
140 let mut n16 = 0;
141 if WriteConsoleW(
142 handle,
143 s.as_ptr().cast(),
144 min(s.len(), u32::MAX as usize) as u32,
145 &mut n16,
146 null_mut(),
147 ) == 0
148 {
149 return Err(fmt::Error);
150 }
151 s = &s[n16 as usize..];
152 }
153 }
154
155 self.console_pos = 0;
156 Ok(())
157 }
158}
159
160impl Drop for Writer {
161 fn drop(&mut self) {
162 #[cfg(feature = "errno")]
163 #[cfg(unix)]
164 errno::set_errno(self.saved_errno);
165
166 #[cfg(windows)]
167 unsafe {
168 SetLastError(self.saved_error);
169 }
170 }
171}
172
173impl fmt::Write for Writer {
174 fn write_str(&mut self, s: &str) -> fmt::Result {
175 #[cfg(windows)]
178 {
179 let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE) };
180 if unsafe { BorrowedHandle::borrow_raw(stderr as _) }.is_terminal() {
181 if self.pos != 0 {
183 return Err(fmt::Error);
184 }
185
186 for c in s.chars() {
188 if self.console_buf.len() - self.console_pos < 2 {
189 self.flush_console(stderr)?;
190 }
191 self.console_pos += c
192 .encode_utf16(&mut self.console_buf[self.console_pos..])
193 .len();
194 }
195 return Ok(());
196 }
197
198 if self.console_pos != 0 {
200 return Err(fmt::Error);
201 }
202 }
203
204 let mut bytes = s.as_bytes();
205 while !bytes.is_empty() {
206 let mut sink = &mut self.buf[self.pos..];
207 if sink.is_empty() {
208 self.flush_buf()?;
209 sink = &mut self.buf;
210 }
211
212 let len = min(sink.len(), bytes.len());
213 let (now, later) = bytes.split_at(len);
214 sink[..len].copy_from_slice(now);
215
216 self.pos += len;
217 bytes = later;
218 }
219 Ok(())
220 }
221}
222
223#[doc(hidden)]
224pub fn _eprint(args: fmt::Arguments<'_>) {
225 let mut writer = Writer::new();
226 writer.write_fmt(args).unwrap();
227 writer.flush();
228}
229
230#[doc(hidden)]
231pub fn _eprintln(args: fmt::Arguments<'_>) {
232 let mut writer = Writer::new();
233 writer.write_fmt(args).unwrap();
234 writer.write_fmt(format_args!("\n")).unwrap();
235 writer.flush();
236}
237
238#[doc(hidden)]
239pub struct _Dbg(Writer);
240
241#[doc(hidden)]
242pub fn _dbg_start() -> _Dbg {
243 _Dbg(Writer::new())
244}
245
246#[doc(hidden)]
247pub fn _dbg_write(dbg: &mut _Dbg, file: &str, line: u32, name: &str, args: fmt::Arguments<'_>) {
248 writeln!(&mut dbg.0, "[{}:{}] {} = {}", file, line, name, args).unwrap();
249}
250
251#[doc(hidden)]
252pub fn _dbg_finish(mut dbg: _Dbg) {
253 dbg.0.flush();
254}
255
256#[macro_export]
266macro_rules! eprint {
267 ($($arg:tt)*) => {
268 $crate::_eprint(core::format_args!($($arg)*))
269 };
270}
271
272#[macro_export]
282macro_rules! eprintln {
283 () => {
284 $crate::eprint!("\n")
285 };
286 ($($arg:tt)*) => {
287 $crate::_eprintln(core::format_args!($($arg)*))
289 };
290}
291
292#[macro_export]
303macro_rules! dbg {
304 () => {
305 $crate::eprintln!("[{}:{}]", core::file!(), core::line!())
306 };
307 ($val:expr $(,)?) => {
308 match $val {
310 tmp => {
311 $crate::eprintln!("[{}:{}] {} = {:#?}",
312 core::file!(), core::line!(), core::stringify!($val), &tmp);
313 tmp
314 }
315 }
316 };
317 ($($val:expr),+ $(,)?) => {
318 let mut dbg = $crate::_dbg_start();
319
320 ($(match $val {
322 tmp => {
323 $crate::_dbg_write(
324 &mut dbg,
325 core::file!(),
326 core::line!(),
327 core::stringify!($val),
328 core::format_args!("{:#?}", &tmp),
329 );
330 tmp
331 }
332 }),+);
333
334 $crate::_dbg_finish(dbg);
335 };
336}