wechat_minapp/
qr_code.rs

1use crate::{
2    Client, Result, constants,
3    error::Error::{self, InternalServer},
4};
5use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use tracing::debug;
9
10#[derive(Debug, Serialize, Deserialize, Clone)]
11pub struct QrCode {
12    buffer: Vec<u8>,
13}
14
15impl QrCode {
16    pub fn buffer(&self) -> &Vec<u8> {
17        &self.buffer
18    }
19}
20
21#[derive(Debug, Deserialize)]
22pub struct QrCodeArgs {
23    path: String,
24    width: Option<i16>,
25    auto_color: Option<bool>,
26    line_color: Option<Rgb>,
27    is_hyaline: Option<bool>,
28    env_version: Option<MinappEnvVersion>,
29}
30
31#[derive(Debug, Deserialize)]
32pub struct QrCodeArgBuilder {
33    path: Option<String>,
34    width: Option<i16>,
35    auto_color: Option<bool>,
36    line_color: Option<Rgb>,
37    is_hyaline: Option<bool>,
38    env_version: Option<MinappEnvVersion>,
39}
40#[derive(Debug, Deserialize, Clone, Serialize)]
41pub struct Rgb {
42    r: i16,
43    g: i16,
44    b: i16,
45}
46
47impl Rgb {
48    pub fn new(r: i16, g: i16, b: i16) -> Self {
49        Rgb { r, g, b }
50    }
51}
52impl QrCodeArgs {
53    pub fn builder() -> QrCodeArgBuilder {
54        QrCodeArgBuilder::new()
55    }
56
57    pub fn path(&self) -> String {
58        self.path.clone()
59    }
60
61    pub fn width(&self) -> Option<i16> {
62        self.width
63    }
64
65    pub fn auto_color(&self) -> Option<bool> {
66        self.auto_color
67    }
68
69    pub fn line_color(&self) -> Option<Rgb> {
70        self.line_color.clone()
71    }
72
73    pub fn is_hyaline(&self) -> Option<bool> {
74        self.is_hyaline
75    }
76
77    pub fn env_version(&self) -> Option<MinappEnvVersion> {
78        self.env_version.clone()
79    }
80}
81
82impl Default for QrCodeArgBuilder {
83    fn default() -> Self {
84        Self::new()
85    }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub enum MinappEnvVersion {
90    Release,
91    Trial,
92    Develop,
93}
94
95impl From<MinappEnvVersion> for String {
96    fn from(value: MinappEnvVersion) -> Self {
97        match value {
98            MinappEnvVersion::Develop => "develop".to_string(),
99            MinappEnvVersion::Release => "release".to_string(),
100            MinappEnvVersion::Trial => "trial".to_string(),
101        }
102    }
103}
104impl QrCodeArgBuilder {
105    pub fn new() -> Self {
106        QrCodeArgBuilder {
107            path: None,
108            width: None,
109            auto_color: None,
110            line_color: None,
111            is_hyaline: None,
112            env_version: None,
113        }
114    }
115
116    pub fn path(mut self, path: impl Into<String>) -> Self {
117        self.path = Some(path.into());
118        self
119    }
120
121    pub fn width(mut self, width: i16) -> Self {
122        self.width = Some(width);
123        self
124    }
125
126    pub fn with_auto_color(mut self) -> Self {
127        self.auto_color = Some(true);
128        self
129    }
130
131    pub fn line_color(mut self, color: Rgb) -> Self {
132        self.line_color = Some(color);
133        self
134    }
135
136    pub fn with_is_hyaline(mut self) -> Self {
137        self.is_hyaline = Some(true);
138        self
139    }
140
141    pub fn env_version(mut self, version: MinappEnvVersion) -> Self {
142        self.env_version = Some(version);
143        self
144    }
145
146    pub fn build(self) -> Result<QrCodeArgs> {
147        let path = self.path.map_or_else(
148            || {
149                Err(Error::InvalidParameter(
150                    "小程序页面路径不能为空".to_string(),
151                ))
152            },
153            |v| {
154                if v.len() > 1024 {
155                    return Err(Error::InvalidParameter(
156                        "页面路径最大长度 1024 个字符".to_string(),
157                    ));
158                }
159                Ok(v)
160            },
161        )?;
162
163        Ok(QrCodeArgs {
164            path,
165            width: self.width,
166            auto_color: self.auto_color,
167            line_color: self.line_color,
168            is_hyaline: self.is_hyaline,
169            env_version: self.env_version,
170        })
171    }
172}
173
174impl Client {
175    /// ```ignore
176    /// use wechat_minapp::{Client,QrCodeArgs};
177    ///
178    /// #[tokio::main]
179    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
180    ///     let app_id = "your app id";
181    ///     let secret = "your app secret";
182    ///     
183    ///     let client = Client::new(app_id, secret);
184    ///     let qr_args = QrCodeArgs::builder().path(&page).build()?;
185    ///     let buffer = minapp.qr_code(qr_args).await?;
186    ///
187    ///     Ok(())
188    /// }
189    /// ```
190    ///
191    pub async fn qr_code(&self, args: QrCodeArgs) -> Result<QrCode> {
192        debug!("get qr code args {:?}", &args);
193
194        let mut query = HashMap::new();
195        let mut body = HashMap::new();
196
197        query.insert("access_token", self.access_token().await?);
198        body.insert("path", args.path);
199
200        if let Some(width) = args.width {
201            body.insert("width", width.to_string());
202        }
203
204        if let Some(auto_color) = args.auto_color {
205            body.insert("auto_color", auto_color.to_string());
206        }
207
208        if let Some(line_color) = args.line_color {
209            let value = serde_json::to_string(&line_color)?;
210            body.insert("line_color", value);
211        }
212
213        if let Some(is_hyaline) = args.is_hyaline {
214            body.insert("is_hyaline", is_hyaline.to_string());
215        }
216
217        if let Some(env_version) = args.env_version {
218            body.insert("env_version", env_version.into());
219        }
220
221        let mut headers = HeaderMap::new();
222        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
223        headers.insert("encoding", HeaderValue::from_static("null"));
224
225        let response = self
226            .request()
227            .post(constants::QR_CODE_ENDPOINT)
228            .headers(headers)
229            .query(&query)
230            .json(&body)
231            .send()
232            .await?;
233
234        debug!("response: {:#?}", response);
235
236        if response.status().is_success() {
237            let response = response.bytes().await?;
238
239            Ok(QrCode {
240                buffer: response.to_vec(),
241            })
242        } else {
243            Err(InternalServer(response.text().await?))
244        }
245    }
246}