git_prole/
format_bulleted_list.rs

1use std::fmt::Display;
2
3use itertools::Itertools;
4
5/// Format an iterator of items into a bulleted list with line breaks between elements.
6pub fn format_bulleted_list(items: impl IntoIterator<Item = impl Display>) -> String {
7    let mut items = items.into_iter().peekable();
8    if items.peek().is_none() {
9        String::new()
10    } else {
11        // This kind of sucks.
12        format!("• {}", items.join("\n• "))
13    }
14}
15
16/// Like [`format_bulleted_list`], except the second and subsequent lines of multi-line items are
17/// indented as well.
18pub fn format_bulleted_list_multiline(items: impl IntoIterator<Item = impl Display>) -> String {
19    format_bulleted_list(items.into_iter().map(|item| {
20        let item = item.to_string();
21        let mut lines = item.lines().peekable();
22        match lines.next() {
23            None => {
24                // ???
25                String::new()
26            }
27            Some(first) => {
28                if lines.peek().is_none() {
29                    // One line.
30                    item
31                } else {
32                    // Two or more lines.
33                    format!("{first}\n  {}", lines.join("\n  "))
34                }
35            }
36        }
37    }))
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use indoc::indoc;
44    use pretty_assertions::assert_eq;
45
46    #[test]
47    fn test_format_bulleted_list() {
48        assert_eq!(format_bulleted_list(Vec::<String>::new()), "");
49
50        assert_eq!(format_bulleted_list(["puppy"]), "• puppy");
51        assert_eq!(
52            format_bulleted_list(["puppy", "doggy"]),
53            indoc!(
54                "
55                • puppy
56                • doggy"
57            )
58        );
59    }
60
61    #[test]
62    fn test_format_bulleted_list_multiline() {
63        assert_eq!(format_bulleted_list_multiline(Vec::<String>::new()), "");
64
65        assert_eq!(format_bulleted_list_multiline(["puppy"]), "• puppy");
66        assert_eq!(
67            format_bulleted_list_multiline(["puppy", "doggy"]),
68            indoc!(
69                "
70                • puppy
71                • doggy"
72            )
73        );
74
75        assert_eq!(
76            format_bulleted_list_multiline([
77                "puppy\ndoggy",
78                "sammy\ngoldie",
79                &format_bulleted_list_multiline(["ears", "tail", "fetch!"])
80            ]),
81            indoc!(
82                "
83                • puppy
84                  doggy
85                • sammy
86                  goldie
87                • • ears
88                  • tail
89                  • fetch!"
90            )
91        );
92    }
93}