use super::User;
use super::credential::Credential;
use crate::{
Result, constants, error::Error::InternalServer, response::Response,
user::credential::CredentialBuilder,
};
use http::{HeaderValue, Method, Request};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tracing::{debug, instrument};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserInfo {
nickname: String,
gender: u8,
country: String,
province: String,
city: String,
avatar: String,
watermark: Watermark,
}
impl UserInfo {
pub fn nickname(&self) -> &str {
&self.nickname
}
pub fn gender(&self) -> u8 {
self.gender
}
pub fn country(&self) -> &str {
&self.country
}
pub fn province(&self) -> &str {
&self.province
}
pub fn city(&self) -> &str {
&self.city
}
pub fn avatar(&self) -> &str {
&self.avatar
}
pub fn app_id(&self) -> &str {
&self.watermark.app_id
}
pub fn timestamp(&self) -> u64 {
self.watermark.timestamp
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct UserBuilder {
#[serde(rename = "nickName")]
nickname: String,
gender: u8,
country: String,
province: String,
city: String,
#[serde(rename = "avatarUrl")]
avatar: String,
watermark: WatermarkBuilder,
}
impl UserBuilder {
pub(crate) fn build(self) -> UserInfo {
UserInfo {
nickname: self.nickname,
gender: self.gender,
country: self.country,
province: self.province,
city: self.city,
avatar: self.avatar,
watermark: self.watermark.build(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Contact {
phone_number: String,
pure_phone_number: String,
country_code: String,
watermark: Watermark,
}
impl Contact {
pub fn phone_number(&self) -> &str {
&self.phone_number
}
pub fn pure_phone_number(&self) -> &str {
&self.pure_phone_number
}
pub fn country_code(&self) -> &str {
&self.country_code
}
pub fn app_id(&self) -> &str {
&self.watermark.app_id
}
pub fn timestamp(&self) -> u64 {
self.watermark.timestamp
}
}
#[derive(Debug, Deserialize, Clone)]
pub(crate) struct ContactBuilder {
#[serde(rename = "phone_info")]
inner: PhoneInner,
}
impl ContactBuilder {
pub(crate) fn build(self) -> Contact {
Contact {
phone_number: self.inner.phone_number,
pure_phone_number: self.inner.pure_phone_number,
country_code: self.inner.country_code,
watermark: self.inner.watermark.build(),
}
}
}
#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
struct PhoneInner {
#[serde(rename = "phoneNumber")]
phone_number: String,
#[serde(rename = "purePhoneNumber")]
pure_phone_number: String,
country_code: String,
watermark: WatermarkBuilder,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Watermark {
app_id: String,
timestamp: u64,
}
#[derive(Debug, Deserialize, Clone)]
struct WatermarkBuilder {
#[serde(rename = "appid")]
app_id: String,
timestamp: u64,
}
impl WatermarkBuilder {
fn build(self) -> Watermark {
Watermark {
app_id: self.app_id,
timestamp: self.timestamp,
}
}
}
impl User {
#[instrument(skip(self, code))]
pub async fn login(&self, code: &str) -> Result<Credential> {
debug!("code: {}", code);
let mut map: HashMap<&str, &str> = HashMap::new();
let config = self.client.app_config();
map.insert("appid", &config.app_id);
map.insert("secret", &config.secret);
map.insert("js_code", code);
map.insert("grant_type", "authorization_code");
let mut url = url::Url::parse(constants::AUTHENTICATION_END_POINT)?;
url.query_pairs_mut().extend_pairs(&map);
let client = &self.client.client;
let query = serde_json::to_vec(&map)?;
let request = Request::builder()
.uri(url.as_str())
.method(Method::GET)
.header(
"User-Agent",
HeaderValue::from_static(constants::HTTP_CLIENT_USER_AGENT),
)
.body(query)?;
let response = client.execute(request).await?;
debug!("authentication response: {:#?}", &response);
if response.status().is_success() {
let (_parts, body) = response.into_parts();
let json = serde_json::from_slice::<Response<CredentialBuilder>>(&body.to_vec())?;
let credential = json.extract()?.build();
debug!("credential: {:#?}", credential);
Ok(credential)
} else {
let (_parts, body) = response.into_parts();
let message = String::from_utf8_lossy(&body.to_vec()).to_string();
Err(InternalServer(message))
}
}
pub async fn get_contact(&self, code: &str, open_id: Option<&str>) -> Result<Contact> {
debug!("code: {}, open_id: {:?}", code, open_id);
let token = format!("access_token={}", self.client.token().await?);
let mut url = url::Url::parse(constants::PHONE_END_POINT)?;
url.set_query(Some(&token));
let mut body = HashMap::new();
body.insert("code", code);
if let Some(open_id) = open_id {
body.insert("openid", open_id);
}
let client = &self.client.client;
let query = serde_json::to_vec(&body)?;
let request = Request::builder()
.uri(url.as_str())
.method(Method::POST)
.header(
"User-Agent",
HeaderValue::from_static(constants::HTTP_CLIENT_USER_AGENT),
)
.body(query)?;
let response = client.execute(request).await?;
debug!("authentication response: {:#?}", &response);
if response.status().is_success() {
let (_parts, body) = response.into_parts();
let json = serde_json::from_slice::<Response<ContactBuilder>>(&body.to_vec())?;
let builder = json.extract()?;
debug!("contact builder: {:#?}", builder);
Ok(builder.build())
} else {
let (_parts, body) = response.into_parts();
let message = String::from_utf8_lossy(&body.to_vec()).to_string();
Err(InternalServer(message))
}
}
}