Skip to main content

code_moniker_query/
bounded.rs

1use std::fmt::{self, Debug, Display, Write as _};
2
3/// A debug representation that stops formatting after `max_chars` Unicode
4/// characters instead of materializing the complete value before truncation.
5pub 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}