use std::fmt; use std::iter::Iterator; use std::slice::Iter as SliceIter; use std::str::Chars; const MAX_DATA_LEN: usize = 2000; pub trait Strippable { type Item: fmt::Display; type TypeIter: Iterator; fn length(&self) -> usize; fn iterator(&self) -> Self::TypeIter; } impl<'a> Strippable for &'a str { type Item = char; type TypeIter = Chars<'a>; fn length(&self) -> usize { self.len() } fn iterator(&self) -> Self::TypeIter { self.chars() } } impl<'a> Strippable for &'a Vec { type Item = &'a u8; type TypeIter = SliceIter<'a, u8>; fn length(&self) -> usize { self.len() } fn iterator(&self) -> Self::TypeIter { self.iter() } } pub struct Stripped<'inner, Inner: Strippable + 'inner>(pub &'inner Inner); impl<'inner, Inner: Strippable + 'inner> Stripped<'inner, Inner> { fn iter(&self) -> Inner::TypeIter { self.0.iterator() } fn placeholder(&self) -> &'static str { if self.0.length() >= MAX_DATA_LEN { " <...>" } else { "" } } } impl<'inner, Inner: Strippable + 'inner> fmt::Display for Stripped<'inner, Inner> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let placeholder = self.placeholder(); for c in self.iter().take(MAX_DATA_LEN - placeholder.len()) { write!(f, "{}", c)?; } write!(f, "{}", placeholder) } } #[cfg(test)] mod tests { use super::*; use rstest::*; #[rstest] #[case("abc", 3)] #[case("abcde".repeat(50), MAX_DATA_LEN)] fn test_strip(#[case] input: impl Into, #[case] result_len: usize) { let s = input.into(); assert_eq!(Stripped(&s.as_str()).to_string().len(), result_len); } }