devela/text/fmt/buf.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
// devela::text::fmt::buf
//
//! Non-allocating formatting backed by a buffer.
//
use crate::{fmt_write, iif, FmtArguments, FmtError, FmtResult, TextWrite, _core::cmp::min};
#[allow(unused_imports, reason = "±unsafe")]
use ::core::str::{from_utf8, from_utf8_unchecked};
/// Returns a formatted [`str`] slice backed by a buffer, non-allocating.
///
/// It calls the [`format_buf_args`][format_buf_args()] function with the
/// [`format_args`] macro.
///
/// # Example
/// ```
/// # use devela::format_buf;
/// let mut buf = [0u8; 64];
/// let s = format_buf![&mut buf, "Test: {} {}", "foo", 42];
/// assert_eq!(Ok("Test: foo 42"), s);
/// ```
///
/// # Features
/// Makes use of the `unsafe_str` feature if enabled.
#[macro_export]
#[cfg_attr(cargo_primary_package, doc(hidden))]
macro_rules! format_buf {
($buf:expr, $($args:tt)*) => { $crate::format_buf_args($buf, format_args![$($args)*]) };
}
#[doc(inline)]
pub use format_buf;
/// Returns a formatted [`str`] slice backed by a buffer, non-allocating.
///
/// See also the slightly more convenient to use [`format_buf!`][crate::format_buf!] macro.
///
/// # Example
/// ```
/// # use devela::text::format_buf_args;
/// let mut buf = [0u8; 64];
/// let s = format_buf_args(&mut buf, format_args!["Test: {} {}", "foo", 42]);
/// assert_eq!(Ok("Test: foo 42"), s);
/// ```
///
/// # Features
/// Makes use of the `unsafe_str` feature if enabled.
pub fn format_buf_args<'a>(buf: &'a mut [u8], arg: FmtArguments) -> Result<&'a str, FmtError> {
let mut w = WriteTo::new(buf);
fmt_write(&mut w, arg)?;
w.as_str()
}
#[derive(Debug)]
struct WriteTo<'a> {
buf: &'a mut [u8],
len: usize,
}
#[rustfmt::skip]
impl<'a> WriteTo<'a> {
fn new(buf: &'a mut [u8]) -> Self { WriteTo { buf, len: 0 } }
fn as_str(self) -> Result<&'a str, FmtError> {
if self.len <= self.buf.len() {
#[cfg(any(feature = "safe_text", not(feature = "unsafe_str")))]
{ from_utf8(&self.buf[..self.len]).map_err(|_| FmtError) }
#[cfg(all(not(feature = "safe_text"), feature = "unsafe_str"))]
// SAFETY: the buffer is always filled from a previous &str
{ Ok(unsafe { from_utf8_unchecked(&self.buf[..self.len]) }) }
} else {
Err(FmtError)
}
}
}
impl TextWrite for WriteTo<'_> {
fn write_str(&mut self, s: &str) -> FmtResult<()> {
// IMPROVE?
let rem = &mut self.buf[self.len..];
let raw_s = s.as_bytes();
let num = min(raw_s.len(), rem.len());
rem[..num].copy_from_slice(&raw_s[..num]);
self.len += num;
iif![num < raw_s.len(); Err(FmtError); Ok(())]
}
}