1use crate::api::TwitterApi;
2use crate::authorization::Authorization;
3use crate::data::Expansions;
4use crate::error::{Error, Result};
5use crate::meta::PaginationMeta;
6use crate::query::UrlQueryExt;
7use async_trait::async_trait;
8use reqwest::{Method, Response, StatusCode};
9use serde::{de::DeserializeOwned, Deserialize, Serialize};
10use std::collections::HashMap;
11use std::{fmt, ops};
12use url::Url;
13
14#[derive(Deserialize, Serialize, Debug, Clone)]
15pub struct ApiPayload<T, M> {
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub data: Option<T>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub meta: Option<M>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub includes: Option<Expansions>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub errors: Option<Vec<ApiError>>,
24}
25
26impl<T, M> ApiPayload<T, M> {
27 pub fn data(&self) -> Option<&T> {
28 self.data.as_ref()
29 }
30 pub fn meta(&self) -> Option<&M> {
31 self.meta.as_ref()
32 }
33 pub fn includes(&self) -> Option<&Expansions> {
34 self.includes.as_ref()
35 }
36 pub fn errors(&self) -> Option<&[ApiError]> {
37 self.errors.as_deref()
38 }
39 pub fn into_data(self) -> Option<T> {
40 self.data
41 }
42 pub fn into_meta(self) -> Option<M> {
43 self.meta
44 }
45 pub fn into_includes(self) -> Option<Expansions> {
46 self.includes
47 }
48 pub fn into_errors(self) -> Option<Vec<ApiError>> {
49 self.errors
50 }
51}
52
53#[derive(Deserialize, Serialize, Debug, Clone)]
54pub struct ApiErrorItem {
55 parameters: HashMap<String, Vec<serde_json::Value>>,
56 message: String,
57}
58
59#[derive(Deserialize, Serialize, Debug, Clone, Default)]
60pub struct ApiError {
61 pub title: String,
62 #[serde(rename = "type")]
63 pub kind: String,
64 #[serde(default, with = "crate::utils::serde::status_code")]
65 pub status: StatusCode,
66 #[serde(default)]
67 pub detail: String,
68 #[serde(default)]
69 pub errors: Vec<ApiErrorItem>,
70}
71
72impl fmt::Display for ApiError {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.write_fmt(format_args!("[{}] {}", self.status, self.detail))
75 }
76}
77
78impl std::error::Error for ApiError {}
79
80#[derive(Debug)]
81pub struct ApiResponse<A, T, M> {
82 client: TwitterApi<A>,
83 url: Url,
84 payload: ApiPayload<T, M>,
85}
86
87impl<A, T, M> ApiResponse<A, T, M> {
88 pub(crate) fn new(client: &TwitterApi<A>, url: Url, payload: ApiPayload<T, M>) -> Self {
89 Self {
90 client: client.clone(),
91 url,
92 payload,
93 }
94 }
95 pub fn client(&self) -> &TwitterApi<A> {
96 &self.client
97 }
98 pub fn url(&self) -> &Url {
99 &self.url
100 }
101 pub fn payload(&self) -> &ApiPayload<T, M> {
102 &self.payload
103 }
104 pub fn into_payload(self) -> ApiPayload<T, M> {
105 self.payload
106 }
107 pub fn into_data(self) -> Option<T> {
108 self.payload.data
109 }
110 pub fn into_meta(self) -> Option<M> {
111 self.payload.meta
112 }
113 pub fn into_includes(self) -> Option<Expansions> {
114 self.payload.includes
115 }
116 pub fn into_errors(self) -> Option<Vec<ApiError>> {
117 self.payload.errors
118 }
119}
120
121impl<A, T, M> ops::Deref for ApiResponse<A, T, M> {
122 type Target = ApiPayload<T, M>;
123 fn deref(&self) -> &Self::Target {
124 &self.payload
125 }
126}
127
128impl<A, T, M> Serialize for ApiResponse<A, T, M>
129where
130 T: Serialize,
131 M: Serialize,
132{
133 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
134 where
135 S: serde::Serializer,
136 {
137 self.payload.serialize(serializer)
138 }
139}
140
141impl<A, T, M> Clone for ApiResponse<A, T, M>
142where
143 T: Clone,
144 M: Clone,
145{
146 fn clone(&self) -> Self {
147 Self {
148 client: self.client.clone(),
149 url: self.url.clone(),
150 payload: self.payload.clone(),
151 }
152 }
153}
154
155#[async_trait]
156pub trait PaginableApiResponse: Sized {
157 async fn next_page(&self) -> Result<Option<Self>>;
158 async fn previous_page(&self) -> Result<Option<Self>>;
159}
160
161#[async_trait]
162impl<A, T, M> PaginableApiResponse for ApiResponse<A, T, M>
163where
164 A: Authorization + Send + Sync,
165 T: DeserializeOwned + Send + Sync,
166 M: PaginationMeta + DeserializeOwned + Send + Sync,
167{
168 async fn next_page(&self) -> Result<Option<Self>> {
169 if let Some(token) = self.meta().and_then(|m| m.next_token()) {
170 let mut url = self.url.clone();
171 url.replace_query_val("pagination_token", token);
172 Ok(Some(
173 self.client
174 .send(self.client.request(Method::GET, url))
175 .await?,
176 ))
177 } else {
178 Ok(None)
179 }
180 }
181 async fn previous_page(&self) -> Result<Option<Self>> {
182 if let Some(token) = self.meta().and_then(|m| m.previous_token()) {
183 let mut url = self.url.clone();
184 url.replace_query_val("pagination_token", token);
185 Ok(Some(
186 self.client
187 .send(self.client.request(Method::GET, url))
188 .await?,
189 ))
190 } else {
191 Ok(None)
192 }
193 }
194}
195
196pub type ApiResult<A, T, M> = Result<ApiResponse<A, T, M>>;
197
198#[async_trait]
199pub(crate) trait ApiResponseExt: Sized {
200 async fn api_error_for_status(self) -> Result<Self>;
201}
202
203#[async_trait]
204impl ApiResponseExt for Response {
205 async fn api_error_for_status(self) -> Result<Self> {
206 let status = self.status();
207 if status.is_success() {
208 Ok(self)
209 } else {
210 let text = self.text().await?;
211 Err(Error::Api(
212 if let Ok(mut error) = serde_json::from_str::<ApiError>(&text) {
213 error.status = status;
214 error
215 } else {
216 ApiError {
217 status,
218 detail: text,
219 ..Default::default()
220 }
221 },
222 ))
223 }
224 }
225}