diff --git a/werewolves-proto/src/game/mod.rs b/werewolves-proto/src/game/mod.rs index 84b7fb1..6cb3dd4 100644 --- a/werewolves-proto/src/game/mod.rs +++ b/werewolves-proto/src/game/mod.rs @@ -27,7 +27,10 @@ use core::{ use chrono::{DateTime, Utc}; use rand::{Rng, seq::SliceRandom}; -use serde::{Deserialize, Serialize}; +use serde::{ + Deserialize, Serialize, + de::{Expected, Unexpected}, +}; use uuid::Uuid; use crate::{ @@ -482,12 +485,70 @@ pub enum Maybe { Maybe, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] pub enum GameTime { Day { number: NonZeroU8 }, Night { number: u8 }, } +impl Serialize for GameTime { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + match self { + GameTime::Day { number } => format!("day_{number}"), + GameTime::Night { number } => format!("night_{number}"), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for GameTime { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + enum ExpectedGameTime { + Format, + NumberU8, + NonZeroU8, + } + impl Expected for ExpectedGameTime { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str(match self { + Self::Format => "expected day_{num_nz} or night_{num}, where {num_nz} is a nonzero u8, and {num} is a u8", + Self::NumberU8 => "expected a u8 after day_ or night_", + Self::NonZeroU8 => "expected a non-zero day number", + }) + } + } + let s = crate::limited::ClampedString::<5, 9>::deserialize(deserializer)?; + let (day_or_night, number) = s.split_once('_').ok_or(serde::de::Error::invalid_value( + Unexpected::Str(s.as_str()), + &ExpectedGameTime::Format, + ))?; + let parsed_number = number.parse::().map_err(|_| { + serde::de::Error::invalid_value(Unexpected::Str(number), &ExpectedGameTime::NumberU8) + })?; + match day_or_night { + "day" => NonZeroU8::new(parsed_number) + .map(|number| GameTime::Day { number }) + .ok_or(serde::de::Error::invalid_value( + Unexpected::Str(number), + &ExpectedGameTime::NonZeroU8, + )), + "night" => Ok(GameTime::Night { + number: parsed_number, + }), + _ => Err(serde::de::Error::invalid_value( + Unexpected::Str(day_or_night), + &ExpectedGameTime::Format, + )), + } + } +} + impl PartialOrd for GameTime { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other))