Refine network/mod.rs

This commit is contained in:
Temirkhan Myrzamadi 2019-09-02 16:21:10 +06:00
parent ce87928a90
commit cad60fa2fb
6 changed files with 123 additions and 85 deletions

View file

@ -1,59 +1,63 @@
use reqwest::StatusCode;
use reqwest::r#async::Client;
use serde_json::Value;
use futures::compat::Future01CompatExt;
use apply::Apply;
use serde::de::DeserializeOwned;
use super::requests::Request; use super::requests::Request;
use apply::Apply;
use futures::compat::Future01CompatExt;
use reqwest::r#async::Client;
use reqwest::StatusCode;
use serde::de::DeserializeOwned;
use serde_json::Value;
const TELEGRAM_URL_START: &str = "https://api.telegram.org/bot"; const TELEGRAM_API_URL: &str = "https://api.telegram.org";
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum RequestError {
Api { ApiError {
status_code: StatusCode, status_code: StatusCode,
description: Option<String>, description: String,
}, },
Send(reqwest::Error), NetworkError(reqwest::Error),
InvalidJson(reqwest::Error), InvalidJson(serde_json::Error),
} }
pub type Response<T> = Result<T, Error>; pub type ResponseResult<T> = Result<T, RequestError>;
pub async fn request<R: DeserializeOwned, Req: Request<R>>( pub async fn request<T: DeserializeOwned, R: Request<T>>(
client: &Client, client: &Client,
request: Req, request: R,
) -> Response<T> { ) -> ResponseResult<T> {
let mut response = client let mut response = client
.post(&format!( .post(&format!(
"{}{token}/{method}", "{url}/bot{token}/{method}",
TELEGRAM_URL_START, url = TELEGRAM_API_URL,
token = request.token(), token = request.token(),
method = request.name(), method = request.name(),
)) ))
.apply(|req| if let Some(params) = request.params() { .apply(|request_builder| {
req.multipart(params) if let Some(params) = request.params() {
} else { req }) request_builder.multipart(params)
} else {
request_builder
}
})
.send() .send()
.compat() .compat()
.await .await
.map_err(Error::Send)?; .map_err(RequestError::NetworkError)?;
let response_json = response let response_json = serde_json::from_str::<Value>(
.json::<Value>() &response
.text()
.compat() .compat()
.await .await
.map_err(Error::InvalidJson)?; .map_err(RequestError::NetworkError)?,
)
.map_err(RequestError::InvalidJson)?;
if response_json["ok"] == "false" { if response_json["ok"] == "false" {
return Err(Error::Api { Err(RequestError::ApiError {
status_code: response.status(), status_code: response.status(),
description: match response_json.get("description") { description: response_json["description"].to_string(),
None => None, })
Some(description) => Some(description.to_string()), } else {
},
});
}
Ok(serde_json::from_value(response_json["result"].clone()).unwrap()) Ok(serde_json::from_value(response_json["result"].clone()).unwrap())
}
} }

View file

