use std::ops::{Add, AddAssign, Sub, SubAssign};
use serde::{Deserialize, Serialize};
use paste::paste;
use crate::details::DetailedDisplay;
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, PartialOrd)]
pub struct Rank {
value: i32
}
impl Default for Rank {
fn default() -> Self {
Rank { value: 0 }
}
}
pub trait IsRanked {
fn rank(&self) -> Rank;
fn rank_mut(&mut self) -> &mut Rank {
panic!("Cannot mutate: mutator not implemented/applicable for this type!")
}
}
macro_rules! define_isranked_for_prim {
($bits:expr) => {paste!{
impl IsRanked for [<i $bits>]{ fn rank(&self) -> Rank { Rank {
value: (*self).try_into()
.expect(format!("Value '{:+}' is WAY too big for the teensy i32 to hold onto…!", self).as_str())}}}
impl IsRanked for &[<i $bits>]{ fn rank(&self) -> Rank { (**self).rank() }}
impl IsRanked for [<u $bits>]{ fn rank(&self) -> Rank { Rank {
value: (*self).try_into()
.expect({
log::error!("Value '{:+}' is WAY too big for the teensy i32 to hold onto…!", self);
format!("Value '{:+}' is WAY too big for the teensy i32 to hold onto…!", self)}.as_str())}}}
impl IsRanked for &[<u $bits>]{ fn rank(&self) -> Rank { (**self).rank() }}
}};
}
define_isranked_for_prim!(8);
define_isranked_for_prim!(16);
define_isranked_for_prim!(32);
define_isranked_for_prim!(64);
define_isranked_for_prim!(128);
define_isranked_for_prim!(size);
impl Rank {
fn new(value: i32) -> Self {
Self { value }
}
fn explain(&self) -> &'static str {
match &self.value {
..=0 => "Rank 0",
1 => "Rank 1",
2 => "Rank 2",
3 => "Rank 3",
4 => "Rank 4",
5 => "Rank 5",
6 => "Rank 6",
7 => "Rank 7",
8 => "Rank 8",
9 => "Rank 9",
10 => "Rank 10",
_ => "Rank 11+",
}
}
pub const AVERAGE: Rank = Rank { value: 3 };
pub const NONE: Rank = Rank { value: 0 };
}
impl std::fmt::Display for Rank {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
impl DetailedDisplay for Rank {
fn detailed_display(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.explain())
}
}
impl Add<i32> for Rank {
type Output = Self;
fn add(self, rhs: i32) -> Self::Output {
Self { value: self.value + rhs }
}
}
impl AddAssign<i32> for Rank {
fn add_assign(&mut self, rhs: i32) {
self.value += rhs
}
}
impl Sub<i32> for Rank {
type Output = Self;
fn sub(self, rhs: i32) -> Self::Output {
Self { value: self.value - rhs }
}
}
impl SubAssign<i32> for Rank {
fn sub_assign(&mut self, rhs: i32) {
self.value -= rhs
}
}
impl PartialEq<i32> for Rank {
fn eq(&self, other: &i32) -> bool {
self.value == *other
}
}
impl PartialEq<Rank> for i32 {
fn eq(&self, other: &Rank) -> bool {
*self == other.value
}
}
impl PartialOrd<i32> for Rank {
fn partial_cmp(&self, other: &i32) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(other)
}
}
impl PartialOrd<Rank> for i32 {
fn partial_cmp(&self, other: &Rank) -> Option<std::cmp::Ordering> {
self.partial_cmp(&other.value)
}
}
macro_rules! impl_from_primitive_for_rank {
($prim:expr) => {paste!{
impl From<[<i $prim>]> for Rank { fn from(value: [<i $prim>]) -> Self { Self::new( value.try_into().unwrap() )}}
impl From<&[<i $prim>]> for Rank { fn from(value: &[<i $prim>]) -> Self { Self::new( (*value).try_into().unwrap() )}}
impl From<[<u $prim>]> for Rank { fn from(value: [<u $prim>]) -> Self { Self::new( value.try_into().unwrap() )}}
impl From<&[<u $prim>]> for Rank { fn from(value: &[<u $prim>]) -> Self { Self::new( (*value).try_into().unwrap() )}}
}};
}
impl_from_primitive_for_rank!(8);
impl_from_primitive_for_rank!(16);
impl_from_primitive_for_rank!(32);
impl_from_primitive_for_rank!(64);
impl_from_primitive_for_rank!(128);
impl_from_primitive_for_rank!(size);
#[cfg(test)]
mod rank_tests {
use super::*;
#[test]
fn add_i32_to_rank() {
let r = Rank::new(0);
let r = r + 5;
assert_eq!(5, r);
}
#[test]
fn sub_i32_from_rank() {
let r = Rank::new(0);
let r = r - 5;
assert_eq!(r, -5);
}
#[test]
fn addassign_i32_to_rank() {
let mut r = Rank::new(0);
r += 10;
assert_eq!(r, 10);
}
#[test]
fn subassign_i32_to_rank() {
let mut r = Rank::new(0);
r -= 7;
assert_eq!(-7, r);
}
#[test]
#[should_panic]
fn assigning_u64max_panix() {
let max = u64::MAX;
let _ = env_logger::try_init();
let _ = max.rank();
assert_eq!("Should've paniced before this assert…", "42")
}
}