use core::fmt::{self, Write as _};
use crate::std::string::String;
#[must_use]
#[expect(
clippy::expect_used,
reason = "match format! semantics for caller-provided formatting implementations"
)]
pub fn format_with_capacity(capacity: usize, args: fmt::Arguments<'_>) -> String {
let mut output = String::with_capacity(capacity);
output
.write_fmt(args)
.expect("formatting into a String cannot fail");
output
}
pub fn try_format_into<'a>(
output: &'a mut String,
args: fmt::Arguments<'_>,
) -> Result<&'a str, fmt::Error> {
output.clear();
output.write_fmt(args)?;
Ok(output.as_str())
}
pub fn display_fn<F>(formatter: F) -> DisplayFn<F> {
DisplayFn(formatter)
}
#[derive(Clone, Copy)]
pub struct DisplayFn<F>(F);
impl<F> fmt::Display for DisplayFn<F>
where
F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.0)(formatter)
}
}
#[cfg(test)]
mod tests {
use core::fmt;
use super::*;
#[test]
fn formats_with_requested_capacity() {
let output = format_with_capacity(32, format_args!("hello {value}", value = 42));
assert_eq!(output, "hello 42");
assert!(output.capacity() >= 32);
}
#[test]
fn formats_into_reused_string() {
let mut output = String::with_capacity(64);
output.push_str("old contents");
let allocation = output.as_ptr();
let formatted =
try_format_into(&mut output, format_args!("new {value}", value = "contents")).unwrap();
assert_eq!(formatted, "new contents");
assert_eq!(output.as_ptr(), allocation);
}
#[test]
fn formatting_into_reused_string_propagates_display_errors() {
struct Fails;
impl fmt::Display for Fails {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
Err(fmt::Error)
}
}
let mut output = String::from("old contents");
let value = Fails;
let error = try_format_into(&mut output, format_args!("prefix {value}")).unwrap_err();
assert_eq!(error, fmt::Error);
assert_eq!(output, "prefix ");
}
#[test]
fn display_fn_defers_display_formatting() {
let value =
display_fn(|formatter: &mut fmt::Formatter<'_>| formatter.write_str("deferred"));
assert_eq!(value.to_string(), "deferred");
}
}