serenity/internal/macros.rs
1//! A set of macros for easily working with internals.
2
3#[cfg(any(feature = "model", feature = "utils"))]
4macro_rules! cdn {
5 ($e:expr) => {
6 concat!("https://cdn.discordapp.com", $e)
7 };
8 ($e:expr, $($rest:tt)*) => {
9 format!(cdn!($e), $($rest)*)
10 };
11}
12
13#[cfg(feature = "http")]
14macro_rules! api {
15 ($e:expr) => {
16 concat!("https://discord.com/api/v10", $e)
17 };
18 ($e:expr, $($rest:tt)*) => {
19 format!(api!($e), $($rest)*)
20 };
21}
22
23#[cfg(feature = "http")]
24macro_rules! status {
25 ($e:expr) => {
26 concat!("https://status.discord.com/api/v2", $e)
27 };
28}
29
30/// The `enum_number!` macro generates `From` implementations to convert between values and the
31/// enum which can then be utilized by `serde` with `#[serde(from = "u8", into = "u8")]`.
32///
33/// When defining the enum like this:
34/// ```ignore
35/// enum_number! {
36/// /// The `Foo` enum
37/// #[derive(Clone, Copy, Deserialize, Serialize)]
38/// #[serde(from = "u8", into = "u8")]
39/// pub enum Foo {
40/// /// First
41/// Aah = 1,
42/// /// Second
43/// Bar = 2,
44/// _ => Unknown(u8),
45/// }
46/// }
47/// ```
48///
49/// Code like this will be generated:
50///
51/// ```
52/// # use serde::{Deserialize, Serialize};
53/// #
54/// /// The `Foo` enum
55/// #[derive(Clone, Copy, Deserialize, Serialize)]
56/// #[serde(from = "u8", into = "u8")]
57/// pub enum Foo {
58/// /// First
59/// Aah,
60/// /// Second,
61/// Bar,
62/// /// Variant value is unknown.
63/// Unknown(u8),
64/// }
65///
66/// impl From<u8> for Foo {
67/// fn from(value: u8) -> Self {
68/// match value {
69/// 1 => Self::Aah,
70/// 2 => Self::Bar,
71/// unknown => Self::Unknown(unknown),
72/// }
73/// }
74/// }
75///
76/// impl From<Foo> for u8 {
77/// fn from(value: Foo) -> Self {
78/// match value {
79/// Foo::Aah => 1,
80/// Foo::Bar => 2,
81/// Foo::Unknown(unknown) => unknown,
82/// }
83/// }
84/// }
85/// ```
86macro_rules! enum_number {
87 (
88 $(#[$outer:meta])*
89 $vis:vis enum $Enum:ident {
90 $(
91 $(#[doc = $doc:literal])*
92 $(#[cfg $($cfg:tt)*])?
93 $(#[default $($dummy:tt)?])?
94 $Variant:ident = $value:literal,
95 )*
96 _ => Unknown($T:ty),
97 }
98 ) => {
99 $(#[$outer])*
100 $vis enum $Enum {
101 $(
102 $(#[doc = $doc])*
103 $(#[cfg $($cfg)*])?
104 $(#[default $($dummy:tt)?])?
105 $Variant,
106 )*
107 /// Variant value is unknown.
108 Unknown($T),
109 }
110
111 impl From<$T> for $Enum {
112 fn from(value: $T) -> Self {
113 match value {
114 $($(#[cfg $($cfg)*])? $value => Self::$Variant,)*
115 unknown => Self::Unknown(unknown),
116 }
117 }
118 }
119
120 impl From<$Enum> for $T {
121 fn from(value: $Enum) -> Self {
122 match value {
123 $($(#[cfg $($cfg)*])? $Enum::$Variant => $value,)*
124 $Enum::Unknown(unknown) => unknown,
125 }
126 }
127 }
128 };
129}
130
131/// The macro forwards the generation to the `bitflags::bitflags!` macro and implements the default
132/// (de)serialization for Discord's bitmask values.
133///
134/// The flags are created with `T::from_bits_truncate` for the deserialized integer value.
135///
136/// Use the `bitflags::bitflags! macro directly if a different serde implementation is required.
137macro_rules! bitflags {
138 (
139 $(#[$outer:meta])*
140 $vis:vis struct $BitFlags:ident: $T:ty {
141 $(
142 $(#[$inner:ident $($args:tt)*])*
143 const $Flag:ident = $value:expr;
144 )*
145 }
146 ) => {
147 $(#[$outer])*
148 #[repr(Rust, packed)]
149 $vis struct $BitFlags($T);
150
151 bitflags::bitflags! {
152 impl $BitFlags: $T {
153 $(
154 $(#[$inner $($args)*])*
155 const $Flag = $value;
156 )*
157 }
158 }
159
160 bitflags!(__impl_serde $BitFlags: $T);
161 };
162 (__impl_serde $BitFlags:ident: $T:tt) => {
163 impl<'de> serde::de::Deserialize<'de> for $BitFlags {
164 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
165 Ok(Self::from_bits_truncate(<$T>::deserialize(deserializer)?))
166 }
167 }
168
169 impl serde::ser::Serialize for $BitFlags {
170 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
171 self.bits().serialize(serializer)
172 }
173 }
174 };
175}
176
177#[cfg(test)]
178mod tests {
179 use crate::json::{assert_json, json};
180
181 #[test]
182 fn enum_number() {
183 enum_number! {
184 #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
185 #[serde(from = "u8", into = "u8")]
186 pub enum T {
187 /// AAA
188 A = 1,
189 /// BBB
190 B = 2,
191 /// CCC
192 C = 3,
193 _ => Unknown(u8),
194 }
195 }
196
197 assert_json(&T::A, json!(1));
198 assert_json(&T::B, json!(2));
199 assert_json(&T::C, json!(3));
200 assert_json(&T::Unknown(123), json!(123));
201 }
202}