1#![allow(clippy::non_ascii_literal)]
3
4use is_macro::Is;
5use serde::{
6 de::{self, Deserializer, IgnoredAny, MapAccess, Visitor},
7 ser::Serializer,
8 Deserialize, Serialize,
9};
10
11#[derive(Debug, PartialEq, Eq, Clone, Hash, Is)]
13#[non_exhaustive]
14pub enum Kind {
15 Darts,
17 Dice,
19 Basketball,
21 Unknown(String),
32}
33
34#[derive(Debug, PartialEq, Eq, Clone, Hash)]
38#[non_exhaustive]
39pub struct Dice {
40 pub value: u8,
42 pub kind: Kind,
44}
45
46const VALUE: &str = "value";
47const EMOJI: &str = "emoji";
48
49struct DiceVisitor;
50
51impl<'v> Visitor<'v> for DiceVisitor {
52 type Value = Dice;
53
54 fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
55 write!(fmt, "struct Dice")
56 }
57
58 fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
59 where
60 V: MapAccess<'v>,
61 {
62 let mut value = None;
63 let mut emoji: Option<String> = None;
64
65 while let Some(key) = map.next_key()? {
66 match key {
67 VALUE => value = Some(map.next_value()?),
68 EMOJI => emoji = Some(map.next_value()?),
69 _ => {
70 let _ = map.next_value::<IgnoredAny>();
71 }
72 }
73 }
74
75 let kind = match emoji.as_deref() {
76 Some("🎯") => Kind::Darts,
77 Some("🎲") => Kind::Dice,
78 Some("🏀") => Kind::Basketball,
79 Some(unknown) => Kind::Unknown(unknown.to_string()),
80 None => return Err(de::Error::missing_field(EMOJI)),
81 };
82
83 Ok(Dice {
84 kind,
85 value: value.ok_or_else(|| de::Error::missing_field(VALUE))?,
86 })
87 }
88}
89
90impl<'de> Deserialize<'de> for Dice {
91 fn deserialize<D>(d: D) -> Result<Self, D::Error>
92 where
93 D: Deserializer<'de>,
94 {
95 d.deserialize_struct("Dice", &[VALUE, EMOJI], DiceVisitor)
96 }
97}
98
99impl Serialize for Kind {
100 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
101 where
102 S: Serializer,
103 {
104 serializer.serialize_str(match self {
105 Self::Dice => "🎲",
106 Self::Darts => "🎯",
107 Self::Basketball => "🏀",
108 Self::Unknown(emoji) => emoji,
109 })
110 }
111}