Skip to main content

helpers/
truncated_display.rs

1//! Auto-truncate strings for display
2
3use std::fmt::{self, Debug, Display};
4
5/// Auto-truncate objects at a given length for displaying
6pub trait TruncatedDisplay {
7    /// Display `self` with at most `max_length` `char`s, including a trailing ‘…’ in case of
8    /// truncation.
9    ///
10    /// The returned object implements both `Debug` and `Display`, depending on how you want to
11    /// display.
12    fn truncated_display(&self, max_length: usize) -> impl Debug + Display;
13}
14
15impl TruncatedDisplay for &str {
16    fn truncated_display(&self, max_length: usize) -> impl Debug + Display {
17        TruncatedDisplayStr {
18            string: self,
19            max_length,
20        }
21    }
22}
23
24impl TruncatedDisplay for String {
25    fn truncated_display(&self, max_length: usize) -> impl Debug + Display {
26        TruncatedDisplayStr {
27            string: self,
28            max_length,
29        }
30    }
31}
32
33/// Implement `Debug` and `Display` for truncating strings after a given maximum length
34struct TruncatedDisplayStr<'a> {
35    /// The string to display
36    string: &'a str,
37    /// The maximum number of `char`s to display (including trailing … in case of truncate)
38    max_length: usize,
39}
40
41impl Debug for TruncatedDisplayStr<'_> {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        if self.max_length == 0 {
44            return Ok(());
45        }
46
47        let mut indices = self.string.char_indices().skip(self.max_length - 1);
48        if let Some(cutoff) = indices.next()
49            && indices.next().is_some()
50        {
51            write!(f, "{:?}…", &self.string[..cutoff.0])
52        } else {
53            write!(f, "{:?}", self.string)
54        }
55    }
56}
57
58impl Display for TruncatedDisplayStr<'_> {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        if self.max_length == 0 {
61            return Ok(());
62        }
63
64        let mut indices = self.string.char_indices().skip(self.max_length - 1);
65        if let Some(cutoff) = indices.next()
66            && indices.next().is_some()
67        {
68            write!(f, "{}…", &self.string[..cutoff.0])
69        } else {
70            write!(f, "{}", self.string)
71        }
72    }
73}