Add UNRANKED to tier enum, fix #11

pull/16/head
Mingwei Samuel 2020-06-02 12:45:40 -07:00
parent 2f1698b183
commit ae6056569e
3 changed files with 121 additions and 20 deletions

View File

@ -1,8 +1,8 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use num_enum::{ IntoPrimitive, TryFromPrimitive };
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use strum_macros::{ EnumString, Display, AsRefStr, IntoStaticStr }; use strum_macros::{ EnumString, Display, AsRefStr, IntoStaticStr };
use num_enum::{ IntoPrimitive, TryFromPrimitive };
/// LoL and TFT rank divisions, I, II, III, IV, and (deprecated) V. /// LoL and TFT rank divisions, I, II, III, IV, and (deprecated) V.
/// ///
@ -31,9 +31,9 @@ serde_string!(Division);
/// Ordered from high rank (I) to low (IV). /// Ordered from high rank (I) to low (IV).
/// Excludes V, which is deprecated. /// Excludes V, which is deprecated.
impl IntoEnumIterator for Division { impl IntoEnumIterator for Division {
type Iterator = std::slice::Iter<'static, Self>; type Iterator = std::iter::Copied<std::slice::Iter<'static, Self>>;
fn iter() -> Self::Iterator { fn iter() -> Self::Iterator {
[ Self::I, Self::II, Self::III, Self::IV ].iter() [ Self::I, Self::II, Self::III, Self::IV ].iter().copied()
} }
} }

View File

