code_moniker_query/
bounded.rs1use std::fmt::{self, Debug, Display, Write as _};
2
3pub struct BoundedDebug<'a, T: ?Sized> {
6 value: &'a T,
7 max_chars: usize,
8}
9
10pub fn bounded_debug<T: Debug + ?Sized>(value: &T, max_chars: usize) -> BoundedDebug<'_, T> {
11 BoundedDebug { value, max_chars }
12}
13
14impl<T: Debug + ?Sized> Display for BoundedDebug<'_, T> {
15 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
16 let (result, truncated) = {
17 let mut writer = BoundedWriter {
18 inner: formatter,
19 remaining: self.max_chars,
20 truncated: false,
21 };
22 let result = write!(&mut writer, "{:?}", self.value);
23 (result, writer.truncated)
24 };
25 if truncated {
26 formatter.write_str("…")
27 } else {
28 result
29 }
30 }
31}
32
33struct BoundedWriter<'a, 'b> {
34 inner: &'a mut fmt::Formatter<'b>,
35 remaining: usize,
36 truncated: bool,
37}
38
39impl fmt::Write for BoundedWriter<'_, '_> {
40 fn write_str(&mut self, value: &str) -> fmt::Result {
41 let Some((boundary, count)) = char_boundary(value, self.remaining) else {
42 self.remaining -= value.chars().count();
43 return self.inner.write_str(value);
44 };
45 self.inner.write_str(&value[..boundary])?;
46 self.remaining -= count;
47 self.truncated = true;
48 Err(fmt::Error)
49 }
50}
51
52fn char_boundary(value: &str, max_chars: usize) -> Option<(usize, usize)> {
53 value
54 .char_indices()
55 .nth(max_chars)
56 .map(|(boundary, _)| (boundary, max_chars))
57}
58
59#[cfg(test)]
60mod tests {
61 use super::bounded_debug;
62
63 #[test]
64 fn bounded_debug_stops_on_unicode_character_boundary() {
65 assert_eq!(bounded_debug(&"abécd", 4).to_string(), "\"abé…");
66 assert_eq!(bounded_debug(&"ab", 8).to_string(), "\"ab\"");
67 }
68}