fix: regen, updates to fix bad match data

https://github.com/RiotGames/developer-relations/issues/898
This commit is contained in:
Mingwei Samuel 2024-03-05 11:27:30 -08:00
parent ff6a3af9e1
commit 95c3ccbe0b
10 changed files with 157 additions and 13 deletions

View file

@ -7,5 +7,9 @@
"RUST_LOG": "riven=debug" "RUST_LOG": "riven=debug"
} }
} }
],
"rust-analyzer.cargo.features": [
"nightly",
"deny-unknown"
] ]
} }

View file

@ -31,5 +31,38 @@ pub enum GameType {
TUTORIAL_GAME, TUTORIAL_GAME,
} }
impl GameType {
/// https://github.com/RiotGames/developer-relations/issues/898
pub(crate) fn serialize_empty<S>(
val: &Option<Self>,
serializer: S,
) -> Result<S::Ok, S::Error>
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<Option<Self>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::de::IntoDeserializer;
let opt = Option::<String>::deserialize(deserializer)?;
match opt.as_deref() {
None | Some("") => Ok(None),
Some(s) => Self::deserialize(s.into_deserializer()).map(Some)
}
}
}
#[cfg(test)] #[cfg(test)]
mod test; mod test;

View file

@ -49,10 +49,17 @@ macro_rules! serde_strum_unknown {
<&str>::deserialize(deserializer) <&str>::deserialize(deserializer)
.map(Into::into) .map(Into::into)
.and_then(|item| match item { .and_then(|item| match item {
Self::UNKNOWN(unknown) => Err(serde::de::Error::unknown_variant( 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, &*unknown,
<Self as strum::VariantNames>::VARIANTS, <Self as strum::VariantNames>::VARIANTS,
)), ))
}
}
other => Ok(other), other => Ok(other),
}) })
} }
@ -151,7 +158,8 @@ macro_rules! newtype_enum {
{ {
<$repr>::deserialize(deserializer).map(Into::into) <$repr>::deserialize(deserializer).map(Into::into)
.and_then(|item: Self| { .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!( Err(serde::de::Error::custom(format!(
"Unknown integer enum variant: {} (\"deny-unknown-enum-variants-integers\" feature is enabled).\nExpected one of the following: {:?}", "Unknown integer enum variant: {} (\"deny-unknown-enum-variants-integers\" feature is enabled).\nExpected one of the following: {:?}",
item, Self::ALL_KNOWN item, Self::ALL_KNOWN

View file

@ -56,3 +56,36 @@ pub use team::*;
mod tier; mod tier;
pub use tier::*; pub use tier::*;
/// https://github.com/RiotGames/developer-relations/issues/898
pub(crate) fn serialize_empty_string_none<S, T>(
val: &Option<T>,
serializer: S,
) -> Result<S::Ok, S::Error>
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<Option<T>, D::Error>
where
D: serde::de::Deserializer<'de>,
T: serde::de::Deserialize<'de>,
{
use serde::de::{Deserialize, IntoDeserializer};
let opt = Option::<String>::deserialize(deserializer)?;
match opt.as_deref() {
None | Some("") => Ok(None),
Some(s) => T::deserialize(s.into_deserializer()).map(Some),
}
}

View file

@ -8,7 +8,7 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/ // http://www.mingweisamuel.com/riotapi-schema/tool/
// Version ba7699aed741222f2431e1f3e4ba42c3ac302510 // Version 031d3e7fc343bd86d82c45559fc79d3a87fa1b82
//! Automatically generated endpoint handles. //! Automatically generated endpoint handles.
#![allow(clippy::let_and_return, clippy::too_many_arguments)] #![allow(clippy::let_and_return, clippy::too_many_arguments)]

View file

@ -8,7 +8,7 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/ // http://www.mingweisamuel.com/riotapi-schema/tool/
// Version ba7699aed741222f2431e1f3e4ba42c3ac302510 // Version 031d3e7fc343bd86d82c45559fc79d3a87fa1b82
//! Metadata about the Riot API and Riven. //! Metadata about the Riot API and Riven.
//! //!

View file

@ -8,7 +8,7 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/ // http://www.mingweisamuel.com/riotapi-schema/tool/
// Version ba7699aed741222f2431e1f3e4ba42c3ac302510 // Version 031d3e7fc343bd86d82c45559fc79d3a87fa1b82
#![allow(missing_docs)] #![allow(missing_docs)]
@ -999,7 +999,11 @@ pub mod match_v5 {
#[serde(rename = "gameStartTimestamp")] #[serde(rename = "gameStartTimestamp")]
pub game_start_timestamp: i64, pub game_start_timestamp: i64,
#[serde(rename = "gameType")] #[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<crate::consts::GameType>,
/// The first two parts can be used to determine the patch a game was played on. /// The first two parts can be used to determine the patch a game was played on.
#[serde(rename = "gameVersion")] #[serde(rename = "gameVersion")]
pub game_version: String, pub game_version: String,
@ -2203,7 +2207,8 @@ pub mod match_v5 {
#[serde(rename = "events")] #[serde(rename = "events")]
pub events: std::vec::Vec<MatchTimelineInfoFrameEvent>, pub events: std::vec::Vec<MatchTimelineInfoFrameEvent>,
#[serde(rename = "participantFrames")] #[serde(rename = "participantFrames")]
pub participant_frames: MatchTimelineInfoFrameParticipantFrames, #[serde(skip_serializing_if = "Option::is_none")]
pub participant_frames: Option<MatchTimelineInfoFrameParticipantFrames>,
#[serde(rename = "timestamp")] #[serde(rename = "timestamp")]
pub timestamp: i32, pub timestamp: i32,
} }

View file

@ -29,5 +29,38 @@ pub enum GameType {
}} }}
} }
impl GameType {
/// https://github.com/RiotGames/developer-relations/issues/898
pub(crate) fn serialize_empty<S>(
val: &Option<Self>,
serializer: S,
) -> Result<S::Ok, S::Error>
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<Option<Self>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::de::IntoDeserializer;
let opt = Option::<String>::deserialize(deserializer)?;
match opt.as_deref() {
None | Some("") => Ok(None),
Some(s) => Self::deserialize(s.into_deserializer()).map(Some)
}
}
}
#[cfg(test)] #[cfg(test)]
mod test; mod test;

View file

@ -68,6 +68,12 @@ pub mod {{= dotUtils.changeCase.snakeCase(endpoint) }} {
#[serde(serialize_with = "crate::consts::Champion::serialize_result")] #[serde(serialize_with = "crate::consts::Champion::serialize_result")]
#[serde(deserialize_with = "crate::consts::Champion::deserialize_result")] #[serde(deserialize_with = "crate::consts::Champion::deserialize_result")]
pub {{= name }}: Result<crate::consts::Champion, std::num::TryFromIntError>, pub {{= name }}: Result<crate::consts::Champion, std::num::TryFromIntError>,
{{?? '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 }) }}, pub {{= name }}: {{= dotUtils.stringifyType(prop, { optional }) }},
{{?}} {{?}}

View file

@ -227,6 +227,23 @@ pub async fn match_v5_get(
.champion() .champion()
.map_err(|e| format!("Failed to determine match {} champion: {}", matche, e))?; .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() { if m.info.teams.is_empty() {
return Err(format!("Match {} should have teams.", matche)); 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)); return Err(format!("Match {} should have participants.", matche));
} }
if let Some(game_id) = m.info.game_id { if let Some(game_id) = m.info.game_id {
if matche[(matche.find('_').unwrap() + 1)..] != game_id.to_string() { if 0 == game_id {
return Err(format!("Match {} number ID should match.", matche)); 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() { 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(()) Ok(())
}); });