use asknothingx2_util::api::APIRequest;
use chrono::{DateTime, FixedOffset};
use serde::{de, Deserialize, Deserializer, Serialize};
use url::Url;
use crate::{serde_util::serialize_none_as_empty_string, Error, GetUsersError, Result};
crate::impl_endpoint!(
/// https://dev.twitch.tv/docs/api/reference/#get-users
/// To include the user’s verified email address in the response,
/// you must use a user access token that includes the
/// user:read:email scope.
GetUsers {
id: Vec<String>,
login: Vec<String>
};
new = {
params = {},
init = {
id :Vec::new(),
login: Vec::new()
}
},
url = ["users"],
);
impl GetUsers {
fn specify_check(&self) -> usize {
self.id.len() + self.login.len()
}
pub fn add_id<S>(mut self, id: S) -> Result<Self>
where
S: Into<String>,
{
if self.specify_check() == 100 {
Err(Error::UserError(GetUsersError::AddIdError(format!(
"error add id curret len: {}",
self.id.len() + self.login.len()
))))
} else {
self.id.push(id.into());
Ok(self)
}
}
pub fn add_ids<S>(mut self, ids: Vec<S>) -> Result<Self>
where
S: Into<String>,
{
if self.specify_check() + ids.len() > 100 {
Err(Error::UserError(GetUsersError::AddIdError(format!(
"error add id curret len: {}",
self.specify_check() + ids.len()
))))
} else {
self.id
.extend(ids.into_iter().map(|x| x.into()).collect::<Vec<String>>());
Ok(self)
}
}
pub fn add_login<S>(mut self, login: S) -> Result<Self>
where
S: Into<String>,
{
if self.specify_check() == 100 {
Err(Error::UserError(GetUsersError::AddIdError(format!(
"error add id curret len: {}",
self.specify_check()
))))
} else {
self.login.push(login.into());
Ok(self)
}
}
pub fn add_logins<S>(mut self, logins: Vec<S>) -> Result<Self>
where
S: Into<String>,
{
if self.specify_check() + logins.len() > 100 {
Err(Error::UserError(GetUsersError::AddIdError(format!(
"error add id curret len: {}",
self.specify_check() + logins.len()
))))
} else {
self.login.extend(
logins
.into_iter()
.map(|x| x.into())
.collect::<Vec<String>>(),
);
Ok(self)
}
}
}
impl APIRequest for GetUsers {
crate::impl_api_request_method!(GET);
crate::impl_api_request_header!();
fn url(&self) -> Url {
let mut url = self.get_url();
if !self.id.is_empty() {
let ids = self
.id
.clone()
.into_iter()
.map(|x| ("id".to_string(), x.to_string()))
.collect::<Vec<(String, String)>>();
url.query_pairs_mut().extend_pairs(ids);
}
if !self.login.is_empty() {
let logins = self
.login
.clone()
.into_iter()
.map(|x| ("login".to_string(), x.to_string()))
.collect::<Vec<(String, String)>>();
url.query_pairs_mut().extend_pairs(logins);
}
url
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum GetUserType {
Admin,
GlobalMod,
Staff,
Normal,
}
impl Serialize for GetUserType {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
GetUserType::Admin => serializer.serialize_str("admin"),
GetUserType::GlobalMod => serializer.serialize_str("global_mod"),
GetUserType::Staff => serializer.serialize_str("staff"),
GetUserType::Normal => serializer.serialize_str(""),
}
}
}
impl<'de> Deserialize<'de> for GetUserType {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"admin" => Ok(GetUserType::Admin),
"global_mod" => Ok(GetUserType::GlobalMod),
"staff" => Ok(GetUserType::Staff),
"" => Ok(GetUserType::Normal),
_ => Err(de::Error::custom("invalid user type")),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum BroadcasterType {
Affiliate,
Partner,
Normal,
}
impl Serialize for BroadcasterType {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
BroadcasterType::Normal => serializer.serialize_str(""),
BroadcasterType::Affiliate => serializer.serialize_str("affiliate"),
BroadcasterType::Partner => serializer.serialize_str("partner"),
}
}
}
impl<'de> Deserialize<'de> for BroadcasterType {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"" => Ok(BroadcasterType::Normal),
"affiliate" => Ok(BroadcasterType::Affiliate),
"partner" => Ok(BroadcasterType::Partner),
_ => Err(de::Error::custom("invalid broadcaster type")),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GetUsersResponse {
pub data: Vec<User>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub login: String,
pub display_name: String,
#[serde(rename = "type")]
pub kind: GetUserType,
pub broadcaster_type: BroadcasterType,
pub description: String,
pub profile_image_url: String,
/// NOTE: This field has been deprecated (see Get Users API endpoint – “view_count” deprecation).
/// Any data in this field is not valid and should not be used.
pub view_count: u64,
pub created_at: DateTime<FixedOffset>,
pub offline_image_url: String,
/// if the user access token includes the user:read:email scope.
#[serde(serialize_with = "serialize_none_as_empty_string")]
pub email: Option<String>,
}
#[cfg(test)]
mod tests {
use crate::{
api_general, expect_APIRequest, expect_headers, expect_response_json, GetUsersResponse,
};
use super::GetUsers;
#[test]
fn get_users_id() {
let get_user = api_general!(GetUsers);
let get_user = get_user.add_id("141981764".to_string()).unwrap();
let expected_headers = expect_headers!();
expect_APIRequest!(
GET,
expected_headers,
"https://api.twitch.tv/helix/users?id=141981764",
json = None,
text = None,
urlencoded = None,
get_user
);
}
#[test]
fn get_users_login() {
let get_user = api_general!(GetUsers);
let get_user = get_user.add_login("twitchdev".to_string()).unwrap();
let expected_headers = expect_headers!();
expect_APIRequest!(
GET,
expected_headers,
"https://api.twitch.tv/helix/users?login=twitchdev",
json = None,
text = None,
urlencoded = None,
get_user
);
}
#[test]
fn get_users_id_login() {
let get_user = api_general!(GetUsers);
let get_user = get_user
.add_login("twitchdev".to_string())
.unwrap()
.add_id("141981764".to_string())
.unwrap();
let expected_headers = expect_headers!();
expect_APIRequest!(
GET,
expected_headers,
"https://api.twitch.tv/helix/users?id=141981764&login=twitchdev",
json = None,
text = None,
urlencoded = None,
get_user
);
}
#[test]
fn get_users_login_max() {
let get_user = api_general!(GetUsers);
let get_user = get_user.add_login("twitchdev".to_string()).unwrap();
let over_logins = vec!["twitchdev"; 100];
let get_user = get_user.add_logins(over_logins);
assert!(get_user.is_err());
}
#[test]
fn response_get_users() {
expect_response_json!("{\n \"data\": [\n {\n \"id\": \"141981764\",\n \"login\": \"twitchdev\",\n \"display_name\": \"TwitchDev\",\n \"type\": \"\",\n \"broadcaster_type\": \"partner\",\n \"description\": \"Supporting third-party developers building Twitch integrations from chatbots to game integrations.\",\n \"profile_image_url\": \"https://static-cdn.jtvnw.net/jtv_user_pictures/8a6381c7-d0c0-4576-b179-38bd5ce1d6af-profile_image-300x300.png\",\n \"offline_image_url\": \"https://static-cdn.jtvnw.net/jtv_user_pictures/3f13ab61-ec78-4fe6-8481-8682cb3b0ac2-channel_offline_image-1920x1080.png\",\n \"view_count\": 5980557,\n \"email\": \"not-real@email.com\",\n \"created_at\": \"2016-12-14T20:32:28Z\"\n }\n ]\n}",
GetUsersResponse
);
}
}