1use super::User;
2use super::credential::Credential;
3use crate::{
4 Result, constants, error::Error::InternalServer, response::Response,
5 user::credential::CredentialBuilder,
6};
7use http::{HeaderValue, Method, Request};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use tracing::{debug, instrument};
11
12#[derive(Debug, Serialize, Deserialize, Clone)]
53pub struct UserInfo {
54 nickname: String,
55 gender: u8,
56 country: String,
57 province: String,
58 city: String,
59 avatar: String,
60 watermark: Watermark,
61}
62
63impl UserInfo {
64 pub fn nickname(&self) -> &str {
65 &self.nickname
66 }
67
68 pub fn gender(&self) -> u8 {
69 self.gender
70 }
71
72 pub fn country(&self) -> &str {
73 &self.country
74 }
75
76 pub fn province(&self) -> &str {
77 &self.province
78 }
79
80 pub fn city(&self) -> &str {
81 &self.city
82 }
83
84 pub fn avatar(&self) -> &str {
85 &self.avatar
86 }
87
88 pub fn app_id(&self) -> &str {
89 &self.watermark.app_id
90 }
91
92 pub fn timestamp(&self) -> u64 {
93 self.watermark.timestamp
94 }
95}
96
97#[derive(Debug, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub(crate) struct UserBuilder {
100 #[serde(rename = "nickName")]
101 nickname: String,
102 gender: u8,
103 country: String,
104 province: String,
105 city: String,
106 #[serde(rename = "avatarUrl")]
107 avatar: String,
108 watermark: WatermarkBuilder,
109}
110
111impl UserBuilder {
112 pub(crate) fn build(self) -> UserInfo {
113 UserInfo {
114 nickname: self.nickname,
115 gender: self.gender,
116 country: self.country,
117 province: self.province,
118 city: self.city,
119 avatar: self.avatar,
120 watermark: self.watermark.build(),
121 }
122 }
123}
124
125#[derive(Debug, Serialize, Deserialize, Clone)]
126pub struct Contact {
127 phone_number: String,
128 pure_phone_number: String,
129 country_code: String,
130 watermark: Watermark,
131}
132
133impl Contact {
134 pub fn phone_number(&self) -> &str {
135 &self.phone_number
136 }
137
138 pub fn pure_phone_number(&self) -> &str {
139 &self.pure_phone_number
140 }
141
142 pub fn country_code(&self) -> &str {
143 &self.country_code
144 }
145
146 pub fn app_id(&self) -> &str {
147 &self.watermark.app_id
148 }
149
150 pub fn timestamp(&self) -> u64 {
151 self.watermark.timestamp
152 }
153}
154
155#[derive(Debug, Deserialize, Clone)]
156pub(crate) struct ContactBuilder {
157 #[serde(rename = "phone_info")]
158 inner: PhoneInner,
159}
160
161impl ContactBuilder {
162 pub(crate) fn build(self) -> Contact {
163 Contact {
164 phone_number: self.inner.phone_number,
165 pure_phone_number: self.inner.pure_phone_number,
166 country_code: self.inner.country_code,
167 watermark: self.inner.watermark.build(),
168 }
169 }
170}
171
172#[derive(Debug, Deserialize, Clone)]
173#[serde(rename_all = "camelCase")]
174struct PhoneInner {
175 #[serde(rename = "phoneNumber")]
176 phone_number: String,
177 #[serde(rename = "purePhoneNumber")]
178 pure_phone_number: String,
179 country_code: String,
180 watermark: WatermarkBuilder,
181}
182
183#[derive(Debug, Serialize, Deserialize, Clone)]
184struct Watermark {
185 app_id: String,
186 timestamp: u64,
187}
188
189#[derive(Debug, Deserialize, Clone)]
190struct WatermarkBuilder {
191 #[serde(rename = "appid")]
192 app_id: String,
193 timestamp: u64,
194}
195
196impl WatermarkBuilder {
197 fn build(self) -> Watermark {
198 Watermark {
199 app_id: self.app_id,
200 timestamp: self.timestamp,
201 }
202 }
203}
204
205impl User {
206 #[instrument(skip(self, code))]
247 pub async fn login(&self, code: &str) -> Result<Credential> {
248 debug!("code: {}", code);
249
250 let mut map: HashMap<&str, &str> = HashMap::new();
251 let config = self.client.app_config();
252 map.insert("appid", &config.app_id);
253 map.insert("secret", &config.secret);
254 map.insert("js_code", code);
255 map.insert("grant_type", "authorization_code");
256
257 let mut url = url::Url::parse(constants::AUTHENTICATION_END_POINT)?;
258 url.query_pairs_mut().extend_pairs(&map);
259
260 let client = &self.client.client;
261 let query = serde_json::to_vec(&map)?;
262 let request = Request::builder()
263 .uri(url.as_str())
264 .method(Method::GET)
265 .header(
266 "User-Agent",
267 HeaderValue::from_static(constants::HTTP_CLIENT_USER_AGENT),
268 )
269 .body(query)?;
270
271 let response = client.execute(request).await?;
272 debug!("authentication response: {:#?}", &response);
273
274 if response.status().is_success() {
275 let (_parts, body) = response.into_parts();
276 let json = serde_json::from_slice::<Response<CredentialBuilder>>(&body.to_vec())?;
277
278 let credential = json.extract()?.build();
279
280 debug!("credential: {:#?}", credential);
281
282 Ok(credential)
283 } else {
284 let (_parts, body) = response.into_parts();
285 let message = String::from_utf8_lossy(&body.to_vec()).to_string();
286 Err(InternalServer(message))
287 }
288 }
289
290 pub async fn get_contact(&self, code: &str, open_id: Option<&str>) -> Result<Contact> {
346 debug!("code: {}, open_id: {:?}", code, open_id);
347 let token = format!("access_token={}", self.client.token().await?);
348 let mut url = url::Url::parse(constants::PHONE_END_POINT)?;
349 url.set_query(Some(&token));
350
351 let mut body = HashMap::new();
352 body.insert("code", code);
353
354 if let Some(open_id) = open_id {
355 body.insert("openid", open_id);
356 }
357
358 let client = &self.client.client;
359 let query = serde_json::to_vec(&body)?;
360 let request = Request::builder()
361 .uri(url.as_str())
362 .method(Method::POST)
363 .header(
364 "User-Agent",
365 HeaderValue::from_static(constants::HTTP_CLIENT_USER_AGENT),
366 )
367 .body(query)?;
368
369 let response = client.execute(request).await?;
370 debug!("authentication response: {:#?}", &response);
371
372 if response.status().is_success() {
373 let (_parts, body) = response.into_parts();
374 let json = serde_json::from_slice::<Response<ContactBuilder>>(&body.to_vec())?;
375
376 let builder = json.extract()?;
377
378 debug!("contact builder: {:#?}", builder);
379
380 Ok(builder.build())
381 } else {
382 let (_parts, body) = response.into_parts();
383 let message = String::from_utf8_lossy(&body.to_vec()).to_string();
384 Err(InternalServer(message))
385 }
386 }
387}