#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ZoomRange {
pub from_index: usize,
pub to_index: usize,
}
impl ZoomRange {
#[must_use]
pub const fn len(self) -> usize {
self.to_index - self.from_index + 1
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.from_index > self.to_index
}
#[must_use]
pub const fn offset_to_original(self, display_index: usize) -> usize {
self.from_index + display_index
}
}
pub const MIN_ZOOM_SPAN: usize = 2;
#[must_use]
pub fn commit_drag(a: usize, b: usize, n_points: usize) -> Option<ZoomRange> {
if n_points == 0 {
return None;
}
let last = n_points - 1;
let lo = a.min(b).min(last);
let hi = a.max(b).min(last);
if hi - lo + 1 < MIN_ZOOM_SPAN {
return None;
}
Some(ZoomRange {
from_index: lo,
to_index: hi,
})
}
#[cfg(test)]
mod tests {
#![allow(
clippy::expect_used,
reason = "panicking helpers are fine in unit tests; failure is what we want"
)]
use super::*;
#[test]
fn commit_drag_oriented_lr() {
let z = commit_drag(2, 5, 10).expect("ok");
assert_eq!(z.from_index, 2);
assert_eq!(z.to_index, 5);
assert_eq!(z.len(), 4);
}
#[test]
fn commit_drag_oriented_rl_normalizes() {
let z = commit_drag(5, 2, 10).expect("ok");
assert_eq!(z.from_index, 2);
assert_eq!(z.to_index, 5);
}
#[test]
fn commit_drag_clamps_to_last_index() {
let z = commit_drag(0, 999, 10).expect("ok");
assert_eq!(z.from_index, 0);
assert_eq!(z.to_index, 9);
}
#[test]
fn commit_drag_rejects_tiny_spans() {
assert!(commit_drag(4, 4, 10).is_none());
}
#[test]
fn commit_drag_accepts_min_span() {
let z = commit_drag(3, 4, 10).expect("min span ok");
assert_eq!(z.len(), 2);
}
#[test]
fn commit_drag_handles_empty_input() {
assert!(commit_drag(0, 0, 0).is_none());
}
#[test]
fn offset_to_original_shifts_by_from_index() {
let z = ZoomRange {
from_index: 7,
to_index: 12,
};
assert_eq!(z.offset_to_original(0), 7);
assert_eq!(z.offset_to_original(5), 12);
}
}