mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-24 09:16:12 +01:00
Split network into different files
This commit is contained in:
parent
5284d5e884
commit
f37552536e
4 changed files with 169 additions and 138 deletions
39
src/network/download.rs
Normal file
39
src/network/download.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use reqwest::r#async::{Client, Chunk};
|
||||||
|
use tokio::{
|
||||||
|
stream::Stream,
|
||||||
|
io::{AsyncWrite, AsyncWriteExt},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
DownloadError,
|
||||||
|
network::{TELEGRAM_API_URL, file_url},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn download_file<D>(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
path: &str,
|
||||||
|
destination: &mut D,
|
||||||
|
) -> Result<(), DownloadError>
|
||||||
|
where
|
||||||
|
D: AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
let mut stream = download_file_stream(client, token, path).await?;
|
||||||
|
|
||||||
|
while let Some(chunk) = stream.next().await {
|
||||||
|
destination.write_all(chunk?.bytes()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn download_file_stream(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<impl Stream<Item = Result<Chunk, reqwest::Error>>, reqwest::Error> {
|
||||||
|
let url = file_url(TELEGRAM_API_URL, token, path);
|
||||||
|
let resp = client.get(&url).send().await?.error_for_status()?;
|
||||||
|
Ok(resp.into_body())
|
||||||
|
}
|
|
@ -1,23 +1,26 @@
|
||||||
|
mod download;
|
||||||
|
mod request;
|
||||||
|
mod telegram_response;
|
||||||
|
|
||||||
|
use apply::Apply;
|
||||||
|
use bytes::Buf;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use reqwest::{
|
||||||
|
r#async::{multipart::Form, Chunk, Client, Response},
|
||||||
|
StatusCode,
|
||||||
|
};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
stream::Stream,
|
|
||||||
io::{AsyncWrite, AsyncWriteExt},
|
io::{AsyncWrite, AsyncWriteExt},
|
||||||
|
stream::Stream,
|
||||||
};
|
};
|
||||||
use reqwest::{
|
|
||||||
StatusCode,
|
|
||||||
r#async::{multipart::Form, Client, Response, Chunk},
|
|
||||||
};
|
|
||||||
use bytes::Buf;
|
|
||||||
use apply::Apply;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DownloadError, RequestError,
|
requests::ResponseResult, types::ResponseParameters, DownloadError,
|
||||||
requests::ResponseResult, types::ResponseParameters,
|
RequestError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
||||||
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
|
||||||
|
|
||||||
/// Creates URL for making HTTPS requests. See the [Telegram documentation].
|
/// Creates URL for making HTTPS requests. See the [Telegram documentation].
|
||||||
///
|
///
|
||||||
|
@ -43,133 +46,6 @@ fn file_url(base: &str, token: &str, file_path: &str) -> String {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request_multipart<T: DeserializeOwned>(
|
|
||||||
client: &Client,
|
|
||||||
token: &str,
|
|
||||||
method_name: &str,
|
|
||||||
params: Option<Form>,
|
|
||||||
) -> ResponseResult<T> {
|
|
||||||
process_response(
|
|
||||||
client
|
|
||||||
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
|
||||||
.apply(|request_builder| match params {
|
|
||||||
Some(params) => request_builder.multipart(params),
|
|
||||||
None => request_builder,
|
|
||||||
})
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(RequestError::NetworkError)?,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
|
||||||
client: &Client,
|
|
||||||
token: &str,
|
|
||||||
method_name: &str,
|
|
||||||
params: &P,
|
|
||||||
) -> ResponseResult<T> {
|
|
||||||
process_response(
|
|
||||||
client
|
|
||||||
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
|
||||||
.json(params)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(RequestError::NetworkError)?,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn process_response<T: DeserializeOwned>(
|
|
||||||
mut response: Response,
|
|
||||||
) -> ResponseResult<T> {
|
|
||||||
let response = serde_json::from_str::<TelegramResponse<T>>(
|
|
||||||
&response.text().await.map_err(RequestError::NetworkError)?,
|
|
||||||
)
|
|
||||||
.map_err(RequestError::InvalidJson)?;
|
|
||||||
|
|
||||||
response.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum TelegramResponse<R> {
|
|
||||||
Ok {
|
|
||||||
/// A dummy field. Used only for deserialization.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
ok: bool, // TODO: True type
|
|
||||||
|
|
||||||
result: R,
|
|
||||||
},
|
|
||||||
Err {
|
|
||||||
/// A dummy field. Used only for deserialization.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
ok: bool, // TODO: False type
|
|
||||||
|
|
||||||
description: String,
|
|
||||||
error_code: u16,
|
|
||||||
response_parameters: Option<ResponseParameters>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn download_file<D>(
|
|
||||||
client: &Client,
|
|
||||||
token: &str,
|
|
||||||
path: &str,
|
|
||||||
destination: &mut D,
|
|
||||||
) -> Result<(), DownloadError>
|
|
||||||
where
|
|
||||||
D: AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
let mut stream = download_file_stream(client, token, path).await?;
|
|
||||||
|
|
||||||
while let Some(chunk) = stream.next().await {
|
|
||||||
destination.write_all(chunk?.bytes()).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn download_file_stream(
|
|
||||||
client: &Client,
|
|
||||||
token: &str,
|
|
||||||
path: &str,
|
|
||||||
) -> Result<impl Stream<Item = Result<Chunk, reqwest::Error>>, reqwest::Error> {
|
|
||||||
let url = file_url(TELEGRAM_API_URL, token, path);
|
|
||||||
let resp = client.get(&url).send().await?.error_for_status()?;
|
|
||||||
Ok(resp.into_body())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R> Into<ResponseResult<R>> for TelegramResponse<R> {
|
|
||||||
fn into(self) -> Result<R, RequestError> {
|
|
||||||
match self {
|
|
||||||
TelegramResponse::Ok { result, .. } => Ok(result),
|
|
||||||
TelegramResponse::Err {
|
|
||||||
description,
|
|
||||||
error_code,
|
|
||||||
response_parameters,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
if let Some(params) = response_parameters {
|
|
||||||
match params {
|
|
||||||
ResponseParameters::RetryAfter(i) => {
|
|
||||||
Err(RequestError::RetryAfter(i))
|
|
||||||
}
|
|
||||||
ResponseParameters::MigrateToChatId(to) => {
|
|
||||||
Err(RequestError::MigrateToChatId(to))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(RequestError::ApiError {
|
|
||||||
description,
|
|
||||||
status_code: StatusCode::from_u16(error_code).unwrap(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
60
src/network/request.rs
Normal file
60
src/network/request.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
use reqwest::r#async::{
|
||||||
|
Client, multipart::Form, Response,
|
||||||
|
};
|
||||||
|
use apply::Apply;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
RequestError,
|
||||||
|
requests::ResponseResult,
|
||||||
|
network::{method_url, TELEGRAM_API_URL},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn request_multipart<T>(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
method_name: &str,
|
||||||
|
params: Option<Form>,
|
||||||
|
) -> ResponseResult<T> where T: DeserializeOwned {
|
||||||
|
process_response(
|
||||||
|
client
|
||||||
|
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
||||||
|
.apply(|request_builder| match params {
|
||||||
|
Some(params) => request_builder.multipart(params),
|
||||||
|
None => request_builder,
|
||||||
|
})
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(RequestError::NetworkError)?,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
method_name: &str,
|
||||||
|
params: &P,
|
||||||
|
) -> ResponseResult<T> {
|
||||||
|
process_response(
|
||||||
|
client
|
||||||
|
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
||||||
|
.json(params)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(RequestError::NetworkError)?,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_response<T: DeserializeOwned>(
|
||||||
|
mut response: Response,
|
||||||
|
) -> ResponseResult<T> {
|
||||||
|
let response = serde_json::from_str::<TelegramResponse<T>>(
|
||||||
|
&response.text().await.map_err(RequestError::NetworkError)?,
|
||||||
|
)
|
||||||
|
.map_err(RequestError::InvalidJson)?;
|
||||||
|
|
||||||
|
response.into()
|
||||||
|
}
|
56
src/network/telegram_response.rs
Normal file
56
src/network/telegram_response.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
requests::ResponseResult, types::ResponseParameters, RequestError,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum TelegramResponse<R> {
|
||||||
|
Ok {
|
||||||
|
/// A dummy field. Used only for deserialization.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ok: bool, // TODO: True type
|
||||||
|
|
||||||
|
result: R,
|
||||||
|
},
|
||||||
|
Err {
|
||||||
|
/// A dummy field. Used only for deserialization.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ok: bool, // TODO: False type
|
||||||
|
|
||||||
|
description: String,
|
||||||
|
error_code: u16,
|
||||||
|
response_parameters: Option<ResponseParameters>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Into<ResponseResult<R>> for TelegramResponse<R> {
|
||||||
|
fn into(self) -> Result<R, RequestError> {
|
||||||
|
match self {
|
||||||
|
TelegramResponse::Ok { result, .. } => Ok(result),
|
||||||
|
TelegramResponse::Err {
|
||||||
|
description,
|
||||||
|
error_code,
|
||||||
|
response_parameters,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if let Some(params) = response_parameters {
|
||||||
|
match params {
|
||||||
|
ResponseParameters::RetryAfter(i) => {
|
||||||
|
Err(RequestError::RetryAfter(i))
|
||||||
|
}
|
||||||
|
ResponseParameters::MigrateToChatId(to) => {
|
||||||
|
Err(RequestError::MigrateToChatId(to))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(RequestError::ApiError {
|
||||||
|
description,
|
||||||
|
status_code: StatusCode::from_u16(error_code).unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue