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