1use crate::responses::errors::Errors;
2use crate::{
3 api::{apply_options, execute_twitter, make_url, Authentication, TwapiOptions},
4 error::Error,
5 headers::Headers,
6};
7use reqwest::RequestBuilder;
8use serde::{Deserialize, Serialize};
9
10const URL: &str = "/2/tweets";
11
12#[derive(Serialize, Deserialize, Debug, Default, Clone)]
13pub struct Geo {
14 pub place_id: String,
15}
16
17#[derive(Serialize, Deserialize, Debug, Default, Clone)]
18pub struct Media {
19 pub media_ids: Vec<String>,
20 pub tagged_user_ids: Vec<String>,
21}
22
23#[derive(Serialize, Deserialize, Debug, Default, Clone)]
24pub struct Poll {
25 pub duration_minutes: String,
26 pub options: Vec<String>,
27}
28
29#[derive(Serialize, Deserialize, Debug, Default, Clone)]
30pub struct Reply {
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub exclude_reply_user_ids: Option<Vec<String>>,
33 pub in_reply_to_tweet_id: String,
34}
35
36#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Clone)]
37pub enum ReplySettings {
38 #[serde(rename = "mentionedUsers")]
39 Mentionedusers,
40 #[serde(rename = "following")]
41 Following,
42}
43
44impl std::fmt::Display for ReplySettings {
45 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
46 match self {
47 Self::Mentionedusers => write!(f, "mentionedUsers"),
48 Self::Following => write!(f, "following"),
49 }
50 }
51}
52
53impl Default for ReplySettings {
54 fn default() -> Self {
55 Self::Mentionedusers
56 }
57}
58
59#[derive(Serialize, Deserialize, Debug, Default, Clone)]
60pub struct Body {
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub direct_message_deep_link: Option<String>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub for_super_followers_only: Option<bool>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub geo: Option<Geo>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub media: Option<Media>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub poll: Option<Poll>,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub quote_tweet_id: Option<String>,
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub reply: Option<Reply>,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub reply_settings: Option<ReplySettings>,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub text: Option<String>,
79}
80
81#[derive(Debug, Clone, Default)]
82pub struct Api {
83 body: Body,
84 twapi_options: Option<TwapiOptions>,
85}
86
87impl Api {
88 pub fn new(body: Body) -> Self {
89 Self {
90 body,
91 ..Default::default()
92 }
93 }
94
95 pub fn twapi_options(mut self, value: TwapiOptions) -> Self {
96 self.twapi_options = Some(value);
97 self
98 }
99
100 pub fn build(self, authentication: &impl Authentication) -> RequestBuilder {
101 let client = reqwest::Client::new();
102 let url = make_url(&self.twapi_options, URL);
103 let builder = client.post(&url).json(&self.body);
104 authentication.execute(
105 apply_options(builder, &self.twapi_options),
106 "POST",
107 &url,
108 &[],
109 )
110 }
111
112 pub async fn execute(
113 self,
114 authentication: &impl Authentication,
115 ) -> Result<(Response, Headers), Error> {
116 execute_twitter(self.build(authentication)).await
117 }
118}
119
120#[derive(Serialize, Deserialize, Debug, Clone, Default)]
121pub struct Response {
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub data: Option<Data>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub errors: Option<Vec<Errors>>,
126 #[serde(flatten)]
127 pub extra: std::collections::HashMap<String, serde_json::Value>,
128}
129
130impl Response {
131 pub fn is_empty_extra(&self) -> bool {
132 let res = self.extra.is_empty()
133 && self
134 .data
135 .as_ref()
136 .map(|it| it.is_empty_extra())
137 .unwrap_or(true)
138 && self
139 .errors
140 .as_ref()
141 .map(|it| it.iter().all(|item| item.is_empty_extra()))
142 .unwrap_or(true);
143 if !res {
144 println!("Response {:?}", self.extra);
145 }
146 res
147 }
148}
149
150#[derive(Serialize, Deserialize, Debug, Clone, Default)]
151pub struct Data {
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub id: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub text: Option<String>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub edit_history_tweet_ids: Option<Vec<String>>,
158 #[serde(flatten)]
159 pub extra: std::collections::HashMap<String, serde_json::Value>,
160}
161
162impl Data {
163 pub fn is_empty_extra(&self) -> bool {
164 let res = self.extra.is_empty();
165 if !res {
166 println!("Data {:?}", self.extra);
167 }
168 res
169 }
170}