1use core::fmt::{self, Write};
9
10#[must_use]
12pub const fn format_buffer_capacity() -> usize {
13 crate::config::BUFFER_SIZE
14}
15
16const CAPACITY: usize = format_buffer_capacity();
17
18pub struct FormatBuffer {
25 inner: heapless::String<CAPACITY>,
26 truncated: bool,
27}
28
29impl FormatBuffer {
30 #[must_use]
32 pub fn new() -> Self {
33 Self {
34 inner: heapless::String::new(),
35 truncated: false,
36 }
37 }
38
39 #[must_use]
41 pub fn as_str(&self) -> &str {
42 self.inner.as_str()
43 }
44
45 #[must_use]
47 pub fn truncated(&self) -> bool {
48 self.truncated
49 }
50}
51
52impl Default for FormatBuffer {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl Write for FormatBuffer {
59 fn write_str(&mut self, s: &str) -> fmt::Result {
60 if self.truncated {
61 return Ok(());
62 }
63 match self.inner.push_str(s) {
64 Ok(()) => Ok(()),
65 Err(()) => {
66 self.truncated = true;
67 let target_len = CAPACITY.saturating_sub(3);
71 let bytes = self.inner.as_bytes();
72 let mut trunc_at = bytes.len().min(target_len);
73 while trunc_at > 0 && (bytes[trunc_at - 1] & 0b1100_0000) == 0b1000_0000 {
74 trunc_at -= 1;
75 }
76 self.inner.truncate(trunc_at);
77 let _ = self.inner.push('\u{2026}');
78 Ok(())
79 }
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use core::fmt::Write;
88
89 #[test]
90 fn small_payload_fits() {
91 let mut b = FormatBuffer::new();
92 write!(b, "hello {}", 42).unwrap();
93 assert_eq!(b.as_str(), "hello 42");
94 assert!(!b.truncated());
95 }
96
97 #[test]
98 fn overflow_truncates_and_appends_ellipsis() {
99 let mut b = FormatBuffer::new();
100 let pad = "x".repeat(CAPACITY * 2);
101 write!(b, "{}", pad).unwrap();
102 assert!(b.truncated());
103 assert!(b.as_str().ends_with('\u{2026}'));
104 assert!(b.as_str().len() <= CAPACITY);
105 }
106
107 #[test]
108 fn capacity_matches_feature_default() {
109 assert_eq!(format_buffer_capacity(), 256);
111 }
112}