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}