telexide_fork/api/
api_client.rs1use super::{api::API, endpoints::APIEndpoint, response::Response};
2use crate::utils::{
3 encode_multipart_form_data, result::Result, AsFormData, FormDataFile, BOUNDARY,
4};
5use async_trait::async_trait;
6use hyper::{body::HttpBody, client::HttpConnector, Body, Client, Request};
7use std::io::Write;
8
9static TELEGRAM_API: &str = "https://api.telegram.org/bot";
10
11pub struct APIClient {
36 hyper_client: Client<hyper_tls::HttpsConnector<HttpConnector>>,
37 token: String,
38}
39
40impl APIClient {
41 pub fn new<T: ToString>(
44 hyper_client: Option<Client<hyper_tls::HttpsConnector<HttpConnector>>>,
45 token: &T,
46 ) -> Self {
47 hyper_client.map_or_else(
48 || Self {
49 hyper_client: hyper::Client::builder().build(hyper_tls::HttpsConnector::new()),
50 token: token.to_string(),
51 },
52 |c| Self {
53 hyper_client: c,
54 token: token.to_string(),
55 },
56 )
57 }
58
59 pub fn new_default<T: ToString>(token: &T) -> Self {
62 Self {
63 hyper_client: hyper::Client::builder().build(hyper_tls::HttpsConnector::new()),
64 token: token.to_string(),
65 }
66 }
67
68 fn parse_endpoint(&self, endpoint: &APIEndpoint) -> String {
69 format!("{}{}/{}", TELEGRAM_API, self.token, endpoint)
70 }
71
72 pub async fn request<D>(&self, endpoint: APIEndpoint, data: Option<&D>) -> Result<Response>
75 where
76 D: ?Sized + serde::Serialize,
77 {
78 let data: Option<serde_json::Value> = if let Some(d) = data {
79 Some(serde_json::to_value(d)?)
80 } else {
81 None
82 };
83
84 match endpoint {
85 e if e.as_str().starts_with("get") => self.get(e, data).await,
86 e => self.post(e, data).await,
87 }
88 }
89
90 pub fn get_hyper(&self) -> &Client<hyper_tls::HttpsConnector<HttpConnector>> {
93 &self.hyper_client
94 }
95}
96
97#[async_trait]
98impl API for APIClient {
99 async fn get(
100 &self,
101 endpoint: APIEndpoint,
102 data: Option<serde_json::Value>,
103 ) -> Result<Response> {
104 let req_builder = Request::get(self.parse_endpoint(&endpoint))
105 .header("content-type", "application/json")
106 .header("accept", "application/json");
107
108 let request = if let Some(d) = data {
109 req_builder.body(Body::from(serde_json::to_string(&d)?))?
110 } else {
111 req_builder.body(Body::empty())?
112 };
113
114 log::debug!("GET request to {}", &endpoint);
115 let mut response = self.hyper_client.request(request).await?;
116
117 let mut res: Vec<u8> = Vec::new();
118 while let Some(chunk) = response.body_mut().data().await {
119 res.write_all(&chunk?)?;
120 }
121
122 Ok(serde_json::from_slice(&res)?)
123 }
124
125 async fn post(
126 &self,
127 endpoint: APIEndpoint,
128 data: Option<serde_json::Value>,
129 ) -> Result<Response> {
130 let req_builder = Request::post(self.parse_endpoint(&endpoint))
131 .header("content-type", "application/json")
132 .header("accept", "application/json");
133
134 let request = if let Some(d) = data {
135 req_builder.body(Body::from(serde_json::to_string(&d)?))?
136 } else {
137 req_builder.body(Body::empty())?
138 };
139
140 log::debug!("POST request to {}", &endpoint);
141 let mut response = self.hyper_client.request(request).await?;
142
143 let mut res: Vec<u8> = Vec::new();
144 while let Some(chunk) = response.body_mut().data().await {
145 res.write_all(&chunk?)?;
146 }
147
148 Ok(serde_json::from_slice(&res)?)
149 }
150
151 async fn post_file(
152 &self,
153 endpoint: APIEndpoint,
154 data: Option<serde_json::Value>,
155 files: Option<Vec<FormDataFile>>,
156 ) -> Result<Response> {
157 if files.is_none() {
158 return self.post(endpoint, data).await;
159 }
160
161 let mut files = files.expect("no files");
162 if files.is_empty() {
163 return self.post(endpoint, data).await;
164 }
165
166 let req_builder = Request::post(self.parse_endpoint(&endpoint))
167 .header(
168 "content-type",
169 format!("multipart/form-data; boundary={BOUNDARY}"),
170 )
171 .header("accept", "application/json");
172
173 if data.is_some() {
174 files.append(&mut data.expect("no data").as_form_data()?);
175 }
176
177 let bytes = encode_multipart_form_data(&files)?;
178 let request = req_builder.body(Body::from(bytes))?;
179
180 log::debug!("POST request with files to {}", &endpoint);
181 let mut response = self.hyper_client.request(request).await?;
182
183 let mut res: Vec<u8> = Vec::new();
184 while let Some(chunk) = response.body_mut().data().await {
185 res.write_all(&chunk?)?;
186 }
187
188 Ok(serde_json::from_slice(&res)?)
189 }
190}