-
Notifications
You must be signed in to change notification settings - Fork 391
Expand file tree
/
Copy pathlib.rs
More file actions
114 lines (101 loc) · 3.3 KB
/
lib.rs
File metadata and controls
114 lines (101 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use std::fmt::{self, Debug, Write};
/// Returns a possibly modified version of `s` that fits within the specified bounds (in terms of
/// the number of UTF-8 characters).
///
/// More precisely, middle characters are removed such that the return value has at most `max_len`
/// characters. Some examples:
/// ```
/// println!("{}", clamp_string_len("abcdef", 5)); // a...f
/// println!("{}", clamp_string_len("abcde", 5)); // abcde
/// println!("{}", clamp_string_len("abcd", 5)); // abcd
/// ```
///
/// This is analogous clamp method on numeric types in that this makes the value bounded.
pub fn clamp_string_len(s: &str, max_len: usize) -> String {
// Collect into a vector so that we can safely index the input.
let chars: Vec<_> = s.chars().collect();
if max_len <= 3 {
return chars.into_iter().take(max_len).collect();
}
if chars.len() <= max_len {
return s.to_string();
}
let ellipsis = "...";
let content_len = max_len - ellipsis.len();
let tail_len = content_len / 2;
let head_len = content_len - tail_len;
let tail_begin = chars.len() - tail_len;
format!(
"{}{}{}",
chars[..head_len].iter().collect::<String>(),
ellipsis,
chars[tail_begin..].iter().collect::<String>(),
)
}
/// Formats object, but limits the output size.
///
/// Unlike clamp_string_len, does not include the tail (when truncating).
pub fn clamp_debug_len(object: &impl Debug, max_len: usize) -> String {
let mut buf = LimitedWriter::new(max_len);
// write! returns Err if the writer returns Err, which LimitedWriter does
// once the limit is hit. We intentionally ignore this "error".
let _ignore_err = write!(buf, "{object:#?}");
if buf.truncated {
// Replace the last 3 chars with "..." to indicate truncation,
// or just append if the string is very short.
let s = &mut buf.buffer;
if s.len() >= 3 {
s.truncate(s.len() - 3);
s.push_str("...");
} else {
s.push_str("...");
}
}
buf.buffer
}
/// A `fmt::Write` implementation that stops accepting characters after a limit.
struct LimitedWriter {
buffer: String,
remaining: usize,
truncated: bool,
}
impl LimitedWriter {
fn new(max_len: usize) -> Self {
Self {
buffer: String::with_capacity(max_len.min(1024)),
remaining: max_len,
truncated: false,
}
}
}
impl fmt::Write for LimitedWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
if self.remaining == 0 {
self.truncated = true;
return Err(fmt::Error);
}
let chars_available = self.remaining;
let mut char_count = 0;
let byte_limit = s
.char_indices()
.take_while(|(_, _)| {
char_count += 1;
char_count <= chars_available
})
.last()
.map(|(i, c)| i + c.len_utf8())
.unwrap_or(0);
if byte_limit < s.len() {
self.buffer.push_str(&s[..byte_limit]);
self.remaining = 0;
self.truncated = true;
Err(fmt::Error)
} else {
self.buffer.push_str(s);
self.remaining -= char_count;
Ok(())
}
}
}
#[cfg(test)]
mod tests;