wechat_backend_auth/
types.rs

1use secrecy::{ExposeSecret, SecretString};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::time::Duration;
5
6/// 微信应用ID(公众号、开放平台、移动应用通用)
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct AppId(String);
9
10impl AppId {
11    pub fn new(id: impl Into<String>) -> Self {
12        Self(id.into())
13    }
14
15    pub fn as_str(&self) -> &str {
16        &self.0
17    }
18}
19
20impl fmt::Display for AppId {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        write!(f, "{}", self.0)
23    }
24}
25
26/// 应用密钥
27#[derive(Clone)]
28pub struct AppSecret(SecretString);
29
30impl AppSecret {
31    pub fn new(secret: impl Into<String>) -> Self {
32        Self(SecretString::new(secret.into()))
33    }
34
35    pub fn expose_secret(&self) -> &str {
36        self.0.expose_secret()
37    }
38}
39
40impl fmt::Debug for AppSecret {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        f.debug_tuple("AppSecret").field(&"[REDACTED]").finish()
43    }
44}
45
46impl Serialize for AppSecret {
47    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
48    where
49        S: serde::Serializer,
50    {
51        serializer.serialize_str(self.0.expose_secret())
52    }
53}
54
55impl<'de> Deserialize<'de> for AppSecret {
56    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
57    where
58        D: serde::Deserializer<'de>,
59    {
60        let s = String::deserialize(deserializer)?;
61        Ok(Self::new(s))
62    }
63}
64
65/// 授权码(前端通过微信授权获取的临时授权码,有效期5分钟,只能使用一次)
66#[derive(Clone)]
67pub struct AuthorizationCode(SecretString);
68
69impl AuthorizationCode {
70    pub fn new(code: impl Into<String>) -> Self {
71        Self(SecretString::new(code.into()))
72    }
73
74    pub fn expose_secret(&self) -> &str {
75        self.0.expose_secret()
76    }
77}
78
79impl fmt::Debug for AuthorizationCode {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        f.debug_tuple("AuthorizationCode")
82            .field(&"[REDACTED]")
83            .finish()
84    }
85}
86
87impl Serialize for AuthorizationCode {
88    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
89    where
90        S: serde::Serializer,
91    {
92        serializer.serialize_str(self.0.expose_secret())
93    }
94}
95
96impl<'de> Deserialize<'de> for AuthorizationCode {
97    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98    where
99        D: serde::Deserializer<'de>,
100    {
101        let s = String::deserialize(deserializer)?;
102        Ok(Self::new(s))
103    }
104}
105
106/// 访问令牌(有效期2小时)
107#[derive(Clone)]
108pub struct AccessToken(SecretString);
109
110impl AccessToken {
111    pub fn new(token: impl Into<String>) -> Self {
112        Self(SecretString::new(token.into()))
113    }
114
115    pub fn expose_secret(&self) -> &str {
116        self.0.expose_secret()
117    }
118}
119
120impl fmt::Debug for AccessToken {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.debug_tuple("AccessToken").field(&"[REDACTED]").finish()
123    }
124}
125
126impl Serialize for AccessToken {
127    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
128    where
129        S: serde::Serializer,
130    {
131        serializer.serialize_str(self.0.expose_secret())
132    }
133}
134
135impl<'de> Deserialize<'de> for AccessToken {
136    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
137    where
138        D: serde::Deserializer<'de>,
139    {
140        let s = String::deserialize(deserializer)?;
141        Ok(Self::new(s))
142    }
143}
144
145/// 刷新令牌(有效期30天)
146#[derive(Clone)]
147pub struct RefreshToken(SecretString);
148
149impl RefreshToken {
150    pub fn new(token: impl Into<String>) -> Self {
151        Self(SecretString::new(token.into()))
152    }
153
154    pub fn expose_secret(&self) -> &str {
155        self.0.expose_secret()
156    }
157}
158
159impl fmt::Debug for RefreshToken {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        f.debug_tuple("RefreshToken").field(&"[REDACTED]").finish()
162    }
163}
164
165impl Serialize for RefreshToken {
166    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
167    where
168        S: serde::Serializer,
169    {
170        serializer.serialize_str(self.0.expose_secret())
171    }
172}
173
174impl<'de> Deserialize<'de> for RefreshToken {
175    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
176    where
177        D: serde::Deserializer<'de>,
178    {
179        let s = String::deserialize(deserializer)?;
180        Ok(Self::new(s))
181    }
182}
183
184/// 用户唯一标识(OpenID)
185#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
186pub struct OpenId(String);
187
188impl OpenId {
189    pub fn new(id: impl Into<String>) -> Self {
190        Self(id.into())
191    }
192
193    pub fn as_str(&self) -> &str {
194        &self.0
195    }
196}
197
198impl fmt::Display for OpenId {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        write!(f, "{}", self.0)
201    }
202}
203
204/// 开放平台统一标识(UnionID)
205#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
206pub struct UnionId(String);
207
208impl UnionId {
209    pub fn new(id: impl Into<String>) -> Self {
210        Self(id.into())
211    }
212
213    pub fn as_str(&self) -> &str {
214        &self.0
215    }
216}
217
218impl fmt::Display for UnionId {
219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220        write!(f, "{}", self.0)
221    }
222}
223
224/// 微信API原始响应(用于解析)
225#[derive(Debug, Deserialize)]
226pub(crate) struct RawResponse<T> {
227    #[serde(default)]
228    pub errcode: Option<i32>,
229    #[serde(default)]
230    pub errmsg: Option<String>,
231    #[serde(flatten)]
232    pub data: Option<T>,
233}
234
235/// Token响应的原始数据
236#[derive(Debug, Deserialize)]
237pub(crate) struct RawTokenResponse {
238    pub access_token: String,
239    #[serde(default)]
240    pub refresh_token: Option<String>,
241    pub openid: String,
242    #[serde(default)]
243    pub unionid: Option<String>,
244    pub expires_in: i64,
245    pub scope: String,
246}
247
248/// Token换取响应
249#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct TokenResponse {
251    pub access_token: AccessToken,
252    pub refresh_token: RefreshToken,
253    pub openid: OpenId,
254    pub unionid: Option<UnionId>,
255    pub expires_in: Duration,
256    pub scope: String,
257}
258
259impl From<RawTokenResponse> for TokenResponse {
260    fn from(raw: RawTokenResponse) -> Self {
261        Self {
262            access_token: AccessToken::new(raw.access_token),
263            refresh_token: RefreshToken::new(raw.refresh_token.unwrap_or_default()),
264            openid: OpenId::new(raw.openid),
265            unionid: raw.unionid.map(UnionId::new),
266            expires_in: Duration::from_secs(raw.expires_in as u64),
267            scope: raw.scope,
268        }
269    }
270}
271
272/// 用户信息
273#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct UserInfo {
275    /// 用户唯一标识
276    pub openid: OpenId,
277    /// 昵称
278    pub nickname: String,
279    /// 性别(0-未知,1-男,2-女)
280    pub sex: u8,
281    /// 省份
282    pub province: String,
283    /// 城市
284    pub city: String,
285    /// 国家
286    pub country: String,
287    /// 头像URL
288    pub headimgurl: String,
289    /// 开放平台统一标识(可选)
290    #[serde(default)]
291    pub unionid: Option<UnionId>,
292    /// 用户特权信息
293    #[serde(default)]
294    pub privilege: Vec<String>,
295}