taco_display_utils/
lib.rs

1//! This crate contains utility functions for displaying iterators and
2//! types in a nice and structured way.
3
4use std::fmt::Display;
5
6/// Size of a tab when displaying types
7pub const TAB_SIZE: usize = 4;
8
9/// Join iterators over string types using the given separator
10///
11/// This function can be used to join iterators over string types using the
12/// given separator. The separator can be any string, including an empty string.
13/// It will not be appended to the end of the result.
14///
15/// # Example
16///
17/// ```
18/// use taco_display_utils::join_iterator;
19///
20/// let list = vec!["a", "b", "c"];
21/// assert_eq!(join_iterator(list.iter(), ", "), "a, b, c");
22/// ```
23pub fn join_iterator<T: ToString + Sized, U: Iterator<Item = T>, S: Into<String>>(
24    list: U,
25    sep: S,
26) -> String {
27    list.map(|x| x.to_string())
28        .collect::<Vec<_>>()
29        .join(sep.into().as_str())
30}
31
32/// Join iterators over string types using the given separator and add separator
33/// at the end
34///
35/// This function can be used to join iterators over string types using the
36/// given separator. The separator can be any string, including an empty string.
37/// It will also be appended to the end of the resulting string, except if the
38/// string is empty.
39///
40/// # Example
41///
42/// ```
43/// use taco_display_utils::join_iterator_and_add_back;
44///
45/// let list = vec!["a", "b", "c"];
46/// assert_eq!(join_iterator_and_add_back(list.iter(), ", "), "a, b, c, ");
47///
48/// let list: Vec<&str> = vec![];
49/// assert_eq!(join_iterator_and_add_back(list.iter(), ", "), "");
50/// ```
51pub fn join_iterator_and_add_back<T: ToString + Sized, U: Iterator<Item = T>, S: Into<String>>(
52    list: U,
53    sep: S,
54) -> String {
55    let sep = sep.into();
56
57    let res = join_iterator(list, &sep);
58
59    if res.is_empty() {
60        return res;
61    }
62
63    res + &sep
64}
65
66/// This function can be used to display an iterator in a stable order
67///
68/// # Example
69///
70/// ```
71/// use taco_display_utils::display_iterator_stable_order;
72///
73/// let list = vec!["c", "a", "b"];
74/// assert_eq!(display_iterator_stable_order(list.iter()), "a, b, c");
75/// ```
76pub fn display_iterator_stable_order<T: Display>(set: impl IntoIterator<Item = T>) -> String {
77    let mut sorted_set = set.into_iter().collect::<Vec<_>>();
78    sorted_set.sort_by_key(|a| a.to_string());
79    join_iterator(sorted_set.iter(), ", ")
80}
81
82/// This function can be used to indent all lines of a string by a tab size
83///
84/// # Example
85///
86/// ```
87/// use taco_display_utils::indent_all;
88///
89/// let input = "a\nb\nc";
90/// assert_eq!(indent_all(input), "    a\n    b\n    c");
91/// ```
92pub fn indent_all<S>(input: S) -> String
93where
94    S: Into<String>,
95{
96    let tab = " ".repeat(TAB_SIZE);
97    let input: String = input.into();
98    let input_n_lines = input.lines().count();
99
100    let mut output = String::with_capacity(input.len() + input_n_lines * TAB_SIZE);
101
102    for (i, line) in input.lines().enumerate() {
103        if !line.is_empty() {
104            output.push_str(&tab);
105        }
106
107        output.push_str(line);
108
109        if i != input_n_lines - 1 {
110            output.push('\n');
111        }
112    }
113
114    if input.ends_with('\n') {
115        output.push('\n');
116    }
117
118    output
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_join_iterator() {
127        let list = ["a", "b", "c"];
128        assert_eq!(join_iterator(list.iter(), ", "), "a, b, c");
129    }
130
131    #[test]
132    fn test_join_iterator_and_add_back() {
133        let list = ["a", "b", "c"];
134        assert_eq!(join_iterator_and_add_back(list.iter(), ", "), "a, b, c, ");
135
136        let list: Vec<&str> = vec![];
137        assert_eq!(join_iterator_and_add_back(list.iter(), ", "), "");
138    }
139
140    #[test]
141    fn test_display_iterator_stable_order() {
142        let list = ["c", "a", "b"];
143        assert_eq!(display_iterator_stable_order(list.iter()), "a, b, c");
144    }
145
146    #[test]
147    fn test_indent_all() {
148        let input = "a\nb\nc";
149        assert_eq!(indent_all(input), "    a\n    b\n    c");
150
151        let input = "a\nb\nc\n";
152        assert_eq!(indent_all(input), "    a\n    b\n    c\n");
153    }
154}