Add deny-unknown-enum-variants features

Add `NONE = -1` variant to Champions
This commit is contained in:
Mingwei Samuel 2022-06-20 20:43:15 -07:00
parent 2e52b03c63
commit bfa9bdc36e
15 changed files with 89 additions and 43 deletions

View file

@ -28,7 +28,15 @@ default-tls = [ "reqwest/default-tls" ]
native-tls = [ "reqwest/native-tls" ] native-tls = [ "reqwest/native-tls" ]
rustls-tls = [ "reqwest/rustls-tls" ] rustls-tls = [ "reqwest/rustls-tls" ]
deny-unknown = [ "deny-unknown-fields", "deny-unknown-enum-variants" ]
# If enabled, extra unknown fields encountered during deserialization will
# cause an error instead of being ignored.
deny-unknown-fields = [] deny-unknown-fields = []
# If enabled, deserialization of unknown enum variants will cause an error
# instead of being deserialized to `UNKNOWN` or other integer variants.
deny-unknown-enum-variants = [ "deny-unknown-enum-variants-strings", "deny-unknown-enum-variants-integers" ]
deny-unknown-enum-variants-strings = []
deny-unknown-enum-variants-integers = []
[dependencies] [dependencies]
lazy_static = "1.4" lazy_static = "1.4"

View file

@ -15,6 +15,7 @@ newtype_enum! {
/// ///
/// Field | Name | Identifier | Id /// Field | Name | Identifier | Id
/// ---|---|---|--- /// ---|---|---|---
/// `NONE` | None (no ban) | | -1
/// `AATROX` | "Aatrox" | "Aatrox" | 266 /// `AATROX` | "Aatrox" | "Aatrox" | 266
/// `AHRI` | "Ahri" | "Ahri" | 103 /// `AHRI` | "Ahri" | "Ahri" | 103
/// `AKALI` | "Akali" | "Akali" | 84 /// `AKALI` | "Akali" | "Akali" | 84
@ -175,9 +176,10 @@ newtype_enum! {
/// `ZILEAN` | "Zilean" | "Zilean" | 26 /// `ZILEAN` | "Zilean" | "Zilean" | 26
/// `ZOE` | "Zoe" | "Zoe" | 142 /// `ZOE` | "Zoe" | "Zoe" | 142
/// `ZYRA` | "Zyra" | "Zyra" | 143 /// `ZYRA` | "Zyra" | "Zyra" | 143
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub newtype_enum Champion(i16) { pub newtype_enum Champion(i16) {
/// `-1`, none. Appears when a champion ban is not used in champ select.
NONE = -1,
/// `266`. /// `266`.
AATROX = 266, AATROX = 266,
/// `103`. /// `103`.

View file

@ -6,14 +6,14 @@
// // // //
/////////////////////////////////////////////// ///////////////////////////////////////////////
use strum_macros::{ EnumString, IntoStaticStr }; use strum_macros::{ EnumString, EnumVariantNames, IntoStaticStr };
/// League of Legends game mode, such as Classic, /// League of Legends game mode, such as Classic,
/// ARAM, URF, One For All, Ascension, etc. /// ARAM, URF, One For All, Ascension, etc.
#[non_exhaustive] #[non_exhaustive]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[derive(Eq, PartialEq, Hash)] #[derive(Eq, PartialEq, Hash)]
#[derive(EnumString, IntoStaticStr)] #[derive(EnumString, EnumVariantNames, IntoStaticStr)]
#[repr(u8)] #[repr(u8)]
pub enum GameMode { pub enum GameMode {
/// Catch-all variant for new, unknown game modes. /// Catch-all variant for new, unknown game modes.

View file

@ -3,7 +3,7 @@
/// Macro for deriving `Serialize` and `Deserialize` for string enums with an /// Macro for deriving `Serialize` and `Deserialize` for string enums with an
/// `UNKNOWN(String)` variant. /// `UNKNOWN(String)` variant.
/// ///
/// Enum should have `#[derive(EnumString, IntoStaticStr)]` included. /// Enum should have `#[derive(EnumString, EnumVariantNames, IntoStaticStr)]` included.
/// ///
/// Also implements `AsRef<str>`, `Display`, and `From<&str>`. /// Also implements `AsRef<str>`, `Display`, and `From<&str>`.
macro_rules! serde_strum_unknown { macro_rules! serde_strum_unknown {
@ -40,7 +40,23 @@ macro_rules! serde_strum_unknown {
where where
D: serde::de::Deserializer<'de> D: serde::de::Deserializer<'de>
{ {
<&str>::deserialize(deserializer).map(Into::into) #[cfg(not(feature = "deny-unknown-enum-variants-strings"))]
{
<&str>::deserialize(deserializer).map(Into::into)
}
#[cfg(feature = "deny-unknown-enum-variants-strings")]
{
<&str>::deserialize(deserializer).map(Into::into)
.and_then(|item| {
match item {
Self::UNKNOWN(unknown) => Err(serde::de::Error::unknown_variant(
&*unknown,
<Self as strum::VariantNames>::VARIANTS,
)),
other => Ok(other),
}
})
}
} }
} }
} }
@ -56,6 +72,13 @@ macro_rules! arr {
} }
} }
/// Macro for newtype "enums" with integer values.
///
/// For serde, use the following:
/// ```ignore
/// #[derive(Serialize, Deserialize)]
/// #[serde(from = "$repr", into = "$repr")]
/// ```
macro_rules! newtype_enum { macro_rules! newtype_enum {
{ {
$( #[$attr:meta] )* $( #[$attr:meta] )*
@ -97,15 +120,49 @@ macro_rules! newtype_enum {
} }
} }
impl std::convert::From<$name> for $repr {
fn from(value: $name ) -> Self {
value.0
}
}
impl serde::ser::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
<$repr>::serialize(&self.0, serializer)
}
}
impl std::convert::From<$repr> for $name { impl std::convert::From<$repr> for $name {
fn from(value: $repr ) -> Self { fn from(value: $repr ) -> Self {
Self(value) Self(value)
} }
} }
impl<'de> serde::de::Deserialize<'de> for $name {
impl std::convert::From<$name> for $repr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
fn from(value: $name ) -> Self { where
value.0 D: serde::de::Deserializer<'de>
{
#[cfg(not(feature = "deny-unknown-enum-variants-integers"))]
{
<$repr>::deserialize(deserializer).map(Into::into)
}
#[cfg(feature = "deny-unknown-enum-variants-integers")]
{
<$repr>::deserialize(deserializer).map(Into::into)
.and_then(|item: Self| {
if !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
)))
}
else {
Ok(item)
}
})
}
} }
} }
@ -114,7 +171,6 @@ macro_rules! newtype_enum {
self.0.fmt(f) self.0.fmt(f)
} }
} }
impl std::fmt::Debug for $name { impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}({}{})", stringify!($name), self.0, if self.is_known() { "" } else { "?" }) write!(f, "{}({}{})", stringify!($name), self.0, if self.is_known() { "" } else { "?" })

