use std::{error::Error, fmt};
use serde::Deserialize;
use crate::types::Integer;
#[derive(Clone, Debug, Deserialize)]
#[serde(from = "RawResponse<T>")]
pub enum Response<T> {
Success(T),
Error(ResponseError),
}
impl<T> Response<T> {
pub fn retry_after(&self) -> Option<u64> {
match self {
Response::Success(_) => None,
Response::Error(err) => err.retry_after(),
}
}
pub fn into_result(self) -> Result<T, ResponseError> {
match self {
Response::Success(obj) => Ok(obj),
Response::Error(err) => Err(err),
}
}
}
impl<T> From<RawResponse<T>> for Response<T> {
fn from(raw: RawResponse<T>) -> Self {
if raw.ok {
if let Some(result) = raw.result {
Response::Success(result)
} else {
Response::Error(ResponseError {
description: String::from("response is ok, but result is not provided"),
error_code: None,
migrate_to_chat_id: None,
retry_after: None,
})
}
} else {
Response::Error(ResponseError {
description: raw.description.unwrap_or_else(|| String::from("no description")),
error_code: raw.error_code,
migrate_to_chat_id: raw.parameters.and_then(|x| x.migrate_to_chat_id),
retry_after: raw.parameters.and_then(|x| x.retry_after),
})
}
}
}
#[derive(Clone, Debug)]
pub struct ResponseError {
description: String,
error_code: Option<Integer>,
migrate_to_chat_id: Option<Integer>,
retry_after: Option<Integer>,
}
impl ResponseError {
pub fn description(&self) -> &str {
&self.description
}
pub fn error_code(&self) -> Option<Integer> {
self.error_code
}
pub fn can_retry(&self) -> bool {
self.retry_after.is_some()
}
pub fn retry_after(&self) -> Option<u64> {
self.retry_after.and_then(|x| x.try_into().ok())
}
pub fn migrate_to_chat_id(&self) -> Option<Integer> {
self.migrate_to_chat_id
}
}
impl Error for ResponseError {}
impl fmt::Display for ResponseError {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
write!(out, "a telegram error has occurred: description={}", self.description)?;
if let Some(code) = self.error_code {
write!(out, "; error_code={code}")?;
}
if let Some(chat_id) = self.migrate_to_chat_id {
write!(out, "; migrate_to_chat_id={chat_id}")?;
}
if let Some(retry_after) = self.retry_after {
write!(out, "; retry_after={retry_after}")?;
}
Ok(())
}
}
#[derive(Clone, Debug, Deserialize)]
struct RawResponse<T> {
ok: bool,
result: Option<T>,
description: Option<String>,
error_code: Option<Integer>,
parameters: Option<RawResponseParameters>,
}
#[derive(Clone, Copy, Debug, Deserialize)]
struct RawResponseParameters {
migrate_to_chat_id: Option<Integer>,
retry_after: Option<Integer>,
}
#[cfg(test)]
mod tests {
use serde::Deserialize;
use super::*;
#[derive(Clone, Debug, Deserialize)]
struct Object {
name: String,
}
#[test]
fn deserialize() {
let success: Response<Object> = serde_json::from_value(serde_json::json!({
"ok": true,
"result": {"name": "test" }
}))
.unwrap();
if let Response::Success(ref obj) = success {
assert_eq!(obj.name, String::from("test"));
} else {
panic!("Unexpected response: {success:?}");
}
let error: Response<Object> = serde_json::from_value(serde_json::json!({
"ok": false,
"description": "test err",
"error_code": 1,
"parameters": {
"migrate_to_chat_id": 2,
"retry_after": 3
}
}))
.unwrap();
if let Response::Error(err) = error {
assert_eq!(
err.to_string(),
"a telegram error has occurred: description=test err; error_code=1; migrate_to_chat_id=2; retry_after=3"
);
assert_eq!(err.description(), "test err");
assert_eq!(err.error_code(), Some(1));
assert!(err.can_retry());
assert_eq!(err.retry_after(), Some(3));
assert_eq!(err.migrate_to_chat_id(), Some(2));
} else {
panic!("Unexpected response: {success:?}");
}
let error: Response<Object> = serde_json::from_value(serde_json::json!({
"ok": false,
"description": "test err"
}))
.unwrap();
if let Response::Error(err) = error {
assert_eq!(err.to_string(), "a telegram error has occurred: description=test err");
assert_eq!(err.description(), "test err");
assert!(!err.can_retry());
assert!(err.retry_after().is_none());
assert!(err.error_code().is_none());
assert!(err.migrate_to_chat_id().is_none());
} else {
panic!("Unexpected response: {success:?}");
}
let rep: RawResponse<Object> = serde_json::from_value(serde_json::json!({
"ok": true
}))
.unwrap();
let Response::Error(err) = Response::from(rep) else {
panic!("got an unexpected response")
};
assert_eq!(err.description(), "response is ok, but result is not provided");
}
}