Skip to main content

display_more/
display_slice.rs

1// Copyright 2021 Datafuse Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16
17/// Implement `Display` for `&[T]` if T is `Display`.
18///
19/// It outputs at most `limit` elements, excluding those from the 5th to the second-to-last one:
20/// - `DisplaySlice{ slice: &[1,2,3,4,5,6], ...}` outputs: `"[1,2,3,4,...,6]"`.
21pub struct DisplaySlice<'a, T: fmt::Display> {
22    slice: &'a [T],
23    /// The maximum number of elements to display. by default, it is 5.
24    limit: Option<usize>,
25    /// The separator between elements. by default, it is ",".
26    separator: &'a str,
27    /// The left brace. by default, it is "[".
28    left_brace: &'a str,
29    /// The right brace. by default, it is "]".
30    right_brace: &'a str,
31    /// The ellipsis string. by default, it is "..".
32    ellipsis: &'a str,
33    /// The prefix for each element. by default, it is "".
34    elem_prefix: &'a str,
35    /// The suffix for each element. by default, it is "".
36    elem_suffix: &'a str,
37    /// Whether to show the total count when truncated. by default, it is false.
38    show_count: bool,
39}
40
41impl<'a, T: fmt::Display> DisplaySlice<'a, T> {
42    pub fn new(slice: &'a [T]) -> Self {
43        Self {
44            slice,
45            limit: None,
46            separator: ",",
47            left_brace: "[",
48            right_brace: "]",
49            ellipsis: "..",
50            elem_prefix: "",
51            elem_suffix: "",
52            show_count: false,
53        }
54    }
55
56    pub fn at_most(mut self, limit: Option<usize>) -> Self {
57        self.limit = limit;
58        self
59    }
60
61    pub fn sep(mut self, separator: &'a str) -> Self {
62        self.separator = separator;
63        self
64    }
65
66    pub fn braces(mut self, left: &'a str, right: &'a str) -> Self {
67        self.left_brace = left;
68        self.right_brace = right;
69        self
70    }
71
72    pub fn ellipsis(mut self, s: &'a str) -> Self {
73        self.ellipsis = s;
74        self
75    }
76
77    pub fn elem(mut self, prefix: &'a str, suffix: &'a str) -> Self {
78        self.elem_prefix = prefix;
79        self.elem_suffix = suffix;
80        self
81    }
82
83    pub fn show_count(mut self) -> Self {
84        self.show_count = true;
85        self
86    }
87
88    pub fn limit(&self) -> usize {
89        self.limit.unwrap_or(5)
90    }
91}
92
93impl<T: fmt::Display> fmt::Display for DisplaySlice<'_, T> {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        let limit = self.limit();
96        let slice = self.slice;
97        let len = slice.len();
98        let truncated = len > limit;
99
100        let ell;
101        let ellipsis = if self.show_count && truncated {
102            ell = format!("{}({len} total)", self.ellipsis);
103            &ell
104        } else {
105            self.ellipsis
106        };
107
108        if limit == 0 {
109            return write!(f, "{}{ellipsis}{}", self.left_brace, self.right_brace);
110        }
111
112        write!(f, "{}", self.left_brace)?;
113
114        let (pre, suf, sep) = (self.elem_prefix, self.elem_suffix, self.separator);
115
116        if truncated {
117            for t in slice[..(limit - 1)].iter() {
118                write!(f, "{pre}{t}{suf}{sep}")?;
119            }
120
121            write!(f, "{ellipsis}{sep}")?;
122            write!(f, "{pre}{}{suf}", slice.last().unwrap())?;
123        } else {
124            for (i, t) in slice.iter().enumerate() {
125                if i > 0 {
126                    write!(f, "{sep}")?;
127                }
128
129                write!(f, "{pre}{t}{suf}")?;
130            }
131        }
132
133        write!(f, "{}", self.right_brace)
134    }
135}
136
137/// Implement `Display` for `&[T]` if T is `Display`.
138///
139/// It outputs at most `MAX` elements, excluding those from the 5th to the second-to-last one:
140/// - `DisplaySlice(&[1,2,3,4,5,6])` outputs: `"[1,2,3,4,...,6]"`.
141///
142/// # Example
143///
144/// ```rust
145/// use display_more::DisplaySliceExt;
146///
147/// let a = vec![1, 2, 3, 4, 5, 6];
148/// assert_eq!(a.display().to_string(), "[1,2,3,4,..,6]");
149/// ```
150pub trait DisplaySliceExt<'a, T: fmt::Display> {
151    fn display(&'a self) -> DisplaySlice<'a, T>;
152
153    /// Display at most `n` elements.
154    fn display_n(&'a self, n: usize) -> DisplaySlice<'a, T> {
155        self.display().at_most(Some(n))
156    }
157}
158
159impl<T> DisplaySliceExt<'_, T> for [T]
160where T: fmt::Display
161{
162    fn display(&self) -> DisplaySlice<T> {
163        DisplaySlice::new(self)
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::DisplaySlice;
170    use crate::DisplaySliceExt;
171
172    #[test]
173    fn test_display_slice() {
174        let a = vec![1, 2, 3, 4];
175        assert_eq!("[1,2,3,4]", DisplaySlice::new(&a).to_string());
176
177        let a = vec![1, 2, 3, 4, 5];
178        assert_eq!("[1,2,3,4,5]", DisplaySlice::new(&a).to_string());
179
180        let a = vec![1, 2, 3, 4, 5, 6];
181        assert_eq!("[1,2,3,4,..,6]", DisplaySlice::new(&a).to_string());
182
183        let a = vec![1, 2, 3, 4, 5, 6, 7];
184        assert_eq!("[1,2,3,4,..,7]", DisplaySlice::new(&a).to_string());
185
186        // with limit
187
188        let a = vec![1, 2, 3, 4, 5, 6, 7];
189        assert_eq!(
190            "[1,..,7]",
191            DisplaySlice::new(&a).at_most(Some(2)).to_string()
192        );
193
194        assert_eq!("[1,..,7]", a.display().at_most(Some(2)).to_string());
195
196        assert_eq!("[1,..,7]", a.display_n(2).to_string());
197
198        assert_eq!("[..,7]", a.display_n(1).to_string());
199
200        assert_eq!("[..]", a.display_n(0).to_string());
201    }
202
203    #[test]
204    fn test_display_slice_separator() {
205        let a = vec![1, 2, 3];
206        assert_eq!("[1, 2, 3]", a.display().sep(", ").to_string());
207
208        let a = vec![1, 2, 3, 4, 5, 6];
209        assert_eq!("[1, 2, 3, 4, .., 6]", a.display().sep(", ").to_string());
210
211        assert_eq!("[1|..|6]", a.display_n(2).sep("|").to_string());
212
213        assert_eq!("[1 2 3 4 .. 6]", a.display().sep(" ").to_string());
214
215        assert_eq!("[1234..6]", a.display().sep("").to_string());
216        assert_eq!("[1..6]", a.display_n(2).sep("").to_string());
217
218        // limit=1 with custom separator
219        assert_eq!("[.. 6]", a.display_n(1).sep(" ").to_string());
220
221        // limit=0 is unaffected by separator
222        assert_eq!("[..]", a.display_n(0).sep(" ").to_string());
223    }
224
225    #[test]
226    fn test_display_slice_ellipsis() {
227        let a = vec![1, 2, 3, 4, 5, 6];
228
229        // Custom ellipsis "..."
230        assert_eq!("[1,2,3,4,...,6]", a.display().ellipsis("...").to_string());
231
232        // Unicode ellipsis
233        assert_eq!(
234            "[1,2,3,4,\u{2026},6]",
235            a.display().ellipsis("\u{2026}").to_string()
236        );
237
238        // Empty ellipsis
239        assert_eq!("[1,2,3,4,,6]", a.display().ellipsis("").to_string());
240
241        // limit=0 with custom ellipsis
242        assert_eq!("[...]", a.display_n(0).ellipsis("...").to_string());
243
244        // Combined with custom separator
245        assert_eq!(
246            "[1, 2, 3, 4, ..., 6]",
247            a.display().ellipsis("...").sep(", ").to_string()
248        );
249    }
250
251    #[test]
252    fn test_display_slice_elem() {
253        let a = vec![1, 2, 3];
254
255        // Quotes
256        assert_eq!("['1','2','3']", a.display().elem("'", "'").to_string());
257
258        // Quotes with truncation
259        let b = vec![1, 2, 3, 4, 5, 6];
260        assert_eq!(
261            "['1','2','3','4',..,'6']",
262            b.display().elem("'", "'").to_string()
263        );
264
265        // Angle brackets
266        assert_eq!("[<1>,<2>,<3>]", a.display().elem("<", ">").to_string());
267
268        // Combined with custom separator
269        assert_eq!(
270            "['1', '2', '3']",
271            a.display().elem("'", "'").sep(", ").to_string()
272        );
273
274        // Empty prefix/suffix (default behavior)
275        assert_eq!("[1,2,3]", a.display().elem("", "").to_string());
276    }
277
278    #[test]
279    fn test_display_slice_show_count() {
280        let a = vec![1, 2, 3, 4, 5, 6, 7];
281
282        // Basic
283        assert_eq!(
284            "[1,2,3,4,..(7 total),7]",
285            a.display().show_count().to_string()
286        );
287
288        // limit=0
289        assert_eq!("[..(7 total)]", a.display_n(0).show_count().to_string());
290
291        // limit=1
292        assert_eq!("[..(7 total),7]", a.display_n(1).show_count().to_string());
293
294        // No truncation (len <= limit): count not shown
295        let c = vec![1, 2, 3];
296        assert_eq!("[1,2,3]", c.display().show_count().to_string());
297
298        // Combined with custom ellipsis
299        assert_eq!(
300            "[1,2,3,4,...(7 total),7]",
301            a.display().ellipsis("...").show_count().to_string()
302        );
303
304        // Combined with all features
305        assert_eq!(
306            "{'1', '2', '3', '4', ...(7 total), '7'}",
307            a.display()
308                .ellipsis("...")
309                .show_count()
310                .elem("'", "'")
311                .sep(", ")
312                .braces("{", "}")
313                .to_string()
314        );
315    }
316
317    #[test]
318    fn test_display_slice_braces() {
319        let a = vec![1, 2, 3];
320
321        // Custom braces, no truncation
322        assert_eq!("{1,2,3}", a.display().braces("{", "}").to_string());
323
324        // Custom braces with truncation
325        let b = vec![1, 2, 3, 4, 5, 6];
326        assert_eq!("{1,2,3,4,..,6}", b.display().braces("{", "}").to_string());
327
328        // Custom braces combined with custom separator
329        assert_eq!(
330            "{1, 2, 3, 4, .., 6}",
331            b.display().braces("{", "}").sep(", ").to_string()
332        );
333
334        // Custom braces with limit=0
335        assert_eq!("{..}", b.display_n(0).braces("{", "}").to_string());
336
337        // Parentheses
338        assert_eq!("(1,2,3)", a.display().braces("(", ")").to_string());
339
340        // Angle brackets
341        assert_eq!("<1,2,3>", a.display().braces("<", ">").to_string());
342
343        // Empty braces
344        assert_eq!("1,2,3", a.display().braces("", "").to_string());
345    }
346}