pub trait Step {
#[must_use]
fn next(&self) -> Self;
#[must_use]
fn back(&self) -> Self;
}
pub trait StepMut {
fn next_mut(&mut self);
fn back_mut(&mut self);
}
pub trait Rotate
where
Self: Clone + Step,
{
#[allow(clippy::comparison_chain)]
#[must_use]
fn rotate(&self, amount: i8) -> Self {
let mut next = self.clone();
if amount == 0 {
return next;
}
let forwards = amount > 0;
let mut amount = amount.abs();
while amount > 0 {
next = if forwards { next.next() } else { next.back() };
amount -= 1;
}
next
}
}
pub trait RotateMut
where
Self: StepMut,
{
#[allow(clippy::comparison_chain)]
fn rotate_mut(&mut self, amount: i8) {
if amount == 0 {
return;
}
let forwards = amount > 0;
let mut amount = amount.abs();
while amount > 0 {
if forwards {
self.next_mut();
} else {
self.back_mut();
};
amount -= 1;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone)]
struct FakeDie(i8);
impl Step for FakeDie {
fn next(&self) -> Self {
FakeDie(self.0 + 1)
}
fn back(&self) -> Self {
FakeDie(self.0 - 1)
}
}
impl StepMut for FakeDie {
fn next_mut(&mut self) {
self.0 += 1
}
fn back_mut(&mut self) {
self.0 -= 1
}
}
impl Rotate for FakeDie {}
impl RotateMut for FakeDie {}
#[test]
fn rotate_impl_none() {
let d = FakeDie(0);
let r = d.rotate(0);
assert_eq!(r.0, 0);
}
#[test]
fn rotate_impl_forwards() {
let d = FakeDie(0);
let r = d.rotate(2);
assert_eq!(r.0, 2);
}
#[test]
fn rotate_impl_backwards() {
let d = FakeDie(0);
let r = d.rotate(-2);
assert_eq!(r.0, -2);
}
#[test]
fn rotate_mut_impl_none() {
let mut d = FakeDie(0);
d.rotate_mut(0);
assert_eq!(d.0, 0);
}
#[test]
fn rotate_mut_impl_forwards() {
let mut d = FakeDie(0);
d.rotate_mut(2);
assert_eq!(d.0, 2);
}
#[test]
fn rotate_mut_impl_backwards() {
let mut d = FakeDie(0);
d.rotate_mut(-2);
assert_eq!(d.0, -2);
}
}