use super::*; pub struct List + Clone> { conjunction: &'static str, values: I, } impl + Clone> List { pub fn or>(values: II) -> Self { Self { conjunction: "or", values: values.into_iter(), } } pub fn and>(values: II) -> Self { Self { conjunction: "and", values: values.into_iter(), } } pub fn or_ticked>( values: II, ) -> List, impl Iterator> + Clone> { List::or(values.into_iter().map(Enclosure::tick)) } pub fn and_ticked>( values: II, ) -> List, impl Iterator> + Clone> { List::and(values.into_iter().map(Enclosure::tick)) } } impl + Clone> Display for List { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let mut values = self.values.clone().fuse(); if let Some(first) = values.next() { write!(f, "{first}")?; } else { return Ok(()); } let second = values.next(); if second.is_none() { return Ok(()); } let third = values.next(); if let (Some(second), None) = (second.as_ref(), third.as_ref()) { write!(f, " {} {second}", self.conjunction)?; return Ok(()); } let mut current = second; let mut next = third; loop { match (current, next) { (Some(c), Some(n)) => { write!(f, ", {c}")?; current = Some(n); next = values.next(); } (Some(c), None) => { write!(f, ", {} {c}", self.conjunction)?; return Ok(()); } _ => unreachable!("Iterator was fused, but returned Some after None"), } } } } #[cfg(test)] mod tests { use super::*; #[test] fn or() { assert_eq!("1", List::or(&[1]).to_string()); assert_eq!("1 or 2", List::or(&[1, 2]).to_string()); assert_eq!("1, 2, or 3", List::or(&[1, 2, 3]).to_string()); assert_eq!("1, 2, 3, or 4", List::or(&[1, 2, 3, 4]).to_string()); } #[test] fn and() { assert_eq!("1", List::and(&[1]).to_string()); assert_eq!("1 and 2", List::and(&[1, 2]).to_string()); assert_eq!("1, 2, and 3", List::and(&[1, 2, 3]).to_string()); assert_eq!("1, 2, 3, and 4", List::and(&[1, 2, 3, 4]).to_string()); } #[test] fn or_ticked() { assert_eq!("`1`", List::or_ticked(&[1]).to_string()); assert_eq!("`1` or `2`", List::or_ticked(&[1, 2]).to_string()); assert_eq!("`1`, `2`, or `3`", List::or_ticked(&[1, 2, 3]).to_string()); assert_eq!( "`1`, `2`, `3`, or `4`", List::or_ticked(&[1, 2, 3, 4]).to_string() ); } #[test] fn and_ticked() { assert_eq!("`1`", List::and_ticked(&[1]).to_string()); assert_eq!("`1` and `2`", List::and_ticked(&[1, 2]).to_string()); assert_eq!( "`1`, `2`, and `3`", List::and_ticked(&[1, 2, 3]).to_string() ); assert_eq!( "`1`, `2`, `3`, and `4`", List::and_ticked(&[1, 2, 3, 4]).to_string() ); } }