#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LengthSelector {
position: usize,
}
impl LengthSelector {
pub const VALID_LENGTHS: [usize; 9] = [2, 3, 4, 5, 6, 8, 10, 12, 16];
const MAX_POSITION: usize = Self::VALID_LENGTHS.len() - 1;
#[must_use]
pub fn new() -> Self {
Self {
position: Self::MAX_POSITION,
}
}
pub fn set_position(&mut self, pos: usize) {
self.position = pos.min(Self::MAX_POSITION);
}
pub fn set_length(&mut self, len: usize) {
let mut best_idx = 0;
let mut best_diff = Self::VALID_LENGTHS[0].abs_diff(len);
for (idx, &valid) in Self::VALID_LENGTHS.iter().enumerate().skip(1) {
let diff = valid.abs_diff(len);
if diff < best_diff {
best_diff = diff;
best_idx = idx;
}
}
self.position = best_idx;
}
pub fn increment(&mut self) {
self.position = (self.position + 1).min(Self::MAX_POSITION);
}
pub fn decrement(&mut self) {
self.position = self.position.saturating_sub(1);
}
#[must_use]
pub fn length(&self) -> usize {
Self::VALID_LENGTHS[self.position]
}
#[must_use]
pub fn length_nonzero(&self) -> std::num::NonZeroUsize {
std::num::NonZeroUsize::new(Self::VALID_LENGTHS[self.position])
.expect("all valid lengths are >= 2")
}
#[must_use]
pub fn position(&self) -> usize {
self.position
}
}
impl Default for LengthSelector {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn default_is_16() {
let sel = LengthSelector::new();
assert_eq!(sel.length(), 16);
}
#[test]
fn set_position_clamps() {
let mut sel = LengthSelector::new();
sel.set_position(99);
assert_eq!(sel.position(), 8);
}
#[test]
fn set_length_snaps() {
let mut sel = LengthSelector::new();
sel.set_length(7);
assert_eq!(sel.length(), 6);
sel.set_length(1);
assert_eq!(sel.length(), 2);
}
#[test]
fn increment_saturates() {
let mut sel = LengthSelector::new();
assert_eq!(sel.position(), 8);
sel.increment();
assert_eq!(sel.position(), 8);
}
#[test]
fn decrement_saturates() {
let mut sel = LengthSelector::new();
sel.set_position(0);
sel.decrement();
assert_eq!(sel.position(), 0);
}
#[test]
fn round_trip() {
let mut sel = LengthSelector::new();
for &len in &LengthSelector::VALID_LENGTHS {
sel.set_length(len);
assert_eq!(sel.length(), len, "round-trip failed for length {len}");
}
}
}