1use std::sync::Arc;
2
3use serde::Deserialize;
4use serde::de::DeserializeOwned;
5
6use crate::access_token::TokenManager;
7use crate::config::Config;
8use crate::error::{Result, WeChatError};
9
10const WECHAT_API_BASE: &str = "https://api.weixin.qq.com/cgi-bin";
11
12pub struct WeChatClient {
14 pub(crate) config: Config,
15 pub(crate) http: reqwest::Client,
16 pub(crate) token_manager: Arc<TokenManager>,
17}
18
19#[derive(Deserialize)]
20struct ApiErrorResponse {
21 errcode: Option<i64>,
22 errmsg: Option<String>,
23}
24
25impl WeChatClient {
26 pub fn new(config: Config) -> Self {
28 let http = reqwest::Client::new();
29 let token_manager = Arc::new(TokenManager::new(&config, http.clone()));
30 Self {
31 config,
32 http,
33 token_manager,
34 }
35 }
36
37 pub async fn access_token(&self) -> Result<String> {
39 self.token_manager.get_token().await
40 }
41
42 pub(crate) async fn get<T: DeserializeOwned>(
44 &self,
45 path: &str,
46 query: &[(&str, &str)],
47 ) -> Result<T> {
48 let token = self.access_token().await?;
49 let url = format!("{}{}", WECHAT_API_BASE, path);
50
51 let mut params = vec![("access_token", token.as_str())];
52 params.extend(query.iter().map(|(k, v)| (*k, *v)));
53
54 let resp = self.http.get(&url).query(¶ms).send().await?;
55 self.parse_response(resp).await
56 }
57
58 pub(crate) async fn post_json<T: DeserializeOwned>(
60 &self,
61 path: &str,
62 body: &impl serde::Serialize,
63 ) -> Result<T> {
64 let token = self.access_token().await?;
65 let url = format!("{}{}?access_token={}", WECHAT_API_BASE, path, token);
66
67 let resp = self.http.post(&url).json(body).send().await?;
68 self.parse_response(resp).await
69 }
70
71 async fn parse_response<T: DeserializeOwned>(&self, resp: reqwest::Response) -> Result<T> {
73 let text = resp.text().await?;
74
75 if let Ok(err_resp) = serde_json::from_str::<ApiErrorResponse>(&text) {
77 if let Some(errcode) = err_resp.errcode {
78 if errcode != 0 {
79 return Err(WeChatError::Api {
80 errcode,
81 errmsg: err_resp.errmsg.unwrap_or_default(),
82 });
83 }
84 }
85 }
86
87 serde_json::from_str(&text).map_err(WeChatError::from)
88 }
89}