Add ApiUrl

Add `ApiUrl` and also use `Url` instead of `&str` for urls to clean code
This commit is contained in:
Waffle 2020-11-17 22:38:30 +03:00
parent fa0e86c547
commit 17b44db578
5 changed files with 60 additions and 28 deletions

17
src/bot/api_url.rs Normal file
View file

@ -0,0 +1,17 @@
#[derive(Debug, Clone)]
pub(crate) enum ApiUrl {
Default,
#[allow(dead_code)]
Custom(reqwest::Url),
}
impl ApiUrl {
pub(crate) fn get(&self) -> reqwest::Url {
match self {
// FIXME(waffle): parse once
ApiUrl::Default => reqwest::Url::parse(crate::net::TELEGRAM_API_URL)
.expect("failed to parse default url"),
ApiUrl::Custom(url) => url.clone(),
}
}
}

View file

@ -7,6 +7,7 @@ use reqwest::{
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use crate::{ use crate::{
bot::api_url::ApiUrl,
net, net,
requests::{Payload, ResponseResult}, requests::{Payload, ResponseResult},
serde_multipart, serde_multipart,
@ -14,6 +15,7 @@ use crate::{
}; };
mod api; mod api;
mod api_url;
mod download; mod download;
pub(crate) const TELOXIDE_TOKEN: &str = "TELOXIDE_TOKEN"; pub(crate) const TELOXIDE_TOKEN: &str = "TELOXIDE_TOKEN";
@ -27,7 +29,7 @@ pub(crate) const TELOXIDE_PROXY: &str = "TELOXIDE_PROXY";
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Bot { pub struct Bot {
token: Arc<str>, token: Arc<str>,
api_url: Option<Arc<str>>, api_url: ApiUrl,
client: Client, client: Client,
parse_mode: Option<ParseMode>, parse_mode: Option<ParseMode>,
} }
@ -103,7 +105,7 @@ impl Bot {
{ {
Self { Self {
token: Into::<Arc<str>>::into(Into::<String>::into(token)), token: Into::<Arc<str>>::into(Into::<String>::into(token)),
api_url: None, api_url: ApiUrl::Default,
client, client,
parse_mode: None, parse_mode: None,
} }
@ -128,9 +130,7 @@ impl Bot {
.expect("serialization of request to be infallible"); .expect("serialization of request to be infallible");
// async move to capture client&token&api_url&params // async move to capture client&token&api_url&params
async move { async move { net::request_json2(&client, token.as_ref(), api_url.get(), P::NAME, params).await }
net::request_json2(&client, token.as_ref(), api_url.as_deref(), P::NAME, params).await
}
} }
pub(crate) fn execute_multipart<P>( pub(crate) fn execute_multipart<P>(
@ -150,8 +150,7 @@ impl Bot {
// async move to capture client&token&api_url&params // async move to capture client&token&api_url&params
async move { async move {
let params = params.await?; let params = params.await?;
net::request_multipart2(&client, token.as_ref(), api_url.as_deref(), P::NAME, params) net::request_multipart2(&client, token.as_ref(), api_url.get(), P::NAME, params).await
.await
} }
} }
} }
@ -289,7 +288,7 @@ impl BotBuilder {
pub fn build(self) -> Bot { pub fn build(self) -> Bot {
Bot { Bot {
token: self.token.unwrap_or_else(|| get_env(TELOXIDE_TOKEN)).into(), token: self.token.unwrap_or_else(|| get_env(TELOXIDE_TOKEN)).into(),
api_url: None, api_url: ApiUrl::Default,
client: self.client.unwrap_or_else(crate::client_from_env), client: self.client.unwrap_or_else(crate::client_from_env),
parse_mode: self.parse_mode, parse_mode: self.parse_mode,
} }

View file

@ -3,8 +3,6 @@ use tokio::io::{AsyncWrite, AsyncWriteExt};
use crate::errors::DownloadError; use crate::errors::DownloadError;
use super::TELEGRAM_API_URL;
pub async fn download_file<D>( pub async fn download_file<D>(
client: &Client, client: &Client,
token: &str, token: &str,
@ -15,7 +13,11 @@ where
D: AsyncWrite + Unpin, D: AsyncWrite + Unpin,
{ {
let mut res = client let mut res = client
.get(&super::file_url(TELEGRAM_API_URL, token, path)) .get(crate::net::file_url(
reqwest::Url::parse(crate::net::TELEGRAM_API_URL).expect("failed to parse default url"),
token,
path,
))
.send() .send()
.await? .await?
.error_for_status()?; .error_for_status()?;
@ -33,7 +35,11 @@ pub async fn download_file_stream(
path: &str, path: &str,
) -> Result<impl futures::Stream<Item = reqwest::Result<bytes::Bytes>>, reqwest::Error> { ) -> Result<impl futures::Stream<Item = reqwest::Result<bytes::Bytes>>, reqwest::Error> {
let res = client let res = client
.get(&super::file_url(TELEGRAM_API_URL, token, path)) .get(crate::net::file_url(
reqwest::Url::parse(crate::net::TELEGRAM_API_URL).expect("failed to parse default url"),
token,
path,
))
.send() .send()
.await? .await?
.error_for_status()?; .error_for_status()?;

View file

@ -8,36 +8,38 @@ mod download;
mod request; mod request;
mod telegram_response; mod telegram_response;
const TELEGRAM_API_URL: &str = "https://api.telegram.org"; pub(crate) 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].
/// ///
/// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests /// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests
fn method_url(base: &str, token: &str, method_name: &str) -> String { fn method_url(base: reqwest::Url, token: &str, method_name: &str) -> reqwest::Url {
format!("{url}/bot{token}/{method}", url = base, token = token, method = method_name) base.join(&format!("/bot{token}/{method}", token = token, method = method_name))
.expect("failed to format url")
} }
/// Creates URL for downloading a file. See the [Telegram documentation]. /// Creates URL for downloading a file. See the [Telegram documentation].
/// ///
/// [Telegram documentation]: https://core.telegram.org/bots/api#file /// [Telegram documentation]: https://core.telegram.org/bots/api#file
fn file_url(base: &str, token: &str, file_path: &str) -> String { fn file_url(base: reqwest::Url, token: &str, file_path: &str) -> reqwest::Url {
format!("{url}/file/bot{token}/{file}", url = base, token = token, file = file_path,) base.join(&format!("file/bot{token}/{file}", token = token, file = file_path))
.expect("failed to format url")
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use crate::net::*;
#[test] #[test]
fn method_url_test() { fn method_url_test() {
let url = method_url( let url = method_url(
TELEGRAM_API_URL, reqwest::Url::parse(TELEGRAM_API_URL).unwrap(),
"535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao", "535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao",
"methodName", "methodName",
); );
assert_eq!( assert_eq!(
url, url.as_str(),
"https://api.telegram.org/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/methodName" "https://api.telegram.org/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/methodName"
); );
} }
@ -45,13 +47,13 @@ mod tests {
#[test] #[test]
fn file_url_test() { fn file_url_test() {
let url = file_url( let url = file_url(
TELEGRAM_API_URL, reqwest::Url::parse(TELEGRAM_API_URL).unwrap(),
"535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao", "535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao",
"AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ", "AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ",
); );
assert_eq!( assert_eq!(
url, url.as_str(),
"https://api.telegram.org/file/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ" "https://api.telegram.org/file/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ"
); );
} }

View file

@ -35,7 +35,11 @@ where
}; };
let response = client let response = client
.post(&super::method_url(TELEGRAM_API_URL, token, method_name)) .post(crate::net::method_url(
reqwest::Url::parse(TELEGRAM_API_URL).expect("failed to parse default url"),
token,
method_name,
))
.multipart(form) .multipart(form)
.send() .send()
.await .await
@ -55,7 +59,11 @@ where
R: DeserializeOwned, R: DeserializeOwned,
{ {
let response = client let response = client
.post(&super::method_url(TELEGRAM_API_URL, token, method_name)) .post(crate::net::method_url(
reqwest::Url::parse(TELEGRAM_API_URL).expect("failed to parse default url"),
token,
method_name,
))
.json(params) .json(params)
.send() .send()
.await .await
@ -72,7 +80,7 @@ where
pub async fn request_multipart2<T>( pub async fn request_multipart2<T>(
client: &Client, client: &Client,
token: &str, token: &str,
api_url: Option<&str>, api_url: reqwest::Url,
method_name: &str, method_name: &str,
params: reqwest::multipart::Form, params: reqwest::multipart::Form,
) -> ResponseResult<T> ) -> ResponseResult<T>
@ -80,7 +88,7 @@ where
T: DeserializeOwned, T: DeserializeOwned,
{ {
let response = client let response = client
.post(&super::method_url(api_url.unwrap_or(TELEGRAM_API_URL), token, method_name)) .post(crate::net::method_url(api_url, token, method_name))
.multipart(params) .multipart(params)
.send() .send()
.await .await
@ -92,7 +100,7 @@ where
pub async fn request_json2<T>( pub async fn request_json2<T>(
client: &Client, client: &Client,
token: &str, token: &str,
api_url: Option<&str>, api_url: reqwest::Url,
method_name: &str, method_name: &str,
params: Vec<u8>, params: Vec<u8>,
) -> ResponseResult<T> ) -> ResponseResult<T>
@ -100,7 +108,7 @@ where
T: DeserializeOwned, T: DeserializeOwned,
{ {
let response = client let response = client
.post(&super::method_url(api_url.unwrap_or(TELEGRAM_API_URL), token, method_name)) .post(crate::net::method_url(api_url, token, method_name))
.header(CONTENT_TYPE, HeaderValue::from_static("application/json")) .header(CONTENT_TYPE, HeaderValue::from_static("application/json"))
.body(params) .body(params)
.send() .send()