View file

@ -6,12 +6,8 @@
// // // //
/////////////////////////////////////////////// ///////////////////////////////////////////////
use serde::{ Serialize, Deserialize };
newtype_enum! { newtype_enum! {
/// A League of Legends map. /// A League of Legends map.
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub newtype_enum Map(u8) { pub newtype_enum Map(u8) {
/// `1`. /// `1`.
/// Summoner's Rift /// Summoner's Rift

View file

@ -6,12 +6,8 @@
// // // //
/////////////////////////////////////////////// ///////////////////////////////////////////////
use serde::{ Serialize, Deserialize };
newtype_enum! { newtype_enum! {
/// A League of Legends matchmaking queue. /// A League of Legends matchmaking queue.
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub newtype_enum Queue(u16) { pub newtype_enum Queue(u16) {
/// `0`. /// `0`.
/// Games on Custom games /// Games on Custom games

View file

@ -1,10 +1,10 @@
use strum_macros::{ EnumString, IntoStaticStr }; use strum_macros::{ EnumString, EnumVariantNames, IntoStaticStr };
/// LoL or TFT ranked queue types. /// LoL or TFT ranked queue types.
#[non_exhaustive] #[non_exhaustive]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[derive(Eq, PartialEq, Hash)] #[derive(Eq, PartialEq, Hash)]
#[derive(EnumString, IntoStaticStr)] #[derive(EnumString, EnumVariantNames, IntoStaticStr)]
pub enum QueueType { pub enum QueueType {
/// Catch-all variant for new, unknown queue types. /// Catch-all variant for new, unknown queue types.
#[strum(default)] #[strum(default)]
@ -62,6 +62,8 @@ mod test {
} }
#[test] #[test]
// Note: this test is often not run due to this condition below.
#[cfg(not(feature = "deny-unknown-enum-variants-strings"))]
fn check_deserialize() { fn check_deserialize() {
use std::collections::BTreeMap; use std::collections::BTreeMap;

View file

@ -6,12 +6,8 @@
// // // //
/////////////////////////////////////////////// ///////////////////////////////////////////////
use serde::{ Serialize, Deserialize };
newtype_enum! { newtype_enum! {
/// A League of Legends season for competitive matchmaking. /// A League of Legends season for competitive matchmaking.
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub newtype_enum Season(u8) { pub newtype_enum Season(u8) {
/// `0`. /// `0`.
PRESEASON_3 = 0, PRESEASON_3 = 0,

View file

@ -17,6 +17,7 @@ newtype_enum! {
/// ///
/// Field | Name | Identifier | Id /// Field | Name | Identifier | Id
/// ---|---|---|--- /// ---|---|---|---
/// `NONE` | None (no ban) | | -1
{{ {{
for (const { id, alias, name } of champions) { for (const { id, alias, name } of champions) {
}} }}
@ -24,9 +25,10 @@ newtype_enum! {
{{ {{
} }
}} }}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub newtype_enum Champion(i16) { pub newtype_enum Champion(i16) {
/// `-1`, none. Appears when a champion ban is not used in champ select.
NONE = -1,
{{ {{
for (const { id, alias, name } of champions) { for (const { id, alias, name } of champions) {
}} }}

View file

@ -3,14 +3,14 @@
const gameModes = require('./.gameModes.json'); const gameModes = require('./.gameModes.json');
}}{{= dotUtils.preamble() }} }}{{= dotUtils.preamble() }}
use strum_macros::{ EnumString, IntoStaticStr }; use strum_macros::{ EnumString, EnumVariantNames, IntoStaticStr };
/// League of Legends game mode, such as Classic, /// League of Legends game mode, such as Classic,
/// ARAM, URF, One For All, Ascension, etc. /// ARAM, URF, One For All, Ascension, etc.
#[non_exhaustive] #[non_exhaustive]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[derive(Eq, PartialEq, Hash)] #[derive(Eq, PartialEq, Hash)]
#[derive(EnumString, IntoStaticStr)] #[derive(EnumString, EnumVariantNames, IntoStaticStr)]
#[repr(u8)] #[repr(u8)]
pub enum GameMode { pub enum GameMode {
/// Catch-all variant for new, unknown game modes. /// Catch-all variant for new, unknown game modes.

View file

@ -3,12 +3,8 @@
const maps = require('./.maps.json'); const maps = require('./.maps.json');
}}{{= dotUtils.preamble() }} }}{{= dotUtils.preamble() }}
use serde::{ Serialize, Deserialize };
newtype_enum! { newtype_enum! {
/// A League of Legends map. /// A League of Legends map.
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub newtype_enum Map(u8) { pub newtype_enum Map(u8) {
{{ {{
for (const e of maps) { for (const e of maps) {

View file

@ -3,12 +3,8 @@
const queues = require('./.queues.json'); const queues = require('./.queues.json');
}}{{= dotUtils.preamble() }} }}{{= dotUtils.preamble() }}
use serde::{ Serialize, Deserialize };
newtype_enum! { newtype_enum! {
/// A League of Legends matchmaking queue. /// A League of Legends matchmaking queue.
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub newtype_enum Queue(u16) { pub newtype_enum Queue(u16) {
{{ {{
for (const e of queues) { for (const e of queues) {

View file

@ -3,12 +3,8 @@
const seasons = require('./.seasons.json'); const seasons = require('./.seasons.json');
}}{{= dotUtils.preamble() }} }}{{= dotUtils.preamble() }}
use serde::{ Serialize, Deserialize };
newtype_enum! { newtype_enum! {
/// A League of Legends season for competitive matchmaking. /// A League of Legends season for competitive matchmaking.
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub newtype_enum Season(u8) { pub newtype_enum Season(u8) {
{{ {{
for (const e of seasons) { for (const e of seasons) {

View file

@ -40,7 +40,7 @@ async_tests! {
// Spot check 10% for `player-data`. // Spot check 10% for `player-data`.
for entry in leaderboard.iter().step_by(10) for entry in leaderboard.iter().step_by(10)
{ {
let player_data = RIOT_API.lol_challenges_v1().get_player_data(ROUTE, &*entry.puuid) let _player_data = RIOT_API.lol_challenges_v1().get_player_data(ROUTE, &*entry.puuid)
.await.map_err(|e| format!("Failed to get player data PUUID {}: {}", entry.puuid, e))?; .await.map_err(|e| format!("Failed to get player data PUUID {}: {}", entry.puuid, e))?;
} }

View file

@ -9,4 +9,4 @@ cargo +stable test --no-run --features tracing
cargo +nightly test --no-run --features nightly,tracing cargo +nightly test --no-run --features nightly,tracing
# Run tests on nightly. # Run tests on nightly.
RGAPI_KEY="$(cat apikey.txt)" RUST_BACKTRACE=1 RUST_LOG=riven=trace cargo +nightly test --features nightly,deny-unknown-fields -- --nocapture RGAPI_KEY="$(cat apikey.txt)" RUST_BACKTRACE=1 RUST_LOG=riven=trace cargo +nightly test --features nightly,deny-unknown -- --nocapture