From 95c3ccbe0bcf60ec4dd89438d3a376c3202a5793 Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Tue, 5 Mar 2024 11:27:30 -0800 Subject: [PATCH] fix: regen, updates to fix bad match data https://github.com/RiotGames/developer-relations/issues/898 --- .vscode/settings.json | 4 ++++ riven/src/consts/game_type.rs | 33 +++++++++++++++++++++++++++++ riven/src/consts/macros.rs | 18 +++++++++++----- riven/src/consts/mod.rs | 33 +++++++++++++++++++++++++++++ riven/src/endpoints.rs | 2 +- riven/src/meta.rs | 2 +- riven/src/models.rs | 11 +++++++--- riven/srcgen/consts/game_type.rs.dt | 33 +++++++++++++++++++++++++++++ riven/srcgen/models.rs.dt | 6 ++++++ riven/tests/testutils.rs | 28 +++++++++++++++++++++--- 10 files changed, 157 insertions(+), 13 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a5b4a73..4b4529f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,9 @@ "RUST_LOG": "riven=debug" } } + ], + "rust-analyzer.cargo.features": [ + "nightly", + "deny-unknown" ] } diff --git a/riven/src/consts/game_type.rs b/riven/src/consts/game_type.rs index fc2ba0f..010ecee 100644 --- a/riven/src/consts/game_type.rs +++ b/riven/src/consts/game_type.rs @@ -31,5 +31,38 @@ pub enum GameType { TUTORIAL_GAME, } +impl GameType { + /// https://github.com/RiotGames/developer-relations/issues/898 + pub(crate) fn serialize_empty( + val: &Option, + serializer: S, + ) -> Result + where + S: serde::ser::Serializer, + { + use serde::ser::Serialize; + if let Some(val) = val { + val.serialize(serializer) + } else { + "".serialize(serializer) + } + } + + /// https://github.com/RiotGames/developer-relations/issues/898 + pub(crate) fn deserialize_empty<'de, D>( + deserializer: D, + ) -> Result, D::Error> + where + D: serde::de::Deserializer<'de>, + { + use serde::de::IntoDeserializer; + let opt = Option::::deserialize(deserializer)?; + match opt.as_deref() { + None | Some("") => Ok(None), + Some(s) => Self::deserialize(s.into_deserializer()).map(Some) + } + } +} + #[cfg(test)] mod test; diff --git a/riven/src/consts/macros.rs b/riven/src/consts/macros.rs index 14028d9..73c2949 100644 --- a/riven/src/consts/macros.rs +++ b/riven/src/consts/macros.rs @@ -49,10 +49,17 @@ macro_rules! serde_strum_unknown { <&str>::deserialize(deserializer) .map(Into::into) .and_then(|item| match item { - Self::UNKNOWN(unknown) => Err(serde::de::Error::unknown_variant( - &*unknown, - ::VARIANTS, - )), + Self::UNKNOWN(unknown) => { + if unknown.is_empty() { + // https://github.com/RiotGames/developer-relations/issues/898 + Ok(Self::UNKNOWN(unknown)) + } else { + Err(serde::de::Error::unknown_variant( + &*unknown, + ::VARIANTS, + )) + } + } other => Ok(other), }) } @@ -151,7 +158,8 @@ macro_rules! newtype_enum { { <$repr>::deserialize(deserializer).map(Into::into) .and_then(|item: Self| { - if !item.is_known() { + // Exception for id zero: https://github.com/RiotGames/developer-relations/issues/898 + if 0 != item.0 && !item.is_known() { Err(serde::de::Error::custom(format!( "Unknown integer enum variant: {} (\"deny-unknown-enum-variants-integers\" feature is enabled).\nExpected one of the following: {:?}", item, Self::ALL_KNOWN diff --git a/riven/src/consts/mod.rs b/riven/src/consts/mod.rs index 986e6bc..2bc03d3 100644 --- a/riven/src/consts/mod.rs +++ b/riven/src/consts/mod.rs @@ -56,3 +56,36 @@ pub use team::*; mod tier; pub use tier::*; + +/// https://github.com/RiotGames/developer-relations/issues/898 +pub(crate) fn serialize_empty_string_none( + val: &Option, + serializer: S, +) -> Result +where + S: serde::ser::Serializer, + T: serde::ser::Serialize, +{ + use serde::ser::Serialize; + if let Some(val) = val { + val.serialize(serializer) + } else { + "".serialize(serializer) + } +} + +/// https://github.com/RiotGames/developer-relations/issues/898 +pub(crate) fn deserialize_empty_string_none<'de, D, T>( + deserializer: D, +) -> Result, D::Error> +where + D: serde::de::Deserializer<'de>, + T: serde::de::Deserialize<'de>, +{ + use serde::de::{Deserialize, IntoDeserializer}; + let opt = Option::::deserialize(deserializer)?; + match opt.as_deref() { + None | Some("") => Ok(None), + Some(s) => T::deserialize(s.into_deserializer()).map(Some), + } +} diff --git a/riven/src/endpoints.rs b/riven/src/endpoints.rs index 83466f4..9d89931 100644 --- a/riven/src/endpoints.rs +++ b/riven/src/endpoints.rs @@ -8,7 +8,7 @@ /////////////////////////////////////////////// // http://www.mingweisamuel.com/riotapi-schema/tool/ -// Version ba7699aed741222f2431e1f3e4ba42c3ac302510 +// Version 031d3e7fc343bd86d82c45559fc79d3a87fa1b82 //! Automatically generated endpoint handles. #![allow(clippy::let_and_return, clippy::too_many_arguments)] diff --git a/riven/src/meta.rs b/riven/src/meta.rs index bcafe6c..5911fcf 100644 --- a/riven/src/meta.rs +++ b/riven/src/meta.rs @@ -8,7 +8,7 @@ /////////////////////////////////////////////// // http://www.mingweisamuel.com/riotapi-schema/tool/ -// Version ba7699aed741222f2431e1f3e4ba42c3ac302510 +// Version 031d3e7fc343bd86d82c45559fc79d3a87fa1b82 //! Metadata about the Riot API and Riven. //! diff --git a/riven/src/models.rs b/riven/src/models.rs index 406e224..4fe8d69 100644 --- a/riven/src/models.rs +++ b/riven/src/models.rs @@ -8,7 +8,7 @@ /////////////////////////////////////////////// // http://www.mingweisamuel.com/riotapi-schema/tool/ -// Version ba7699aed741222f2431e1f3e4ba42c3ac302510 +// Version 031d3e7fc343bd86d82c45559fc79d3a87fa1b82 #![allow(missing_docs)] @@ -999,7 +999,11 @@ pub mod match_v5 { #[serde(rename = "gameStartTimestamp")] pub game_start_timestamp: i64, #[serde(rename = "gameType")] - pub game_type: crate::consts::GameType, + /// + /// Will be `None` if empty string is returned: https://github.com/RiotGames/developer-relations/issues/898 + #[serde(serialize_with = "crate::consts::serialize_empty_string_none")] + #[serde(deserialize_with = "crate::consts::deserialize_empty_string_none")] + pub game_type: Option, /// The first two parts can be used to determine the patch a game was played on. #[serde(rename = "gameVersion")] pub game_version: String, @@ -2203,7 +2207,8 @@ pub mod match_v5 { #[serde(rename = "events")] pub events: std::vec::Vec, #[serde(rename = "participantFrames")] - pub participant_frames: MatchTimelineInfoFrameParticipantFrames, + #[serde(skip_serializing_if = "Option::is_none")] + pub participant_frames: Option, #[serde(rename = "timestamp")] pub timestamp: i32, } diff --git a/riven/srcgen/consts/game_type.rs.dt b/riven/srcgen/consts/game_type.rs.dt index 5cf4dde..78e269c 100644 --- a/riven/srcgen/consts/game_type.rs.dt +++ b/riven/srcgen/consts/game_type.rs.dt @@ -29,5 +29,38 @@ pub enum GameType { }} } +impl GameType { + /// https://github.com/RiotGames/developer-relations/issues/898 + pub(crate) fn serialize_empty( + val: &Option, + serializer: S, + ) -> Result + where + S: serde::ser::Serializer, + { + use serde::ser::Serialize; + if let Some(val) = val { + val.serialize(serializer) + } else { + "".serialize(serializer) + } + } + + /// https://github.com/RiotGames/developer-relations/issues/898 + pub(crate) fn deserialize_empty<'de, D>( + deserializer: D, + ) -> Result, D::Error> + where + D: serde::de::Deserializer<'de>, + { + use serde::de::IntoDeserializer; + let opt = Option::::deserialize(deserializer)?; + match opt.as_deref() { + None | Some("") => Ok(None), + Some(s) => Self::deserialize(s.into_deserializer()).map(Some) + } + } +} + #[cfg(test)] mod test; diff --git a/riven/srcgen/models.rs.dt b/riven/srcgen/models.rs.dt index ee34606..32d319b 100644 --- a/riven/srcgen/models.rs.dt +++ b/riven/srcgen/models.rs.dt @@ -68,6 +68,12 @@ pub mod {{= dotUtils.changeCase.snakeCase(endpoint) }} { #[serde(serialize_with = "crate::consts::Champion::serialize_result")] #[serde(deserialize_with = "crate::consts::Champion::deserialize_result")] pub {{= name }}: Result, +{{?? 'gameType' === propKey && 'Info' === schemaName && 'match-v5' === endpoint }} + /// + /// Will be `None` if empty string is returned: https://github.com/RiotGames/developer-relations/issues/898 + #[serde(serialize_with = "crate::consts::serialize_empty_string_none")] + #[serde(deserialize_with = "crate::consts::deserialize_empty_string_none")] + pub {{= name }}: Option<{{= dotUtils.stringifyType(prop, {}) }}>, {{??}} pub {{= name }}: {{= dotUtils.stringifyType(prop, { optional }) }}, {{?}} diff --git a/riven/tests/testutils.rs b/riven/tests/testutils.rs index d575234..fd1f5b4 100644 --- a/riven/tests/testutils.rs +++ b/riven/tests/testutils.rs @@ -227,6 +227,23 @@ pub async fn match_v5_get( .champion() .map_err(|e| format!("Failed to determine match {} champion: {}", matche, e))?; } + + { + let game_id = m.info.game_id; + if 0 == game_id { + eprintln!( + "Match {} `game_id` is zero, skipping remaining tests (see https://github.com/RiotGames/developer-relations/issues/898).", + matche + ); + return Ok(()); + } else if matche[(matche.find('_').unwrap() + 1)..] != game_id.to_string() { + return Err(format!( + "Match {} timeline number ID should match `game_id` {}.", + matche, game_id + )); + } + } + if m.info.teams.is_empty() { return Err(format!("Match {} should have teams.", matche)); } @@ -256,12 +273,17 @@ pub async fn match_v5_get_timeline( return Err(format!("Match {} should have participants.", matche)); } if let Some(game_id) = m.info.game_id { - if matche[(matche.find('_').unwrap() + 1)..] != game_id.to_string() { - return Err(format!("Match {} number ID should match.", matche)); + if 0 == game_id { + eprintln!("Match {} timeline `game_id` is zero (see https://github.com/RiotGames/developer-relations/issues/898).", matche); + } else if matche[(matche.find('_').unwrap() + 1)..] != game_id.to_string() { + return Err(format!( + "Match {} timeline number ID should match `game_id` {}.", + matche, game_id + )); } } if m.info.frames.is_empty() { - return Err(format!("Match {} timleine should have frames.", matche)); + return Err(format!("Match {} timeline should have frames.", matche)); } Ok(()) });