1use crate::api_result::{ApiPayload, ApiResponse, ApiResponseExt, ApiResult};
2use crate::authorization::Authorization;
3use crate::error::Result;
4use crate::utils::JsonStream;
5use futures::prelude::*;
6use reqwest::header::AUTHORIZATION;
7use reqwest::{Client, IntoUrl, Method, Url};
8use serde::de::DeserializeOwned;
9use std::sync::Arc;
10
11#[derive(Debug)]
12pub struct TwitterApi<A> {
13 client: Client,
14 base_url: Url,
15 auth: Arc<A>,
16}
17
18impl<A> TwitterApi<A>
19where
20 A: Authorization,
21{
22 pub fn new(auth: A) -> Self {
23 Self {
24 client: Client::builder().pool_max_idle_per_host(0).build().unwrap(),
25 base_url: Url::parse("https://api.twitter.com/2/").unwrap(),
26 auth: Arc::new(auth),
27 }
28 }
29
30 pub fn auth(&self) -> &A {
31 &self.auth
32 }
33
34 pub(crate) fn url(&self, url: impl AsRef<str>) -> Result<Url> {
35 Ok(self.base_url.join(url.as_ref())?)
36 }
37
38 pub(crate) fn request(&self, method: Method, url: impl IntoUrl) -> reqwest::RequestBuilder {
39 self.client.request(method, url)
40 }
41
42 pub(crate) async fn send<T: DeserializeOwned, M: DeserializeOwned>(
43 &self,
44 req: reqwest::RequestBuilder,
45 ) -> ApiResult<A, T, M> {
46 let mut req = req.build()?;
47 let authorization = self.auth.header(&req).await?;
48 let _ = req.headers_mut().insert(AUTHORIZATION, authorization);
49 let url = req.url().clone();
50 let response = self
51 .client
52 .execute(req)
53 .await?
54 .api_error_for_status()
55 .await?
56 .json()
57 .await?;
58 Ok(ApiResponse::new(self, url, response))
59 }
60
61 pub(crate) async fn stream<T: DeserializeOwned, M: DeserializeOwned>(
62 &self,
63 req: reqwest::RequestBuilder,
64 ) -> Result<impl Stream<Item = Result<ApiPayload<T, M>>>> {
65 let mut req = req.build()?;
66 let authorization = self.auth.header(&req).await?;
67 let _ = req.headers_mut().insert(AUTHORIZATION, authorization);
68 Ok(JsonStream::new(
69 self.client
70 .execute(req)
71 .await?
72 .api_error_for_status()
73 .await?
74 .bytes_stream(),
75 ))
76 }
77}
78
79impl<A> Clone for TwitterApi<A> {
80 fn clone(&self) -> Self {
81 Self {
82 client: self.client.clone(),
83 base_url: self.base_url.clone(),
84 auth: self.auth.clone(),
85 }
86 }
87}