Skip to main content

leodos_protocols/utils/
fmt.rs

1//! `no_std` formatting utilities.
2
3/// A `no_std`-compatible [`core::fmt::Write`] adapter over a byte slice.
4pub struct BufWriter<'a> {
5    /// The output buffer.
6    pub buf: &'a mut [u8],
7    /// Current write position.
8    pub pos: usize,
9}
10
11impl core::fmt::Write for BufWriter<'_> {
12    fn write_str(&mut self, s: &str) -> core::fmt::Result {
13        let b = s.as_bytes();
14        let end = self.pos + b.len();
15        if end > self.buf.len() {
16            return Err(core::fmt::Error);
17        }
18        self.buf[self.pos..end].copy_from_slice(b);
19        self.pos = end;
20        Ok(())
21    }
22}
23
24/// Formats into a byte buffer using [`core::fmt::Write`], returning bytes written.
25#[macro_export]
26macro_rules! fmt {
27    ($buf:expr, $($arg:tt)*) => {{
28        let mut w = $crate::utils::fmt::BufWriter { buf: $buf, pos: 0 };
29        match core::fmt::Write::write_fmt(&mut w, format_args!($($arg)*)) {
30            Ok(()) => Ok(w.pos),
31            Err(_) => Err(core::fmt::Error),
32        }
33    }};
34}
35
36/// An owned, stack-allocated null-terminated C string.
37pub struct CStrBuf<const N: usize> {
38    #[doc(hidden)]
39    pub buf: [u8; N],
40    #[doc(hidden)]
41    pub len: usize,
42}
43
44impl<const N: usize> CStrBuf<N> {
45    /// Returns the contained C string.
46    pub fn as_cstr(&self) -> &core::ffi::CStr {
47        core::ffi::CStr::from_bytes_until_nul(&self.buf[..self.len + 1]).unwrap()
48    }
49}
50
51impl<const N: usize> core::ops::Deref for CStrBuf<N> {
52    type Target = core::ffi::CStr;
53    fn deref(&self) -> &core::ffi::CStr {
54        self.as_cstr()
55    }
56}
57
58/// Formats a null-terminated C string.
59///
60/// Two forms:
61/// - `fmt_cstr!(buf, "fmt", args)` — writes into `buf: &mut [u8]`, returns `Result<&CStr, _>`
62/// - `fmt_cstr!(32, "fmt", args)` — returns `Result<CStrBuf<32>, _>` (owned, no separate buffer)
63#[macro_export]
64macro_rules! fmt_cstr {
65    ($n:literal, $($arg:tt)*) => {{
66        let mut buf = [0u8; $n];
67        let mut w = $crate::utils::fmt::BufWriter { buf: &mut buf, pos: 0 };
68        match core::fmt::Write::write_fmt(&mut w, format_args!($($arg)*)) {
69            Ok(()) if w.pos < w.buf.len() => {
70                w.buf[w.pos] = 0;
71                let len = w.pos;
72                Ok($crate::utils::fmt::CStrBuf { buf, len })
73            }
74            _ => Err(core::fmt::Error),
75        }
76    }};
77    ($buf:expr, $($arg:tt)*) => {{
78        let buf: &mut [u8] = &mut $buf;
79        let mut w = $crate::utils::fmt::BufWriter { buf, pos: 0 };
80        match core::fmt::Write::write_fmt(&mut w, format_args!($($arg)*)) {
81            Ok(()) if w.pos < w.buf.len() => {
82                w.buf[w.pos] = 0;
83                Ok(core::ffi::CStr::from_bytes_until_nul(&w.buf[..w.pos + 1]).unwrap())
84            }
85            _ => Err(core::fmt::Error),
86        }
87    }};
88}