use crate::{CONST, Cmp, FmtArguments, FmtResult, FmtWrite, Str, is, slice};
crate::_use! {compat::from_utf8}
#[doc = crate::_tags!(fmt)]
#[doc = crate::_doc_location!("text/fmt")]
#[macro_export]
#[cfg_attr(cargo_primary_package, doc(hidden))]
macro_rules! format_buf {
($buf:expr, $($arg:tt)*) => {
$crate::FmtWriter::format($buf, $crate::format_args![$($arg)*])
};
(? $buf:expr, $($arg:tt)*) => {
$crate::unwrap![ok_err $crate::FmtWriter::format($buf, $crate::format_args![$($arg)*])]
};
}
#[doc(inline)]
pub use format_buf;
#[doc = crate::_tags!(fmt)]
#[doc = crate::_doc_location!("text/fmt")]
#[derive(Debug)]
pub struct FmtWriter<'a> {
buf: &'a mut [u8],
len: usize,
truncated: bool,
}
CONST! {
#[allow(unused_import)]
_DOC_AS_INTO_STR = "Returns the written content as a valid UTF‑8 string.\n\n
If the final write ended in the middle of a multi‑byte codepoint only the valid prefix is returned.
# Features
Makes use of the `dep_simd_utf8` feature to accelerate validation,
and of the `unsafe_str` feature to avoid double validation.";
_DOC_AS_INTO_STR_CONST = "Returns the written content as a valid UTF‑8 string.\n\n
If the final write ended in the middle of a multi‑byte codepoint only the valid prefix is returned.
Compile-time friendly version using basic validation.
# Features
Makes use of the `unsafe_str` feature to avoid double validation.";
}
#[rustfmt::skip]
impl<'a> FmtWriter<'a> {
#[inline(always)]
pub const fn new(buf: &'a mut [u8]) -> Self { FmtWriter { buf, len: 0, truncated: false } }
#[inline(always)]
pub const fn is_truncated(&self) -> bool { self.truncated }
#[inline(always)]
pub const fn written_len(&self) -> usize { self.len }
pub fn format(buf: &'a mut [u8], args: FmtArguments) -> Result<&'a str, &'a str> {
let mut w = Self::new(buf);
let _ = ::core::fmt::write(&mut w, args);
is![w.is_truncated(), Err(w.into_str()), Ok(w.into_str())]
}
pub fn format_len(buf: &'a mut [u8], args: FmtArguments) -> Result<usize, usize> {
let mut w = Self::new(buf);
let _ = ::core::fmt::write(&mut w, args);
is![w.is_truncated(), Err(w.written_len()), Ok(w.written_len())]
}
pub fn format_len_unchecked(buf: &'a mut [u8], args: FmtArguments) -> usize {
let mut w = Self::new(buf);
let _ = ::core::fmt::write(&mut w, args);
w.written_len()
}
pub const fn write_str_truncate(&mut self, s: &str) -> usize {
let available = self.buf.len().saturating_sub(self.len);
let s_bytes = s.as_bytes();
let n = Cmp(s_bytes.len()).min(available);
if n > 0 {
slice![mut self.buf, self.len, ..self.len + n].copy_from_slice(slice![s_bytes, ..n]);
}
is![n < s_bytes.len(), self.truncated = true];
self.len += n;
n
}
pub const fn write_str_truncate_checked(&mut self, s: &str) -> Result<&str, &str> {
self.write_str_truncate(s);
let written = self.as_str_const();
is![self.is_truncated(), Err(written), Ok(written)]
}
pub const fn write_str_truncate_checked_len(&mut self, s: &str) -> Result<usize, usize> {
let n = self.write_str_truncate(s);
is![self.is_truncated(), Err(n), Ok(n)]
}
#[must_use]
#[doc = _DOC_AS_INTO_STR!()]
pub fn as_str(&self) -> &str {
match from_utf8(&self.buf[..self.len]) { Ok(valid_str) => valid_str,
Err(e) => Self::get_str_from_slice(self.buf, e.valid_up_to()),
}
}
#[must_use]
#[doc = _DOC_AS_INTO_STR!()]
pub fn into_str(self) -> &'a str {
match from_utf8(&self.buf[..self.len]) {
Ok(valid_str) => valid_str,
Err(e) => Self::get_str_from_slice(self.buf, e.valid_up_to()),
}
}
#[must_use]
#[doc = _DOC_AS_INTO_STR_CONST!()]
pub const fn as_str_const(&'a self) -> &'a str {
match Str::from_utf8(slice![self.buf, ..self.len]) {
Ok(valid_str) => valid_str,
Err(e) => Self::get_str_from_slice_const(self.buf, e.valid_up_to),
}
}
#[must_use]
#[doc = _DOC_AS_INTO_STR_CONST!()]
pub const fn into_str_const(self) -> &'a str {
match Str::from_utf8(slice![self.buf, ..self.len]) {
Ok(valid_str) => valid_str,
Err(e) => Self::get_str_from_slice_const(self.buf, e.valid_up_to)
}
}
#[inline(always)]
fn get_str_from_slice(slice: &[u8], valid_len: usize) -> &str {
let valid_range = &slice[..valid_len]; #[cfg(any(feature = "safe_text", not(feature = "unsafe_str")))]
{ from_utf8(valid_range).unwrap() } #[cfg(all(not(feature = "safe_text"), feature = "unsafe_str"))]
{ unsafe { Str::from_utf8_unchecked(valid_range) } }
}
#[inline(always)]
const fn get_str_from_slice_const(slice: &[u8], valid_len: usize) -> &str {
let valid_range = slice![slice, ..valid_len];
#[cfg(any(feature = "safe_text", not(feature = "unsafe_str")))]
{ crate::unwrap![ok Str::from_utf8(valid_range)] }
#[cfg(all(not(feature = "safe_text"), feature = "unsafe_str"))]
{ unsafe { Str::from_utf8_unchecked(valid_range) } }
}
}
impl FmtWrite for FmtWriter<'_> {
#[inline(always)]
fn write_str(&mut self, s: &str) -> FmtResult<()> {
self.write_str_truncate(s);
Ok(())
}
}