wayfarer/crates/save/src/robe.rs

193 lines
4.4 KiB
Rust

use core::fmt;
use std::str::FromStr;
use binrw::{BinRead, BinWrite};
use crate::Result;
const MIN_TIER: u32 = 1;
const MAX_TIER: u32 = 4;
const MAX_RED_TIER_ID: u32 = 3;
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
#[error("Tier must be in range from 1 to 4")]
TierOutOfRange,
#[error("White tier can not be lower than 2")]
WhiteTierMinimum,
#[error("Invalid color, expected red or white")]
InvalidColor,
}
#[derive(Debug, Clone, BinRead, BinWrite)]
pub struct Robe {
value: u32,
}
impl Robe {
pub fn color(&self) -> Color {
if self.value > MAX_RED_TIER_ID {
Color::White
} else {
Color::Red
}
}
pub fn set_color(&mut self, color: Color) {
self.value = match (self.color(), color, self.value) {
(Color::Red, Color::White, 0) => MAX_RED_TIER_ID + 1,
(Color::Red, Color::White, val) => val + MAX_RED_TIER_ID,
(Color::White, Color::Red, val) => val - MAX_RED_TIER_ID,
_ => return,
}
}
pub fn swap_colors(&mut self) {
let new_color = match self.color() {
Color::Red => Color::White,
Color::White => Color::Red,
};
self.set_color(new_color);
}
pub fn tier(&self) -> u32 {
match self.color() {
Color::Red => self.value + 1,
Color::White => self.value - MAX_RED_TIER_ID + 1,
}
}
pub fn set_tier(&mut self, tier: u32) -> Result<(), Error> {
if tier < MIN_TIER || tier > MAX_TIER {
return Err(Error::TierOutOfRange);
}
self.value = match self.color() {
Color::Red => tier - 1,
Color::White if tier == MIN_TIER => {
return Err(Error::WhiteTierMinimum);
}
Color::White => MAX_RED_TIER_ID + tier - 1,
};
Ok(())
}
pub fn increase_tier(&mut self) {
let _ = self.set_tier(self.tier() + 1);
}
pub fn decrease_tier(&mut self) {
let _ = self.set_tier(self.tier() - 1);
}
}
impl fmt::Display for Robe {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Color {
Red,
White,
}
impl FromStr for Color {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Red" | "red" => Ok(Self::Red),
"White" | "white" => Ok(Self::White),
_ => Err(Error::InvalidColor),
}
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Red => write!(f, "Red"),
Self::White => write!(f, "White"),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn change_robe_color() {
// lowest red tier
let mut robe = Robe { value: 0 };
assert_eq!(robe.color(), Color::Red);
assert_eq!(robe.tier(), 1);
robe.set_color(Color::White);
assert_eq!(robe.color(), Color::White);
assert_eq!(robe.tier(), 2);
robe.set_color(Color::Red);
assert_eq!(robe.color(), Color::Red);
assert_eq!(robe.tier(), 2);
// highest red tier
let mut robe = Robe {
value: MAX_RED_TIER_ID,
};
assert_eq!(robe.color(), Color::Red);
assert_eq!(robe.tier(), 4);
robe.set_color(Color::White);
assert_eq!(robe.color(), Color::White);
assert_eq!(robe.tier(), 4);
robe.set_color(Color::Red);
assert_eq!(robe.color(), Color::Red);
assert_eq!(robe.tier(), 4);
}
#[test]
fn change_red_robe_tier() {
let mut robe = Robe { value: 0 };
for tier in 1..=4 {
robe.set_tier(tier).unwrap();
assert_eq!(robe.tier(), tier, "unexpected tier");
assert_eq!(robe.color(), Color::Red, "unexpected color");
}
}
#[test]
fn change_white_robe_tier() {
let mut robe = Robe { value: 4 };
let result = robe.set_tier(1);
assert_eq!(result, Err(Error::WhiteTierMinimum));
for (tier, expected) in (2..=4).zip([2, 3, 4]) {
robe.set_tier(tier).unwrap();
assert_eq!(robe.tier(), expected, "unexpected tier");
assert_eq!(robe.color(), Color::White, "unexpected color");
}
}
}