use std::fmt::{self, Formatter};
use anyhow::Result;
use derive_more::*;
use serde::{de::DeserializeOwned, Serialize};
use crate::ApiResponse;
#[derive(Debug, Clone, From, Into, FromStr, Display)]
pub struct ApiToken(String);
pub struct PostFn(pub Box<dyn Fn(String, String) -> Result<String> + Send + Sync>);
impl<T> From<T> for PostFn
where
T: Fn(String, String) -> Result<String> + Send + Sync + 'static,
{
fn from(f: T) -> Self {
Self(Box::new(f))
}
}
impl fmt::Debug for PostFn {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("PostFn")
}
}
#[async_trait::async_trait]
pub trait Post {
async fn post(&self, method: String, req: String) -> Result<String>;
}
pub struct Client {
base_url: String,
client: reqwest::Client,
post_handler: Option<Box<dyn Post + Send + Sync>>,
post_handler_fn: Option<PostFn>,
}
impl Client {
pub fn new(token: ApiToken) -> Self {
Self {
base_url: format!("https://api.telegram.org/bot{token}"),
client: reqwest::Client::new(),
post_handler: None,
post_handler_fn: None,
}
}
pub fn with_post_handler_fn(mut self, post_fn: impl Into<PostFn>) -> Self {
self.post_handler_fn = Some(post_fn.into());
self
}
pub fn with_post_handler(mut self, post_handler: impl Post + Send + Sync + 'static) -> Self {
self.post_handler = Some(Box::new(post_handler));
self
}
pub async fn post<Req, Resp>(&self, method: &str, req: &Req) -> Result<Resp>
where
Req: crate::Request,
Resp: Serialize + DeserializeOwned + Clone,
{
let body;
if let Some(ref post_handler) = self.post_handler_fn {
body = (post_handler.0)(method.to_string(), serde_json::to_string(req)?).unwrap();
} else if let Some(ref post_handler) = self.post_handler {
body = post_handler
.post(method.to_string(), serde_json::to_string(req)?)
.await?;
} else {
debug!(
"POST /{}:\n{}",
method,
serde_json::to_string_pretty(req).unwrap()
);
body = self
.client
.post(format!("{}/{}", self.base_url, method))
.json(&req)
.send()
.await?
.text()
.await?;
}
let response = ApiResponse::<Resp>::from_str(&body)?;
debug!(
"Response /{}:\n{}",
method,
serde_json::to_string_pretty(&response).unwrap()
);
Ok(response.result()?.clone())
}
}