use std::fmt;
use crate::display_iterator_options::DisplayIteratorOptions;
pub struct DisplayIntoIter<'a, T, S>
where
T: fmt::Display + 'a,
S: Clone + IntoIterator<Item = &'a T>,
S::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
items: S,
options: DisplayIteratorOptions<'a>,
}
impl<'a, T, S> DisplayIntoIter<'a, T, S>
where
T: fmt::Display + 'a,
S: Clone + IntoIterator<Item = &'a T>,
S::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
pub fn new(items: S) -> Self {
Self {
items,
options: DisplayIteratorOptions::default(),
}
}
pub fn at_most(mut self, limit: Option<usize>) -> Self {
self.options.limit = limit;
self
}
pub fn sep(mut self, separator: &'a str) -> Self {
self.options.separator = separator;
self
}
pub fn braces(mut self, left: &'a str, right: &'a str) -> Self {
self.options.left_brace = left;
self.options.right_brace = right;
self
}
pub fn ellipsis(mut self, s: &'a str) -> Self {
self.options.ellipsis = s;
self
}
pub fn elem(mut self, prefix: &'a str, suffix: &'a str) -> Self {
self.options.elem_prefix = prefix;
self.options.elem_suffix = suffix;
self
}
pub fn show_count(mut self) -> Self {
self.options.show_count = true;
self
}
pub fn limit(&self) -> usize {
self.options.limit()
}
}
impl<'a, T, S> fmt::Display for DisplayIntoIter<'a, T, S>
where
T: fmt::Display + 'a,
S: Clone + IntoIterator<Item = &'a T>,
S::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let limit = self.limit();
let len = self.items.clone().into_iter().len();
let truncated = len > limit;
let options = &self.options;
let ell;
let ellipsis = if options.show_count && truncated {
ell = format!("{}({len} total)", options.ellipsis);
&ell
} else {
options.ellipsis
};
if limit == 0 {
return write!(f, "{}{ellipsis}{}", options.left_brace, options.right_brace);
}
write!(f, "{}", options.left_brace)?;
let (pre, suf, sep) = (options.elem_prefix, options.elem_suffix, options.separator);
if truncated {
let mut iter = self.items.clone().into_iter();
for _ in 0..(limit - 1) {
let item = iter.next().unwrap();
write!(f, "{pre}{item}{suf}{sep}")?;
}
write!(f, "{ellipsis}{sep}")?;
write!(
f,
"{pre}{}{suf}",
self.items.clone().into_iter().next_back().unwrap()
)?;
} else {
for (i, item) in self.items.clone().into_iter().enumerate() {
if i > 0 {
write!(f, "{sep}")?;
}
write!(f, "{pre}{item}{suf}")?;
}
}
write!(f, "{}", options.right_brace)
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use super::DisplayIntoIter;
#[derive(Clone)]
struct Items<'a, T>(&'a [T]);
impl<'a, T> IntoIterator for Items<'a, T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[test]
fn test_display_into_iter_slice() {
let values = [1, 2, 3, 4, 5, 6];
assert_eq!(
"[1,2,3,4,..,6]",
DisplayIntoIter::new(values.iter()).to_string()
);
}
#[test]
fn test_display_into_iter_btreeset() {
let values = (1..=6).collect::<BTreeSet<_>>();
assert_eq!("[1,2,3,4,..,6]", DisplayIntoIter::new(&values).to_string());
}
#[test]
fn test_display_into_iter_custom_container() {
let values = [1, 2, 3, 4, 5, 6];
assert_eq!(
"[1,2,..,6]",
DisplayIntoIter::new(Items(&values))
.at_most(Some(3))
.to_string()
);
}
#[test]
fn test_display_into_iter_limit_edges() {
let values = [1, 2, 3, 4, 5, 6];
assert_eq!(
"[..]",
DisplayIntoIter::new(values.iter())
.at_most(Some(0))
.to_string()
);
assert_eq!(
"[..,6]",
DisplayIntoIter::new(values.iter())
.at_most(Some(1))
.to_string()
);
}
#[test]
fn test_display_into_iter_combined_formatting() {
let values = [1, 2, 3, 4, 5, 6, 7];
assert_eq!(
"{'1' | '2' | '3' | '4' | ...(7 total) | '7'}",
DisplayIntoIter::new(values.iter())
.ellipsis("...")
.show_count()
.elem("'", "'")
.sep(" | ")
.braces("{", "}")
.to_string()
);
}
}