@ -1,5 +1,5 @@
use crate::core::payments::{Invoice, SuccessfulPayment};
use serde::Deserialize; use serde::Deserialize;
use crate::core::payments::{SuccessfulPayment, Invoice};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct User { pub struct User {
@ -16,7 +16,7 @@ pub struct Chat {
id: i64, id: i64,
chat_type: String, chat_type: String,
title: Option<String>, title: Option<String>,
username:Option<String>, username: Option<String>,
first_name: Option<String>, first_name: Option<String>,
last_name: Option<String>, last_name: Option<String>,
photo: Option<ChatPhoto>, photo: Option<ChatPhoto>,
@ -55,7 +55,7 @@ pub struct Message {
sticker: Option<Stickers>, sticker: Option<Stickers>,
video: Option<Video>, video: Option<Video>,
voice: Option<Voice>, voice: Option<Voice>,
video_note: Option< VideoNote>, video_note: Option<VideoNote>,
caption: Option<String>, caption: Option<String>,
contact: Option<Contact>, contact: Option<Contact>,
location: Option<Location>, location: Option<Location>,

View file

@ -1,8 +1,5 @@
use serde::Deserialize;
use crate::core::other::User; use crate::core::other::User;
use serde::Deserialize;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct SendInvoice { pub struct SendInvoice {
@ -76,7 +73,7 @@ pub struct OrderInfo {
name: String, name: String,
phone_number: String, phone_number: String,
email: String, email: String,
shipping_address: ShippingAddress shipping_address: ShippingAddress,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -113,5 +110,5 @@ pub struct PreCheckoutQuery {
total_amount: i64, total_amount: i64,
invoice_payload: String, invoice_payload: String,
shipping_option_id: Option<String>, shipping_option_id: Option<String>,
order_info: Option<OrderInfo> order_info: Option<OrderInfo>,
} }

View file

@ -1,16 +1,21 @@
use crate::core::other::User;
use super::Request; use super::Request;
use crate::core::other::User;
use reqwest::r#async::multipart::Form; use reqwest::r#async::multipart::Form;
#[derive(Debug, Constructor, PartialEq, Eq)] #[derive(Debug, Constructor, PartialEq, Eq)]
pub struct GetMe { pub struct GetMe {
token: String, token: String,
} }
impl Request<User> for GetMe { impl Request<User> for GetMe {
fn name(&self) -> &str { "getMe" } fn name(&self) -> &str {
fn params(self) -> Option<Form> { None } "getMe"
fn token(&self) -> &str { &self.token } }
fn params(self) -> Option<Form> {
None
}
fn token(&self) -> &str {
&self.token
}
} }

View file

@ -1,6 +1,5 @@
use reqwest::r#async::multipart::Form; use reqwest::r#async::multipart::Form;
/// Request that can be sended to telegram. /// Request that can be sended to telegram.
/// `R` - return type. /// `R` - return type.
pub trait Request<R: serde::de::DeserializeOwned> { pub trait Request<R: serde::de::DeserializeOwned> {

View file

@ -1,9 +1,8 @@
use crate::core::other::Message;
use super::{ChatId, Request}; use super::{ChatId, Request};
use crate::core::other::Message;
use reqwest::r#async::multipart::Form; use reqwest::r#async::multipart::Form;
#[derive(Debug, TypedBuilder, PartialEq, Eq)] #[derive(Debug, TypedBuilder, PartialEq, Eq)]
pub struct SendMessage { pub struct SendMessage {
token: String, token: String,
@ -23,7 +22,9 @@ pub struct SendMessage {
} }
impl Request<Message> for SendMessage { impl Request<Message> for SendMessage {
fn name(&self) -> &str { "getMe" } fn name(&self) -> &str {
"getMe"
}
fn params(self) -> Option<Form> { fn params(self) -> Option<Form> {
use apply::Apply; use apply::Apply;
@ -31,31 +32,59 @@ impl Request<Message> for SendMessage {
let params = Form::new() let params = Form::new()
.text("chat_id", format!("{:?}", self.chat_id)) .text("chat_id", format!("{:?}", self.chat_id))
.text("text", self.text) .text("text", self.text)
.apply(|f| if let Some(parse_mode) = self.parse_mode { .apply(|f| {
if let Some(parse_mode) = self.parse_mode {
f.text("parse_mode", parse_mode); f.text("parse_mode", parse_mode);
f f
} else { f }) } else {
.apply(|f| if let Some(disable_web_page_preview) = self.disable_web_page_preview {
f.text("disable_web_page_preview", format!("{:?}", disable_web_page_preview));
f f
} else { f }) }
.apply(|f| if let Some(disable_notification) = self.disable_notification { })
f.text("disable_notification", format!("{:?}", disable_notification)); .apply(|f| {
if let Some(disable_web_page_preview) = self.disable_web_page_preview {
f.text(
"disable_web_page_preview",
format!("{:?}", disable_web_page_preview),
);
f f
} else { f }) } else {
.apply(|f| if let Some(reply_to_message_id) = self.reply_to_message_id { f
}
})
.apply(|f| {
if let Some(disable_notification) = self.disable_notification {
f.text(
"disable_notification",
format!("{:?}", disable_notification),
);
f
} else {
f
}
})
.apply(|f| {
if let Some(reply_to_message_id) = self.reply_to_message_id {
f.text("reply_to_message_id", format!("{:?}", reply_to_message_id)); f.text("reply_to_message_id", format!("{:?}", reply_to_message_id));
f f
} else { f }) } else {
.apply(|f| if let Some(reply_markup) = self.reply_markup { f
}
})
.apply(|f| {
if let Some(reply_markup) = self.reply_markup {
unimplemented!(); unimplemented!();
//f.text("reply_markup", ); //f.text("reply_markup", );
f f
} else { f }); } else {
f
}
});
Some(params) Some(params)
} }
fn token(&self) -> &str { &self.token } fn token(&self) -> &str {
&self.token
}
} }
#[cfg(test)] #[cfg(test)]
@ -64,7 +93,11 @@ mod test {
#[test] #[test]
fn default() { fn default() {
let sm = SendMessage::builder().token("TOKEN").chat_id(123456.into()).text("text").build(); let sm = SendMessage::builder()
.token("TOKEN")
.chat_id(123456.into())
.text("text")
.build();
let r = SendMessage { let r = SendMessage {
token: String::from("TOKEN"), token: String::from("TOKEN"),
chat_id: ChatId::Id(123456), chat_id: ChatId::Id(123456),
@ -73,7 +106,7 @@ mod test {
disable_web_page_preview: None, disable_web_page_preview: None,
disable_notification: None, disable_notification: None,
reply_to_message_id: None, reply_to_message_id: None,
reply_markup: None reply_markup: None,
}; };
assert_eq!(sm, r); assert_eq!(sm, r);