use crate::{
connectors::Connector, errors, internal::Client, token, types::chat,
};
use hyper::{
body::{Body, HttpBody},
header::HeaderValue,
Method, Request, Uri,
};
use serde::{de::DeserializeOwned, Deserialize};
#[derive(Deserialize)]
struct ResponseParameters {
migrate_to_chat_id: Option<chat::Id>,
retry_after: Option<u64>,
}
#[derive(Deserialize)]
struct Response<T> {
result: Option<T>,
description: Option<String>,
error_code: Option<u16>,
parameters: Option<ResponseParameters>,
}
pub async fn send_method<'a, T, C>(
client: &'a Client<C>,
token: token::Ref<'a>,
method: &'static str,
boundary: Option<String>,
body: Vec<u8>,
) -> Result<T, errors::MethodCall>
where
T: DeserializeOwned,
C: Connector,
{
let url = Uri::builder()
.scheme("https")
.authority("api.telegram.org")
.path_and_query(format!("/bot{}/{}", token.as_str(), method).as_str())
.build()
.expect("[tbot] Method URL construction failed");
let mut request = Request::new(Body::from(body));
*request.method_mut() = Method::POST;
*request.uri_mut() = url;
let content_type = if let Some(boundary) = boundary {
let value = format!("multipart/form-data; boundary={}", boundary);
HeaderValue::from_str(&value).unwrap()
} else {
HeaderValue::from_static("application/json")
};
request
.headers_mut()
.insert(hyper::header::CONTENT_TYPE, content_type);
let (parts, mut body) = client.request(request).await?.into_parts();
let mut response = parts
.headers
.get("Content-Length")
.and_then(|x| x.to_str().ok().and_then(|x| x.parse().ok()))
.map_or_else(Vec::new, Vec::with_capacity);
while let Some(chunk) = body.data().await {
response.extend(chunk?);
}
if response.starts_with(b"<") {
return Err(errors::MethodCall::OutOfService);
}
let response: Response<T> = serde_json::from_slice(&response[..])
.map_err(|error| errors::MethodCall::Parse { response, error })?;
if let Some(result) = response.result {
return Ok(result);
}
let (migrate_to_chat_id, retry_after) = match response.parameters {
Some(parameters) => {
(parameters.migrate_to_chat_id, parameters.retry_after)
}
None => (None, None),
};
Err(errors::MethodCall::RequestError {
description: response.description.unwrap(),
error_code: response.error_code.unwrap(),
migrate_to_chat_id,
retry_after,
})
}