use std::fmt; #[derive(Debug, Clone, Copy)] pub enum BarSegment<'s> { Dynamic(&'s str, u64), Static(&'s str), } impl<'s> BarSegment<'s> { fn char(&self) -> &'s str { match self { BarSegment::Dynamic(c, _) => c, BarSegment::Static(c) => c, } } fn length(&self) -> u64 { match self { BarSegment::Dynamic(c, len) => *len, BarSegment::Static(c) => c.len() as u64, } } fn is_static(&self) -> bool { matches!(self, BarSegment::Static(_)) } fn scaled(&self, new_len: u64) -> Self { match self { BarSegment::Dynamic(c, _) => BarSegment::Dynamic(c, new_len), BarSegment::Static(c) => BarSegment::Static(c), } } } #[derive(Debug)] pub struct MultiBar<'s, const N: usize>(pub [BarSegment<'s>; N]); impl MultiBar<'_, N> { fn dynamic_length(&self) -> u64 { self.0 .iter() .filter(|seg| !seg.is_static()) .map(|seg| seg.length()) .sum::() } fn static_length(&self) -> u64 { self.0 .iter() .filter(|seg| seg.is_static()) .map(|seg| seg.length()) .sum::() } /// Transforms the bar to be of target size /// Static parts remain the same length, dynamic parts are scaled proportionally pub(crate) fn scale(&self, size: u64) -> Self { let static_len = self.static_length(); let dynamic_len = std::cmp::max(self.dynamic_length(), 1); let available_size = size.saturating_sub(static_len); let mut prev_prop = 0; let mut curr_prop = 0; let inner = self.0.map(|seg| match seg { BarSegment::Static(_) => seg, BarSegment::Dynamic(c, len) => { curr_prop += len; let nb_chars = available_size * curr_prop / dynamic_len - available_size * prev_prop / dynamic_len; prev_prop = curr_prop; BarSegment::Dynamic(c, nb_chars) } }); Self(inner) } } impl fmt::Display for MultiBar<'_, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for seg in &self.0 { let (c, len) = match seg { BarSegment::Dynamic(c, len) => (c, *len), BarSegment::Static(c) => (c, c.len() as u64), }; for _ in 0..len { f.write_str(c)?; } } Ok(()) } }