1use std::fmt;
4
5pub fn write_start_redacted(
9 f: &mut fmt::Formatter,
10 input: &str,
11 chars: usize,
12 ellipsis: &str,
13) -> fmt::Result {
14 if let Some((pos, _)) = input.char_indices().nth(chars) {
15 let slice = input
16 .get(..pos)
17 .expect("Mismatched character offset calculation");
18 write!(f, "{slice}{ellipsis}")
19 } else {
20 write!(f, "{input}")
21 }
22}
23
24pub fn write_end_redacted(
28 f: &mut fmt::Formatter,
29 input: &str,
30 chars: usize,
31 ellipsis: &str,
32) -> fmt::Result {
33 if chars == 0 {
34 if input.is_empty() {
35 Ok(())
36 } else {
37 write!(f, "{ellipsis}")
38 }
39 } else if let Some((pos, _)) = input.char_indices().nth_back(chars - 1)
40 && pos != 0
41 {
42 let slice = input
43 .get(pos..)
44 .expect("Mismatched character offset calculation");
45 write!(f, "{ellipsis}{slice}")
46 } else {
47 write!(f, "{input}")
48 }
49}
50
51#[cfg(test)]
52mod test {
53 #![allow(clippy::bool_assert_comparison)]
55 #![allow(clippy::clone_on_copy)]
56 #![allow(clippy::dbg_macro)]
57 #![allow(clippy::mixed_attributes_style)]
58 #![allow(clippy::print_stderr)]
59 #![allow(clippy::print_stdout)]
60 #![allow(clippy::single_char_pattern)]
61 #![allow(clippy::unwrap_used)]
62 #![allow(clippy::unchecked_time_subtraction)]
63 #![allow(clippy::useless_vec)]
64 #![allow(clippy::needless_pass_by_value)]
65 #![allow(clippy::string_slice)] use super::*;
69
70 struct Fmt<'a> {
71 string: &'a str,
72 n: usize,
73 start: bool,
74 }
75
76 impl<'a> fmt::Display for Fmt<'a> {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 if self.start {
79 write_start_redacted(f, self.string, self.n, "…")
80 } else {
81 write_end_redacted(f, self.string, self.n, "…")
82 }
83 }
84 }
85
86 fn rstart(string: &str, n: usize) -> String {
87 Fmt {
88 string,
89 n,
90 start: true,
91 }
92 .to_string()
93 }
94
95 fn rend(string: &str, n: usize) -> String {
96 Fmt {
97 string,
98 n,
99 start: false,
100 }
101 .to_string()
102 }
103
104 #[test]
105 fn test_redact_start() {
106 assert_eq!(&rstart("hello world", 2), "he…");
107 assert_eq!(&rstart("he", 2), "he");
108 assert_eq!(&rstart("h", 2), "h");
109 assert_eq!(&rstart("", 2), "");
110
111 assert_eq!(&rstart("", 0), "");
112 assert_eq!(&rstart("hello", 0), "…");
113
114 assert_eq!(&rstart("分久必合,合久必分", 2), "分久…");
115 assert_eq!(&rstart("分久必合,合久必分", 4), "分久必合…");
116 assert_eq!(&rstart("分久必合,合久必分", 9), "分久必合,合久必分");
117 assert_eq!(&rstart("分久必合,合久必分", 10), "分久必合,合久必分");
118 assert_eq!(&rstart("分久必合,合久必分", 0), "…");
119 }
120
121 #[test]
122 fn test_redact_end() {
123 assert_eq!(&rend("hello world", 2), "…ld");
124 assert_eq!(&rend("he", 2), "he");
125 assert_eq!(&rend("h", 2), "h");
126 assert_eq!(&rend("", 2), "");
127
128 assert_eq!(&rend("", 0), "");
129 assert_eq!(&rend("hello", 0), "…");
130
131 assert_eq!(&rend("分久必合,合久必分", 2), "…必分");
132 assert_eq!(&rend("分久必合,合久必分", 4), "…合久必分");
133 assert_eq!(&rend("分久必合,合久必分", 9), "分久必合,合久必分");
134 assert_eq!(&rend("分久必合,合久必分", 10), "分久必合,合久必分");
135 assert_eq!(&rend("分久必合,合久必分", 0), "…");
136 }
137}