@ -6,6 +6,9 @@ use strum::IntoEnumIterator;
use super::{ Tier, Division }; use super::{ Tier, Division };
/// (Tier, Division) tuple representing a rank.
pub type Rank = ( Tier, Division );
/// Iterator for iterating `(Tier, Division)` rank tuples. /// Iterator for iterating `(Tier, Division)` rank tuples.
pub struct Iter { pub struct Iter {
tier_iter: Peekable<<Tier as IntoEnumIterator>::Iterator>, tier_iter: Peekable<<Tier as IntoEnumIterator>::Iterator>,
@ -13,11 +16,11 @@ pub struct Iter {
} }
impl Iterator for Iter { impl Iterator for Iter {
type Item = (Tier, Division); type Item = Rank;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
// First find the tier (innermost loop). // First find the tier (innermost loop).
// If none found, we go to next tier (in unwrap_or_else case). // If none found, we go to next tier (in unwrap_or_else case).
let div = *self.div_iter.next() let div = self.div_iter.next()
.unwrap_or_else(|| { .unwrap_or_else(|| {
// If no divisions available, go to next tier, reset the divisions, and return I. // If no divisions available, go to next tier, reset the divisions, and return I.
self.tier_iter.next(); self.tier_iter.next();
@ -33,7 +36,7 @@ impl Iterator for Iter {
self.div_iter = Division::iter(); self.div_iter = Division::iter();
} }
Some((tier, div)) Some(( tier, div ))
} }
} }
@ -68,16 +71,16 @@ mod tests {
#[test] #[test]
fn iter() { fn iter() {
let mut it = super::iter(); let mut it = super::iter();
assert_eq!(Some((Tier::CHALLENGER, Division::I)), it.next()); assert_eq!(Some(( Tier::CHALLENGER, Division::I )), it.next());
assert_eq!(Some((Tier::GRANDMASTER, Division::I)), it.next()); assert_eq!(Some(( Tier::GRANDMASTER, Division::I )), it.next());
assert_eq!(Some((Tier::MASTER, Division::I)), it.next()); assert_eq!(Some(( Tier::MASTER, Division::I )), it.next());
assert_eq!(Some((Tier::DIAMOND, Division::I)), it.next()); assert_eq!(Some(( Tier::DIAMOND, Division::I )), it.next());
assert_eq!(Some((Tier::DIAMOND, Division::II)), it.next()); assert_eq!(Some(( Tier::DIAMOND, Division::II )), it.next());
let mut last = None; let mut last = None;
for next in &mut it { for next in &mut it {
last = Some(next); last = Some(next);
} }
assert_eq!(Some((Tier::IRON, Division::IV)), last); assert_eq!(Some(( Tier::IRON, Division::IV )), last);
assert_eq!(None, it.next()); assert_eq!(None, it.next());
} }

View File

@ -1,5 +1,6 @@
use strum::IntoEnumIterator;
use num_enum::{ IntoPrimitive, TryFromPrimitive }; use num_enum::{ IntoPrimitive, TryFromPrimitive };
use strum_macros::{ EnumString, EnumIter, Display, AsRefStr, IntoStaticStr }; use strum_macros::{ EnumString, Display, AsRefStr, IntoStaticStr };
/// LoL and TFT ranked tiers, such as gold, diamond, challenger, etc. /// LoL and TFT ranked tiers, such as gold, diamond, challenger, etc.
/// ///
@ -11,7 +12,7 @@ use strum_macros::{ EnumString, EnumIter, Display, AsRefStr, IntoStaticStr };
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[derive(Eq, PartialEq, Hash, PartialOrd, Ord)] #[derive(Eq, PartialEq, Hash, PartialOrd, Ord)]
#[derive(IntoPrimitive, TryFromPrimitive)] #[derive(IntoPrimitive, TryFromPrimitive)]
#[derive(EnumString, EnumIter, Display, AsRefStr, IntoStaticStr)] #[derive(EnumString, Display, AsRefStr, IntoStaticStr)]
#[repr(u8)] #[repr(u8)]
pub enum Tier { pub enum Tier {
/// Challenger, the highest tier, an apex tier. Repr: `220_u8`. /// Challenger, the highest tier, an apex tier. Repr: `220_u8`.
@ -32,19 +33,64 @@ pub enum Tier {
BRONZE = 60, BRONZE = 60,
/// Iron, the lowest tier. Repr: `40_u8`. /// Iron, the lowest tier. Repr: `40_u8`.
IRON = 40, IRON = 40,
/// Unranked, no tier. Repr: `0_u8`.
UNRANKED = 0,
} }
serde_string!(Tier); serde_string!(Tier);
impl Tier { impl Tier {
/// If this tier is an apex tier: master and above. /// If this tier is an apex tier: master, grandmaster, or challenger.
/// /// Returns false for unranked.
/// Inverse of is_standard().
/// ///
/// These tiers are NOT queryable by LeagueV4Endpoints::get_league_entries(...). /// These tiers are NOT queryable by LeagueV4Endpoints::get_league_entries(...).
pub const fn is_apex(self) -> bool { pub const fn is_apex(self) -> bool {
// Casts needed for const.
(Self::MASTER as u8) <= (self as u8) (Self::MASTER as u8) <= (self as u8)
} }
/// If this tier is a "standard" tier: iron through diamond.
/// Returns false for unranked.
///
/// ONLY these tiers are queryable by LeagueV4Endpoints::get_league_entries(...).
pub fn is_standard(self) -> bool {
// Casts needed for const.
((Self::UNRANKED as u8) < (self as u8)) && ((self as u8) < (Self::MASTER as u8))
}
/// If this tier is ranked. Returns true for iron through challenger, false for unranked.
pub const fn is_ranked(self) -> bool {
// Casts needed for const.
(Self::UNRANKED as u8) < (self as u8)
}
/// If this tier is unranked (`Tier::UNRANKED`).
///
/// UNRANKED is returned by `Participant.highest_achieved_season_tier`.
pub const fn is_unranked(self) -> bool {
// Casts needed for const.
(self as u8) <= (Self::UNRANKED as u8)
}
/// Converts UNRANKED to None and all ranked tiers to Some(...).
pub fn to_ranked(self) -> Option<Self> {
if self.is_unranked() { None } else { Some(self) }
}
}
/// Returns a DoubleEndedIterator of I, II, III, IV.
/// Ordered from high rank (I) to low (IV).
/// Excludes V, which is deprecated.
impl IntoEnumIterator for Tier {
type Iterator = std::iter::Copied<std::slice::Iter<'static, Self>>;
fn iter() -> Self::Iterator {
[
Self::CHALLENGER, Self::GRANDMASTER, Self::MASTER,
Self::DIAMOND, Self::PLATINUM, Self::GOLD,
Self::SILVER, Self::BRONZE, Self::IRON
].iter().copied()
}
} }
#[cfg(test)] #[cfg(test)]
@ -52,30 +98,64 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn sort() { fn ord() {
assert!(Tier::GOLD < Tier::DIAMOND); assert!(Tier::GOLD < Tier::DIAMOND);
assert!(Tier::UNRANKED < Tier::IRON);
} }
#[test] #[test]
fn apex_check() { fn is_apex() {
assert!( Tier::GRANDMASTER.is_apex()); assert!(Tier::GRANDMASTER.is_apex());
assert!(!Tier::DIAMOND.is_apex()); assert!(!Tier::DIAMOND.is_apex());
assert!(!Tier::UNRANKED.is_apex());
}
#[test]
fn is_ranked() {
assert!(Tier::GRANDMASTER.is_ranked());
assert!(Tier::DIAMOND.is_ranked());
assert!(!Tier::UNRANKED.is_ranked());
}
#[test]
fn is_unranked() {
assert!(!Tier::GRANDMASTER.is_unranked());
assert!(!Tier::DIAMOND.is_unranked());
assert!(Tier::UNRANKED.is_unranked());
}
#[test]
fn to_ranked() {
assert_eq!(Some(Tier::GRANDMASTER), Tier::GRANDMASTER.to_ranked());
assert_eq!(Some(Tier::DIAMOND), Tier::DIAMOND.to_ranked());
assert_eq!(None, Tier::UNRANKED.to_ranked());
}
#[test]
fn is_standard() {
assert!(!Tier::GRANDMASTER.is_standard());
assert!(Tier::DIAMOND.is_standard());
assert!(!Tier::UNRANKED.is_standard());
} }
#[test] #[test]
fn to_string() { fn to_string() {
assert_eq!("GRANDMASTER", Tier::GRANDMASTER.as_ref()); assert_eq!("GRANDMASTER", Tier::GRANDMASTER.as_ref());
assert_eq!("GRANDMASTER", Tier::GRANDMASTER.to_string()); assert_eq!("GRANDMASTER", Tier::GRANDMASTER.to_string());
assert_eq!("UNRANKED", Tier::UNRANKED.as_ref());
assert_eq!("UNRANKED", Tier::UNRANKED.to_string());
} }
#[test] #[test]
fn from_string() { fn from_string() {
assert_eq!(Ok(Tier::GRANDMASTER), "GRANDMASTER".parse()); assert_eq!(Ok(Tier::GRANDMASTER), "GRANDMASTER".parse());
assert_eq!(Ok(Tier::UNRANKED), "UNRANKED".parse());
} }
#[test] #[test]
fn iter() { fn iter() {
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
let mut iter = Tier::iter(); let mut iter = Tier::iter();
assert_eq!(Some(Tier::CHALLENGER), iter.next()); assert_eq!(Some(Tier::CHALLENGER), iter.next());
iter.next(); iter.next();
@ -87,5 +167,23 @@ mod tests {
iter.next(); iter.next();
assert_eq!(Some(Tier::IRON), iter.next()); assert_eq!(Some(Tier::IRON), iter.next());
assert_eq!(None, iter.next()); assert_eq!(None, iter.next());
assert_eq!(None, iter.next_back());
let mut iter = Tier::iter().rev();
assert_eq!(Some(Tier::IRON), iter.next());
iter.next();
iter.next();
iter.next();
iter.next();
assert_eq!(Some(Tier::DIAMOND), iter.next());
iter.next();
iter.next();
assert_eq!(Some(Tier::CHALLENGER), iter.next());
assert_eq!(None, iter.next());
assert_eq!(None, iter.next_back());
let mut iter = Tier::iter();
assert_eq!(Some(Tier::CHALLENGER), iter.next());
assert_eq!(Some(Tier::IRON), iter.next_back());
} }
} }