use core::ops::Range;
pub type ProgressEntry = Range<u64>;
pub trait Total {
fn total(&self) -> u64;
}
impl Total for ProgressEntry {
#[allow(clippy::inline_always)]
#[inline(always)]
fn total(&self) -> u64 {
self.end.saturating_sub(self.start)
}
}
impl Total for Vec<ProgressEntry> {
fn total(&self) -> u64 {
self.iter().map(Total::total).sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn progress_entry_total() {
assert_eq!((0..0).total(), 0);
assert_eq!((5..10).total(), 5);
assert_eq!((3..3).total(), 0);
let reversed = core::ops::Range { start: 10, end: 5 };
assert_eq!(reversed.total(), 0);
}
#[test]
fn vec_progress_total() {
#![allow(clippy::single_range_in_vec_init)]
let v: Vec<ProgressEntry> = vec![1..5, 8..10, 12..15];
assert_eq!(v.total(), 9);
let empty: Vec<ProgressEntry> = vec![];
assert_eq!(empty.total(), 0);
}
#[test]
fn progress_entry_total_reversed_ranges_are_safe() {
assert_eq!((core::ops::Range { start: 10, end: 5 }).total(), 0);
assert_eq!(
(core::ops::Range {
start: u64::MAX,
end: 0
})
.total(),
0
);
assert_eq!(
(core::ops::Range {
start: 100,
end: 99
})
.total(),
0
);
assert_eq!((core::ops::Range { start: 1, end: 0 }).total(), 0);
}
#[test]
fn progress_entry_total_extreme_and_degenerate() {
assert_eq!((0..u64::MAX).total(), u64::MAX);
assert_eq!((u64::MAX..u64::MAX).total(), 0);
assert_eq!((42..42).total(), 0);
assert_eq!((0..0).total(), 0);
}
#[test]
fn vec_progress_total_counts_overlap_twice() {
#![allow(clippy::single_range_in_vec_init)]
let v: Vec<ProgressEntry> = vec![0..5, 3..10];
assert_eq!(v.total(), 12);
}
#[test]
#[cfg_attr(debug_assertions, should_panic)]
#[allow(clippy::should_panic_without_expect)]
fn vec_progress_total_overflow_is_unchecked() {
let v: Vec<ProgressEntry> = vec![0..u64::MAX, 0..1];
let _ = v.total(); }
}