1use crate::id::{
2 marker::{EmojiMarker, TagMarker},
3 Id,
4};
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
11pub struct DefaultReaction {
12 pub emoji_id: Option<Id<EmojiMarker>>,
16 pub emoji_name: Option<String>,
20}
21
22#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
27#[non_exhaustive]
28#[serde(from = "u8", into = "u8")]
29pub enum ForumLayout {
30 GalleryView,
32 ListView,
34 NotSet,
36 Unknown(u8),
38}
39
40impl ForumLayout {
41 pub const fn name(self) -> &'static str {
42 match self {
43 Self::ListView => "ListView",
44 Self::NotSet => "NotSet",
45 Self::GalleryView => "GalleryView",
46 Self::Unknown(_) => "Unknown",
47 }
48 }
49}
50
51impl From<u8> for ForumLayout {
52 fn from(value: u8) -> Self {
53 match value {
54 0 => Self::NotSet,
55 1 => Self::ListView,
56 2 => Self::GalleryView,
57 unknown => Self::Unknown(unknown),
58 }
59 }
60}
61
62impl From<ForumLayout> for u8 {
63 fn from(value: ForumLayout) -> Self {
64 match value {
65 ForumLayout::NotSet => 0,
66 ForumLayout::ListView => 1,
67 ForumLayout::GalleryView => 2,
68 ForumLayout::Unknown(unknown) => unknown,
69 }
70 }
71}
72
73#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
78#[non_exhaustive]
79#[serde(from = "u8", into = "u8")]
80pub enum ForumSortOrder {
81 CreationDate,
83 LatestActivity,
85 Unknown(u8),
87}
88
89impl ForumSortOrder {
90 pub const fn name(self) -> &'static str {
91 match self {
92 Self::CreationDate => "CreationDate",
93 Self::LatestActivity => "LatestActivity",
94 Self::Unknown(_) => "Unknown",
95 }
96 }
97}
98
99impl From<u8> for ForumSortOrder {
100 fn from(value: u8) -> Self {
101 match value {
102 0 => Self::LatestActivity,
103 1 => Self::CreationDate,
104 unknown => Self::Unknown(unknown),
105 }
106 }
107}
108
109impl From<ForumSortOrder> for u8 {
110 fn from(value: ForumSortOrder) -> Self {
111 match value {
112 ForumSortOrder::LatestActivity => 0,
113 ForumSortOrder::CreationDate => 1,
114 ForumSortOrder::Unknown(unknown) => unknown,
115 }
116 }
117}
118
119#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
126pub struct ForumTag {
127 #[serde(with = "crate::visitor::zeroable_id")]
134 pub emoji_id: Option<Id<EmojiMarker>>,
135 pub emoji_name: Option<String>,
139 pub id: Id<TagMarker>,
141 pub moderated: bool,
147 pub name: String,
149}
150
151#[cfg(test)]
152mod tests {
153 use super::{DefaultReaction, ForumLayout, ForumSortOrder, ForumTag};
154 use crate::id::{
155 marker::{EmojiMarker, TagMarker},
156 Id,
157 };
158 use serde::{Deserialize, Serialize};
159 use serde_test::{assert_tokens, Token};
160 use static_assertions::assert_impl_all;
161 use std::{fmt::Debug, hash::Hash};
162
163 assert_impl_all!(
164 ForumLayout: Clone,
165 Copy,
166 Debug,
167 Deserialize<'static>,
168 Eq,
169 Hash,
170 PartialEq,
171 Send,
172 Serialize,
173 Sync
174 );
175 assert_impl_all!(
176 ForumSortOrder: Clone,
177 Copy,
178 Debug,
179 Deserialize<'static>,
180 Eq,
181 Hash,
182 PartialEq,
183 Send,
184 Serialize,
185 Sync
186 );
187
188 const EMOJI_ID: Id<EmojiMarker> = Id::new(1);
189 const TAG_ID: Id<TagMarker> = Id::new(2);
190
191 #[test]
192 fn default_reaction() {
193 let value = DefaultReaction {
194 emoji_id: None,
195 emoji_name: Some("name".to_owned()),
196 };
197
198 serde_test::assert_tokens(
199 &value,
200 &[
201 Token::Struct {
202 name: "DefaultReaction",
203 len: 2,
204 },
205 Token::Str("emoji_id"),
206 Token::None,
207 Token::Str("emoji_name"),
208 Token::Some,
209 Token::Str("name"),
210 Token::StructEnd,
211 ],
212 );
213 }
214
215 #[test]
216 fn forum_layout() {
217 const MAP: &[(ForumLayout, u8, &str)] = &[
218 (ForumLayout::NotSet, 0, "NotSet"),
219 (ForumLayout::ListView, 1, "ListView"),
220 (ForumLayout::GalleryView, 2, "GalleryView"),
221 (ForumLayout::Unknown(3), 3, "Unknown"),
222 ];
223
224 for (layout, number, name) in MAP {
225 assert_eq!(layout.name(), *name);
226 assert_eq!(u8::from(*layout), *number);
227 assert_eq!(ForumLayout::from(*number), *layout);
228 assert_tokens(layout, &[Token::U8(*number)]);
229 }
230 }
231
232 #[test]
233 fn forum_sort_order() {
234 const MAP: &[(ForumSortOrder, u8, &str)] = &[
235 (ForumSortOrder::LatestActivity, 0, "LatestActivity"),
236 (ForumSortOrder::CreationDate, 1, "CreationDate"),
237 (ForumSortOrder::Unknown(100), 100, "Unknown"),
238 ];
239
240 for (layout, number, name) in MAP {
241 assert_eq!(layout.name(), *name);
242 assert_eq!(u8::from(*layout), *number);
243 assert_eq!(ForumSortOrder::from(*number), *layout);
244 assert_tokens(layout, &[Token::U8(*number)]);
245 }
246 }
247
248 #[test]
251 fn forum_tag_emoji_name() {
252 let value = ForumTag {
253 emoji_id: None,
254 emoji_name: Some("emoji".to_owned()),
255 id: TAG_ID,
256 moderated: true,
257 name: "tag".into(),
258 };
259
260 serde_test::assert_tokens(
261 &value,
262 &[
263 Token::Struct {
264 name: "ForumTag",
265 len: 5,
266 },
267 Token::Str("emoji_id"),
268 Token::None,
269 Token::Str("emoji_name"),
270 Token::Some,
271 Token::Str("emoji"),
272 Token::Str("id"),
273 Token::NewtypeStruct { name: "Id" },
274 Token::Str("2"),
275 Token::Str("moderated"),
276 Token::Bool(true),
277 Token::Str("name"),
278 Token::Str("tag"),
279 Token::StructEnd,
280 ],
281 );
282 }
283
284 #[test]
285 fn forum_tag() {
286 let value = ForumTag {
287 emoji_id: Some(EMOJI_ID),
288 emoji_name: None,
289 id: TAG_ID,
290 moderated: false,
291 name: "other".into(),
292 };
293
294 serde_test::assert_tokens(
295 &value,
296 &[
297 Token::Struct {
298 name: "ForumTag",
299 len: 5,
300 },
301 Token::Str("emoji_id"),
302 Token::Some,
303 Token::NewtypeStruct { name: "Id" },
304 Token::Str("1"),
305 Token::Str("emoji_name"),
306 Token::None,
307 Token::Str("id"),
308 Token::NewtypeStruct { name: "Id" },
309 Token::Str("2"),
310 Token::Str("moderated"),
311 Token::Bool(false),
312 Token::Str("name"),
313 Token::Str("other"),
314 Token::StructEnd,
315 ],
316 );
317 }
318
319 #[test]
324 fn forum_tag_emoji_id_zero() {
325 let value = ForumTag {
326 emoji_id: None,
327 emoji_name: None,
328 id: TAG_ID,
329 moderated: true,
330 name: "tag".into(),
331 };
332
333 serde_test::assert_de_tokens(
334 &value,
335 &[
336 Token::Struct {
337 name: "ForumTag",
338 len: 5,
339 },
340 Token::Str("emoji_id"),
341 Token::U64(0),
342 Token::Str("emoji_name"),
343 Token::None,
344 Token::Str("id"),
345 Token::NewtypeStruct { name: "Id" },
346 Token::Str("2"),
347 Token::Str("moderated"),
348 Token::Bool(true),
349 Token::Str("name"),
350 Token::Str("tag"),
351 Token::StructEnd,
352 ],
353 );
354
355 serde_test::assert_de_tokens(
356 &value,
357 &[
358 Token::Struct {
359 name: "ForumTag",
360 len: 5,
361 },
362 Token::Str("emoji_id"),
363 Token::Unit,
364 Token::Str("emoji_name"),
365 Token::None,
366 Token::Str("id"),
367 Token::NewtypeStruct { name: "Id" },
368 Token::Str("2"),
369 Token::Str("moderated"),
370 Token::Bool(true),
371 Token::Str("name"),
372 Token::Str("tag"),
373 Token::StructEnd,
374 ],
375 );
376 }
377}