twilight_model/gateway/presence/
activity_button.rs1use serde::{
4 de::{Deserializer, Error as DeError, IgnoredAny, MapAccess, Visitor},
5 ser::{SerializeStruct, Serializer},
6 Deserialize, Serialize,
7};
8use std::fmt::{Formatter, Result as FmtResult};
9
10#[derive(Clone, Debug, Eq, Hash, PartialEq)]
53pub enum ActivityButton {
54 Link(ActivityButtonLink),
56 Text(ActivityButtonText),
58 Unknown,
60}
61
62impl ActivityButton {
63 pub const fn is_link(&self) -> bool {
65 matches!(self, Self::Link(_))
66 }
67
68 pub const fn is_text(&self) -> bool {
70 matches!(self, Self::Text(_))
71 }
72
73 pub fn label(&self) -> Option<&str> {
75 match self {
76 Self::Link(link) => Some(&link.label),
77 Self::Text(text) => Some(&text.label),
78 Self::Unknown => None,
79 }
80 }
81
82 pub fn url(&self) -> Option<&str> {
85 if let Self::Link(link) = self {
86 Some(&link.url)
87 } else {
88 None
89 }
90 }
91}
92
93#[derive(Debug, Deserialize)]
94#[serde(field_identifier, rename_all = "snake_case")]
95enum ActivityButtonField {
96 Label,
97 Url,
98}
99
100struct ActivityButtonVisitor;
101
102impl<'de> Visitor<'de> for ActivityButtonVisitor {
103 type Value = ActivityButton;
104
105 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
106 f.write_str("activity button struct or string")
107 }
108
109 fn visit_string<E: DeError>(self, v: String) -> Result<Self::Value, E> {
110 Ok(ActivityButton::Text(ActivityButtonText { label: v }))
111 }
112
113 fn visit_str<E: DeError>(self, v: &str) -> Result<Self::Value, E> {
114 Ok(ActivityButton::Text(ActivityButtonText {
115 label: v.to_owned(),
116 }))
117 }
118
119 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
120 let mut label = None;
121 let mut url = None;
122
123 loop {
124 let key = match map.next_key() {
125 Ok(Some(key)) => key,
126 Ok(None) => break,
127 Err(_) => {
128 map.next_value::<IgnoredAny>()?;
129
130 continue;
131 }
132 };
133
134 match key {
135 ActivityButtonField::Label => {
136 if label.is_some() {
137 return Err(DeError::duplicate_field("label"));
138 }
139
140 label = Some(map.next_value()?);
141 }
142 ActivityButtonField::Url => {
143 if url.is_some() {
144 return Err(DeError::duplicate_field("url"));
145 }
146
147 url = Some(map.next_value()?);
148 }
149 }
150 }
151
152 let label = label.ok_or_else(|| DeError::missing_field("label"))?;
153 let url = url.ok_or_else(|| DeError::missing_field("url"))?;
154
155 Ok(ActivityButton::Link(ActivityButtonLink { label, url }))
156 }
157}
158
159impl<'de> Deserialize<'de> for ActivityButton {
160 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
161 deserializer.deserialize_any(ActivityButtonVisitor)
162 }
163}
164
165impl Serialize for ActivityButton {
166 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
167 match self {
168 Self::Link(link) => {
169 let mut state = serializer.serialize_struct("ActivityButton", 2)?;
170
171 state.serialize_field("label", &link.label)?;
172 state.serialize_field("url", &link.url)?;
173
174 state.end()
175 }
176 Self::Text(text) => serializer.serialize_str(&text.label),
177 Self::Unknown => Err(serde::ser::Error::custom(
178 "Can't serialize an unknown activity button type",
179 )),
180 }
181 }
182}
183
184#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
186pub struct ActivityButtonLink {
187 pub label: String,
189 pub url: String,
191}
192
193#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
212#[serde(transparent)]
213pub struct ActivityButtonText {
214 pub label: String,
216}
217
218#[cfg(test)]
219mod tests {
220 use super::{ActivityButton, ActivityButtonLink, ActivityButtonText};
221 use serde::{Deserialize, Serialize};
222 use serde_test::Token;
223 use static_assertions::{assert_fields, assert_impl_all};
224 use std::fmt::Debug;
225
226 assert_fields!(ActivityButtonLink: label, url);
227 assert_impl_all!(
228 ActivityButtonLink: Clone,
229 Debug,
230 Deserialize<'static>,
231 Eq,
232 PartialEq,
233 Serialize
234 );
235 assert_fields!(ActivityButtonText: label);
236 assert_impl_all!(
237 ActivityButtonText: Clone,
238 Debug,
239 Deserialize<'static>,
240 Eq,
241 PartialEq,
242 Serialize
243 );
244 assert_impl_all!(
245 ActivityButton: Clone,
246 Debug,
247 Deserialize<'static>,
248 Eq,
249 PartialEq,
250 Serialize
251 );
252
253 fn link() -> ActivityButtonLink {
254 ActivityButtonLink {
255 label: "a".to_owned(),
256 url: "b".to_owned(),
257 }
258 }
259
260 fn text() -> ActivityButtonText {
261 ActivityButtonText {
262 label: "a".to_owned(),
263 }
264 }
265
266 #[test]
267 fn activity_button_link() {
268 serde_test::assert_de_tokens(
269 &link(),
270 &[
271 Token::Struct {
272 name: "ActivityButtonLink",
273 len: 2,
274 },
275 Token::Str("label"),
276 Token::Str("a"),
277 Token::Str("url"),
278 Token::Str("b"),
279 Token::StructEnd,
280 ],
281 );
282 }
283
284 #[test]
285 fn activity_button_text() {
286 serde_test::assert_de_tokens(&text(), &[Token::Str("a")]);
287 }
288
289 #[test]
290 fn activity_button_with_url() {
291 serde_test::assert_tokens(
292 &ActivityButton::Link(link()),
293 &[
294 Token::Struct {
295 name: "ActivityButton",
296 len: 2,
297 },
298 Token::Str("label"),
299 Token::Str("a"),
300 Token::Str("url"),
301 Token::Str("b"),
302 Token::StructEnd,
303 ],
304 );
305 }
306
307 #[test]
308 fn activity_button_without_url() {
309 serde_test::assert_tokens(&ActivityButton::Text(text()), &[Token::Str("a")]);
310 }
311}