1use std::fmt;
16
17use crate::display_iterator_options::DisplayIteratorOptions;
18
19pub struct DisplayIntoIter<'a, T, S>
23where
24 T: fmt::Display + 'a,
25 S: Clone + IntoIterator<Item = &'a T>,
26 S::IntoIter: DoubleEndedIterator + ExactSizeIterator,
27{
28 items: S,
29 options: DisplayIteratorOptions<'a>,
30}
31
32impl<'a, T, S> DisplayIntoIter<'a, T, S>
33where
34 T: fmt::Display + 'a,
35 S: Clone + IntoIterator<Item = &'a T>,
36 S::IntoIter: DoubleEndedIterator + ExactSizeIterator,
37{
38 pub fn new(items: S) -> Self {
39 Self {
40 items,
41 options: DisplayIteratorOptions::default(),
42 }
43 }
44
45 pub fn at_most(mut self, limit: Option<usize>) -> Self {
46 self.options.limit = limit;
47 self
48 }
49
50 pub fn sep(mut self, separator: &'a str) -> Self {
51 self.options.separator = separator;
52 self
53 }
54
55 pub fn braces(mut self, left: &'a str, right: &'a str) -> Self {
56 self.options.left_brace = left;
57 self.options.right_brace = right;
58 self
59 }
60
61 pub fn ellipsis(mut self, s: &'a str) -> Self {
62 self.options.ellipsis = s;
63 self
64 }
65
66 pub fn elem(mut self, prefix: &'a str, suffix: &'a str) -> Self {
67 self.options.elem_prefix = prefix;
68 self.options.elem_suffix = suffix;
69 self
70 }
71
72 pub fn show_count(mut self) -> Self {
73 self.options.show_count = true;
74 self
75 }
76
77 pub fn limit(&self) -> usize {
78 self.options.limit()
79 }
80}
81
82impl<'a, T, S> fmt::Display for DisplayIntoIter<'a, T, S>
83where
84 T: fmt::Display + 'a,
85 S: Clone + IntoIterator<Item = &'a T>,
86 S::IntoIter: DoubleEndedIterator + ExactSizeIterator,
87{
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 let limit = self.limit();
90 let len = self.items.clone().into_iter().len();
91 let truncated = len > limit;
92 let options = &self.options;
93
94 let ell;
95 let ellipsis = if options.show_count && truncated {
96 ell = format!("{}({len} total)", options.ellipsis);
97 &ell
98 } else {
99 options.ellipsis
100 };
101
102 if limit == 0 {
103 return write!(f, "{}{ellipsis}{}", options.left_brace, options.right_brace);
104 }
105
106 write!(f, "{}", options.left_brace)?;
107
108 let (pre, suf, sep) = (options.elem_prefix, options.elem_suffix, options.separator);
109
110 if truncated {
111 let mut iter = self.items.clone().into_iter();
112
113 for _ in 0..(limit - 1) {
114 let item = iter.next().unwrap();
115 write!(f, "{pre}{item}{suf}{sep}")?;
116 }
117
118 write!(f, "{ellipsis}{sep}")?;
119 write!(
120 f,
121 "{pre}{}{suf}",
122 self.items.clone().into_iter().next_back().unwrap()
123 )?;
124 } else {
125 for (i, item) in self.items.clone().into_iter().enumerate() {
126 if i > 0 {
127 write!(f, "{sep}")?;
128 }
129
130 write!(f, "{pre}{item}{suf}")?;
131 }
132 }
133
134 write!(f, "{}", options.right_brace)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use std::collections::BTreeSet;
141
142 use super::DisplayIntoIter;
143
144 #[derive(Clone)]
145 struct Items<'a, T>(&'a [T]);
146
147 impl<'a, T> IntoIterator for Items<'a, T> {
148 type Item = &'a T;
149 type IntoIter = std::slice::Iter<'a, T>;
150
151 fn into_iter(self) -> Self::IntoIter {
152 self.0.iter()
153 }
154 }
155
156 #[test]
157 fn test_display_into_iter_slice() {
158 let values = [1, 2, 3, 4, 5, 6];
159 assert_eq!(
160 "[1,2,3,4,..,6]",
161 DisplayIntoIter::new(values.iter()).to_string()
162 );
163 }
164
165 #[test]
166 fn test_display_into_iter_btreeset() {
167 let values = (1..=6).collect::<BTreeSet<_>>();
168 assert_eq!("[1,2,3,4,..,6]", DisplayIntoIter::new(&values).to_string());
169 }
170
171 #[test]
172 fn test_display_into_iter_custom_container() {
173 let values = [1, 2, 3, 4, 5, 6];
174 assert_eq!(
175 "[1,2,..,6]",
176 DisplayIntoIter::new(Items(&values))
177 .at_most(Some(3))
178 .to_string()
179 );
180 }
181
182 #[test]
183 fn test_display_into_iter_limit_edges() {
184 let values = [1, 2, 3, 4, 5, 6];
185
186 assert_eq!(
187 "[..]",
188 DisplayIntoIter::new(values.iter())
189 .at_most(Some(0))
190 .to_string()
191 );
192 assert_eq!(
193 "[..,6]",
194 DisplayIntoIter::new(values.iter())
195 .at_most(Some(1))
196 .to_string()
197 );
198 }
199
200 #[test]
201 fn test_display_into_iter_combined_formatting() {
202 let values = [1, 2, 3, 4, 5, 6, 7];
203
204 assert_eq!(
205 "{'1' | '2' | '3' | '4' | ...(7 total) | '7'}",
206 DisplayIntoIter::new(values.iter())
207 .ellipsis("...")
208 .show_count()
209 .elem("'", "'")
210 .sep(" | ")
211 .braces("{", "}")
212 .to_string()
213 );
214 }
215}