1use serde::{Deserialize, Serialize};
2
3use super::ids::{ContainerId, PostId, UserId};
4use super::time::ThreadsTime;
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub enum ReplyControl {
9 #[serde(rename = "everyone")]
11 Everyone,
12 #[serde(rename = "accounts_you_follow")]
14 AccountsYouFollow,
15 #[serde(rename = "mentioned_only")]
17 MentionedOnly,
18 #[serde(rename = "parent_post_author_only")]
20 ParentPostAuthorOnly,
21 #[serde(rename = "followers_only")]
23 FollowersOnly,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub enum ApprovalStatus {
29 #[serde(rename = "pending")]
31 Pending,
32 #[serde(rename = "ignored")]
34 Ignored,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub enum SearchType {
40 #[serde(rename = "TOP")]
42 Top,
43 #[serde(rename = "RECENT")]
45 Recent,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50pub enum SearchMode {
51 #[serde(rename = "KEYWORD")]
53 Keyword,
54 #[serde(rename = "TAG")]
56 Tag,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub enum GifProvider {
62 #[serde(rename = "TENOR")]
64 Tenor,
65 #[serde(rename = "GIPHY")]
67 Giphy,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74pub enum ReplyAudience {
75 #[serde(rename = "EVERYONE")]
77 Everyone,
78 #[serde(rename = "ACCOUNTS_YOU_FOLLOW")]
80 AccountsYouFollow,
81 #[serde(rename = "MENTIONED_ONLY")]
83 MentionedOnly,
84 #[serde(rename = "PARENT_POST_AUTHOR_ONLY")]
86 ParentPostAuthorOnly,
87 #[serde(rename = "FOLLOWERS_ONLY")]
89 FollowersOnly,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
94pub enum HideStatus {
95 #[serde(rename = "NOT_HUSHED")]
97 NotHushed,
98 #[serde(rename = "UNHUSHED")]
100 Unhushed,
101 #[serde(rename = "HIDDEN")]
103 Hidden,
104 #[serde(rename = "COVERED")]
106 Covered,
107 #[serde(rename = "BLOCKED")]
109 Blocked,
110 #[serde(rename = "RESTRICTED")]
112 Restricted,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub enum MediaType {
118 #[serde(rename = "TEXT")]
120 Text,
121 #[serde(rename = "IMAGE")]
123 Image,
124 #[serde(rename = "VIDEO")]
126 Video,
127 #[serde(rename = "AUDIO")]
129 Audio,
130 #[serde(rename = "CAROUSEL")]
132 Carousel,
133 #[serde(rename = "CAROUSEL_ALBUM")]
135 CarouselAlbum,
136 #[serde(rename = "REPOST_FACADE")]
138 RepostFacade,
139 #[serde(rename = "TEXT_POST")]
141 TextPost,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct PollAttachment {
147 pub option_a: String,
149 pub option_b: String,
151 #[serde(default, skip_serializing_if = "Option::is_none")]
153 pub option_c: Option<String>,
154 #[serde(default, skip_serializing_if = "Option::is_none")]
156 pub option_d: Option<String>,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct PollResult {
162 pub option_a: String,
164 pub option_b: String,
166 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub option_c: Option<String>,
169 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub option_d: Option<String>,
172 pub option_a_votes_percentage: f64,
174 pub option_b_votes_percentage: f64,
176 #[serde(default)]
178 pub option_c_votes_percentage: f64,
179 #[serde(default)]
181 pub option_d_votes_percentage: f64,
182 pub total_votes: i64,
184 pub expiration_timestamp: ThreadsTime,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct TextEntity {
191 pub entity_type: String,
193 pub offset: usize,
195 pub length: usize,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct TextAttachment {
202 pub plaintext: String,
204 #[serde(default, skip_serializing_if = "Option::is_none")]
206 pub link_attachment_url: Option<String>,
207 #[serde(default, skip_serializing_if = "Option::is_none")]
209 pub text_with_styling_info: Option<Vec<TextStylingInfo>>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct TextStylingInfo {
215 pub offset: usize,
217 pub length: usize,
219 pub styling_info: Vec<String>,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct GifAttachment {
226 pub gif_id: String,
228 pub provider: GifProvider,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct PostOwner {
235 pub id: UserId,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct ChildrenData {
242 pub data: Vec<ChildPost>,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct ChildPost {
249 pub id: PostId,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct ContainerStatus {
256 pub id: ContainerId,
258 pub status: String,
260 #[serde(default, skip_serializing_if = "Option::is_none")]
262 pub error_message: Option<String>,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct RepostContent {
268 pub post_id: PostId,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct QuotaConfig {
275 pub quota_total: i64,
277 pub quota_duration: i64,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct TextEntitiesResponse {
284 pub data: Vec<TextEntity>,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct RecentSearch {
291 pub query: String,
293 pub timestamp: i64,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct PublishingLimits {
300 pub quota_usage: i64,
302 pub config: QuotaConfig,
304 pub reply_quota_usage: i64,
306 pub reply_config: QuotaConfig,
308 pub delete_quota_usage: i64,
310 pub delete_config: QuotaConfig,
312 pub location_search_quota_usage: i64,
314 pub location_search_config: QuotaConfig,
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 #[test]
323 fn test_reply_control_serde() {
324 let rc = ReplyControl::AccountsYouFollow;
325 let json = serde_json::to_string(&rc).unwrap();
326 assert_eq!(json, r#""accounts_you_follow""#);
327 let back: ReplyControl = serde_json::from_str(&json).unwrap();
328 assert_eq!(back, ReplyControl::AccountsYouFollow);
329 }
330
331 #[test]
332 fn test_media_type_serde() {
333 let mt = MediaType::Carousel;
334 let json = serde_json::to_string(&mt).unwrap();
335 assert_eq!(json, r#""CAROUSEL""#);
336 let back: MediaType = serde_json::from_str(&json).unwrap();
337 assert_eq!(back, MediaType::Carousel);
338 }
339
340 #[test]
341 fn test_search_type_serde() {
342 let st = SearchType::Recent;
343 let json = serde_json::to_string(&st).unwrap();
344 assert_eq!(json, r#""RECENT""#);
345 }
346
347 #[test]
348 fn test_search_mode_serde() {
349 let sm = SearchMode::Tag;
350 let json = serde_json::to_string(&sm).unwrap();
351 assert_eq!(json, r#""TAG""#);
352 }
353
354 #[test]
355 fn test_gif_provider_serde() {
356 let gp = GifProvider::Giphy;
357 let json = serde_json::to_string(&gp).unwrap();
358 assert_eq!(json, r#""GIPHY""#);
359 }
360
361 #[test]
362 fn test_approval_status_serde() {
363 let s = ApprovalStatus::Pending;
364 let json = serde_json::to_string(&s).unwrap();
365 assert_eq!(json, r#""pending""#);
366 }
367
368 #[test]
369 fn test_poll_attachment_serde() {
370 let poll = PollAttachment {
371 option_a: "Yes".into(),
372 option_b: "No".into(),
373 option_c: None,
374 option_d: None,
375 };
376 let json = serde_json::to_string(&poll).unwrap();
377 assert!(!json.contains("option_c"));
378 let back: PollAttachment = serde_json::from_str(&json).unwrap();
379 assert_eq!(back.option_a, "Yes");
380 }
381
382 #[test]
383 fn test_reply_audience_serde() {
384 let ra = ReplyAudience::AccountsYouFollow;
385 let json = serde_json::to_string(&ra).unwrap();
386 assert_eq!(json, r#""ACCOUNTS_YOU_FOLLOW""#);
387 let back: ReplyAudience = serde_json::from_str(&json).unwrap();
388 assert_eq!(back, ReplyAudience::AccountsYouFollow);
389 }
390
391 #[test]
392 fn test_hide_status_serde() {
393 let hs = HideStatus::Hidden;
394 let json = serde_json::to_string(&hs).unwrap();
395 assert_eq!(json, r#""HIDDEN""#);
396 let back: HideStatus = serde_json::from_str(&json).unwrap();
397 assert_eq!(back, HideStatus::Hidden);
398 }
399
400 #[test]
401 fn test_media_type_audio_serde() {
402 let mt = MediaType::Audio;
403 let json = serde_json::to_string(&mt).unwrap();
404 assert_eq!(json, r#""AUDIO""#);
405 let back: MediaType = serde_json::from_str(&json).unwrap();
406 assert_eq!(back, MediaType::Audio);
407 }
408
409 #[test]
410 fn test_media_type_text_post_serde() {
411 let mt = MediaType::TextPost;
412 let json = serde_json::to_string(&mt).unwrap();
413 assert_eq!(json, r#""TEXT_POST""#);
414 let back: MediaType = serde_json::from_str(&json).unwrap();
415 assert_eq!(back, MediaType::TextPost);
416 }
417
418 #[test]
419 fn test_text_entities_response_serde() {
420 let resp = TextEntitiesResponse {
421 data: vec![TextEntity {
422 entity_type: "SPOILER".into(),
423 offset: 0,
424 length: 5,
425 }],
426 };
427 let json = serde_json::to_string(&resp).unwrap();
428 let back: TextEntitiesResponse = serde_json::from_str(&json).unwrap();
429 assert_eq!(back.data.len(), 1);
430 assert_eq!(back.data[0].entity_type, "SPOILER");
431 }
432
433 #[test]
434 fn test_recent_search_serde() {
435 let rs = RecentSearch {
436 query: "rust".into(),
437 timestamp: 1700000000,
438 };
439 let json = serde_json::to_string(&rs).unwrap();
440 let back: RecentSearch = serde_json::from_str(&json).unwrap();
441 assert_eq!(back.query, "rust");
442 assert_eq!(back.timestamp, 1700000000);
443 }
444
445 #[test]
446 fn test_publishing_limits_deserialize() {
447 let json = r#"{
448 "quota_usage": 10,
449 "config": {"quota_total": 100, "quota_duration": 86400},
450 "reply_quota_usage": 5,
451 "reply_config": {"quota_total": 50, "quota_duration": 86400},
452 "delete_quota_usage": 2,
453 "delete_config": {"quota_total": 25, "quota_duration": 86400},
454 "location_search_quota_usage": 1,
455 "location_search_config": {"quota_total": 10, "quota_duration": 86400}
456 }"#;
457 let limits: PublishingLimits = serde_json::from_str(json).unwrap();
458 assert_eq!(limits.quota_usage, 10);
459 assert_eq!(limits.location_search_quota_usage, 1);
460 }
461
462 #[test]
463 fn test_container_status_serde() {
464 let cs = ContainerStatus {
465 id: ContainerId::from("123"),
466 status: "FINISHED".into(),
467 error_message: None,
468 };
469 let json = serde_json::to_string(&cs).unwrap();
470 assert!(!json.contains("error_message"));
471 }
472}