From dc755b6ec03d344ddf474420c6f2ee74472b806d Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 8 Sep 2019 02:43:58 +0300 Subject: [PATCH] Properly parse telegram answer --- src/core/network/mod.rs | 74 +++++++++++++------ src/core/requests/forward_message.rs | 6 +- src/core/requests/mod.rs | 2 +- src/core/requests/send_message.rs | 10 +-- src/core/types/chat.rs | 7 +- src/core/types/message.rs | 12 ++- src/core/types/mod.rs | 2 + ...se_parameter.rs => response_parameters.rs} | 15 ++-- 8 files changed, 82 insertions(+), 46 deletions(-) rename src/core/types/{response_parameter.rs => response_parameters.rs} (52%) diff --git a/src/core/network/mod.rs b/src/core/network/mod.rs index b2fd4d08..dbc7fcf0 100644 --- a/src/core/network/mod.rs +++ b/src/core/network/mod.rs @@ -1,10 +1,19 @@ -use apply::Apply; -use reqwest::r#async::{multipart::Form, Client}; -use serde::de::DeserializeOwned; -use serde_json::Value; +use crate::core::{ + requests::{RequestError, ResponseResult}, + types::ResponseParameters, +}; + +use apply::Apply; +use serde_json::Value; +use serde::{ + Serialize, + de::DeserializeOwned +}; +use reqwest::{ + StatusCode, + r#async::{Client, multipart::Form}, +}; -use crate::core::requests::{RequestError, ResponseResult}; -use serde::Serialize; const TELEGRAM_API_URL: &str = "https://api.telegram.org"; @@ -48,21 +57,22 @@ pub async fn request_multipart( .await .map_err(RequestError::NetworkError)?; - let response_json = serde_json::from_str::( + let response = serde_json::from_str::>( &response .text() .await .map_err(RequestError::NetworkError)?, ) - .map_err(RequestError::InvalidJson)?; + .map_err(RequestError::InvalidJson)?; - if response_json["ok"] == "false" { - Err(RequestError::ApiError { - status_code: response.status(), - description: response_json["description"].to_string(), - }) - } else { - Ok(serde_json::from_value(response_json["result"].clone()).unwrap()) + match response { + TelegramResponse::Ok { result, .. } => Ok(result), + TelegramResponse::Err { + description, + error_code, + response_parameters, + .. + } => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() }) } } @@ -79,24 +89,40 @@ pub async fn request_json( .await .map_err(RequestError::NetworkError)?; - let response_json = serde_json::from_str::( + let response = serde_json::from_str::>( &response .text() .await .map_err(RequestError::NetworkError)?, ) - .map_err(RequestError::InvalidJson)?; + .map_err(RequestError::InvalidJson)?; - if response_json["ok"] == "false" { - Err(RequestError::ApiError { - status_code: response.status(), - description: response_json["description"].to_string(), - }) - } else { - Ok(serde_json::from_value(response_json["result"].clone()).unwrap()) + match response { + TelegramResponse::Ok { result, .. } => Ok(result), + TelegramResponse::Err { + description, + error_code, + response_parameters, + .. + } => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() }) } } +#[derive(Deserialize)] +#[serde(untagged)] +enum TelegramResponse { + Ok { + ok: bool, // true + result: R, + }, + Err { + ok: bool, // false + description: String, + error_code: u16, + response_parameters: Option, + }, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/core/requests/forward_message.rs b/src/core/requests/forward_message.rs index 3a286f1c..1fb0ad6d 100644 --- a/src/core/requests/forward_message.rs +++ b/src/core/requests/forward_message.rs @@ -37,14 +37,14 @@ impl<'a> Request<'a> for ForwardMessage<'a> { type ReturnValue = Message; fn send(self) -> RequestFuture<'a, ResponseResult> { - Box::pin( + Box::pin(async move { network::request_json( self.ctx.client, self.ctx.token, "forwardMessage", &self, - ) - ) + ).await + }) } } diff --git a/src/core/requests/mod.rs b/src/core/requests/mod.rs index 1a124ac1..e6a745bc 100644 --- a/src/core/requests/mod.rs +++ b/src/core/requests/mod.rs @@ -13,7 +13,7 @@ mod utils; #[derive(Debug, Display)] pub enum RequestError { #[display(fmt = "Telegram error #{}: {}", status_code, description)] - ApiError { + ApiError { // TODO: add response parameters status_code: StatusCode, description: String, }, diff --git a/src/core/requests/send_message.rs b/src/core/requests/send_message.rs index c78e77b6..55239f77 100644 --- a/src/core/requests/send_message.rs +++ b/src/core/requests/send_message.rs @@ -52,25 +52,25 @@ impl<'a> Request<'a> for SendMessage<'a> { type ReturnValue = Message; fn send(self) -> RequestFuture<'a, ResponseResult> { - Box::pin( + Box::pin(async move { network::request_json( self.ctx.client, self.ctx.token, "sendMessage", &self, - ) - ) + ).await + }) } } impl<'a> SendMessage<'a> { pub(crate) fn new( - info: RequestContext<'a>, + ctx: RequestContext<'a>, chat_id: ChatId, text: String, ) -> Self { SendMessage { - ctx: info, + ctx, chat_id, text, parse_mode: None, diff --git a/src/core/types/chat.rs b/src/core/types/chat.rs index dd944722..dbfb080e 100644 --- a/src/core/types/chat.rs +++ b/src/core/types/chat.rs @@ -3,7 +3,6 @@ use crate::core::types::{ChatPermissions, ChatPhoto, Message}; #[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)] pub struct Chat { - #[serde(rename = "chat_id")] pub id: i64, #[serde(flatten)] pub kind: ChatKind, @@ -103,7 +102,7 @@ mod tests { photo: None, }; let actual = from_str( - r#"{"chat_id":-1,"type":"channel","username":"channelname"}"#, + r#"{"id":-1,"type":"channel","username":"channelname"}"#, ) .unwrap(); assert_eq!(expected, actual); @@ -123,12 +122,12 @@ mod tests { photo: None }, from_str( - r#"{"chat_id":0,"type":"private","username":"username","first_name":"Anon"}"# + r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"# ).unwrap()); } #[test] fn private_chat_de_wrong_type_field() { - assert!(from_str::(r#"{"chat_id":0,"type":"WRONG"}"#).is_err()); + assert!(from_str::(r#"{"id":0,"type":"WRONG"}"#).is_err()); } } diff --git a/src/core/types/message.rs b/src/core/types/message.rs index 3c7d0f78..46d6cc14 100644 --- a/src/core/types/message.rs +++ b/src/core/types/message.rs @@ -190,7 +190,7 @@ mod tests { reply_markup: None, }, }; - let actual = from_str::(r#"{"message_id":0,"date":0,"chat":{"chat_id":0,"type":"private"},"text":"Hello","entities":[]}"#).unwrap(); + let actual = from_str::(r#"{"message_id":0,"date":0,"chat":{"id":0,"type":"private"},"text":"Hello","entities":[]}"#).unwrap(); assert_eq!(expected, actual); } @@ -230,9 +230,17 @@ mod tests { }, }; let actual = from_str::( - r#"{"message_id":1,"date":1,"chat":{"chat_id":1,"type":"private"},"forward_date":1,"forward_from":{"id":123,"is_bot":false,"first_name":"Name"},"text":"Message","entities":[]}"#, + r#"{"message_id":1,"date":1,"chat":{"id":1,"type":"private"},"forward_date":1,"forward_from":{"id":123,"is_bot":false,"first_name":"Name"},"text":"Message","entities":[]}"#, ) .unwrap(); assert_eq!(expected, actual); } + + #[test] + fn sent_message_de() { + // actual message from telegram + let json = "{\"message_id\":6534,\"from\":{\"id\":457569668,\"is_bot\":true,\"first_name\":\"\\u0424\\u044b\\u0440\\u044c\\u043a\",\"username\":\"BloodyTestBot\"},\"chat\":{\"id\":218485655,\"first_name\":\"\\u0412\\u0430\\u0444\\u0435\\u043b\\u044c\",\"username\":\"WaffleLapkin\",\"type\":\"private\"},\"date\":1567898953,\"text\":\"text\"}"; + let actual: Result= from_str(json); + assert!(actual.is_ok()); + } } diff --git a/src/core/types/mod.rs b/src/core/types/mod.rs index 6258a4ec..d2ac4b81 100644 --- a/src/core/types/mod.rs +++ b/src/core/types/mod.rs @@ -26,6 +26,7 @@ pub use self::{ successful_payment::SuccessfulPayment, user::User, video::Video, + response_parameters::ResponseParameters }; mod answer_pre_checkout_query; @@ -55,3 +56,4 @@ mod sticker; mod successful_payment; mod user; mod video; +mod response_parameters; \ No newline at end of file diff --git a/src/core/types/response_parameter.rs b/src/core/types/response_parameters.rs similarity index 52% rename from src/core/types/response_parameter.rs rename to src/core/types/response_parameters.rs index 5ec3c8dd..901d1492 100644 --- a/src/core/types/response_parameter.rs +++ b/src/core/types/response_parameters.rs @@ -1,5 +1,4 @@ #[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)] -#[serde(tag = "type")] #[serde(rename_all = "snake_case")] pub enum ResponseParameters { MigrateToChatId(i64), @@ -8,23 +7,25 @@ pub enum ResponseParameters { #[cfg(test)] mod tests { + use super::*; + #[test] fn migrate_to_chat_id_deserialization() { - let expected_struct = ResponseParameters::MigrateToChatId(123456); - let actual_json: ResponseParameters = serde_json::from_str( + let expected = ResponseParameters::MigrateToChatId(123456); + let actual: ResponseParameters = serde_json::from_str( r#"{"migrate_to_chat_id":123456}"# ).unwrap(); - assert_eq!(expected_json, actual_json); + assert_eq!(expected, actual); } #[test] fn retry_after_deserialization() { - let expected_struct = ResponseParameters::RetryAfter(123456); - let actual_json: ResponseParameters = serde_json::from_str( + let expected = ResponseParameters::RetryAfter(123456); + let actual: ResponseParameters = serde_json::from_str( r#"{"retry_after":123456}"# ).unwrap(); - assert_eq!(expected_json, actual_json); + assert_eq!(expected, actual); } } \ No newline at end of file