|
| 1 | +use serde::{ |
| 2 | + de::{Error as DeError, Visitor}, |
| 3 | + Deserialize, Deserializer, |
| 4 | +}; |
| 5 | +use std::fmt::{Formatter, Result as FmtResult}; |
| 6 | + |
| 7 | +const JSON: &str = r#"{ |
| 8 | + "channels": [ |
| 9 | + { |
| 10 | + "default_auto_archive_duration": 10080 |
| 11 | + } |
| 12 | + ], |
| 13 | + "unavailable": false |
| 14 | +}"#; |
| 15 | + |
| 16 | +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] |
| 17 | +#[serde(untagged)] |
| 18 | +pub enum GuildCreate { |
| 19 | + Available(Guild), |
| 20 | + Unavailable(UnavailableGuild), |
| 21 | +} |
| 22 | + |
| 23 | +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)] |
| 24 | +pub struct UnavailableGuild { |
| 25 | + pub unavailable: bool, |
| 26 | +} |
| 27 | + |
| 28 | +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Hash)] |
| 29 | +pub struct Guild { |
| 30 | + pub channels: Vec<Channel>, |
| 31 | +} |
| 32 | + |
| 33 | +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)] |
| 34 | +pub struct Channel { |
| 35 | + pub default_auto_archive_duration: Option<AutoArchiveDuration>, |
| 36 | +} |
| 37 | + |
| 38 | +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
| 39 | +// Fix 1: Uncomment this and comment the handwritten deser impl. |
| 40 | +// #[derive(Deserialize)] |
| 41 | +// #[serde(from = "u16")] |
| 42 | +pub enum AutoArchiveDuration { |
| 43 | + Hour, |
| 44 | + Day, |
| 45 | + ThreeDays, |
| 46 | + Week, |
| 47 | + Unknown { value: u16 }, |
| 48 | +} |
| 49 | + |
| 50 | +impl From<u16> for AutoArchiveDuration { |
| 51 | + fn from(value: u16) -> Self { |
| 52 | + match value { |
| 53 | + 60 => Self::Hour, |
| 54 | + 1440 => Self::Day, |
| 55 | + 4320 => Self::ThreeDays, |
| 56 | + 10080 => Self::Week, |
| 57 | + value => Self::Unknown { value }, |
| 58 | + } |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +impl<'de> Deserialize<'de> for AutoArchiveDuration { |
| 63 | + fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { |
| 64 | + deserializer.deserialize_u16(U16EnumVisitor).map(u16::into) |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +pub struct U16EnumVisitor; |
| 69 | + |
| 70 | +impl<'de> Visitor<'de> for U16EnumVisitor { |
| 71 | + type Value = u16; |
| 72 | + |
| 73 | + fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult { |
| 74 | + f.write_str("u16") |
| 75 | + } |
| 76 | + |
| 77 | + fn visit_u16<E: DeError>(self, value: u16) -> Result<Self::Value, E> { |
| 78 | + Ok(value) |
| 79 | + } |
| 80 | + |
| 81 | + fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> |
| 82 | + where |
| 83 | + E: DeError, |
| 84 | + { |
| 85 | + v.try_into().map_err(DeError::custom) |
| 86 | + } |
| 87 | + |
| 88 | + // Fix 2: Uncomment this |
| 89 | + // fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> |
| 90 | + // where |
| 91 | + // E: DeError, |
| 92 | + // { |
| 93 | + // v.try_into().map_err(DeError::custom) |
| 94 | + // } |
| 95 | +} |
| 96 | + |
| 97 | +#[test] |
| 98 | +fn test_deser_u16() -> Result<(), Box<dyn std::error::Error>> { |
| 99 | + unsafe { |
| 100 | + let mut json = JSON.to_string(); |
| 101 | + let a = dbg!(crate::from_str::<Guild>(&mut json)?); |
| 102 | + let b = dbg!(crate::from_str::<UnavailableGuild>(&mut json)?); |
| 103 | + let c = dbg!(crate::from_str::<GuildCreate>(&mut json)?); |
| 104 | + assert_eq!(a, serde_json::from_str::<Guild>(&json)?); |
| 105 | + assert_eq!(b, serde_json::from_str::<UnavailableGuild>(&json)?); |
| 106 | + assert_eq!(c, serde_json::from_str::<GuildCreate>(&json)?); |
| 107 | + }; |
| 108 | + Ok(()) |
| 109 | +} |
0 commit comments