use api_builder::{Endpoint, QueryParamPair, QueryParamPairs, api_endpoint};
use serde::{Deserialize, Deserializer, Serialize};
use time::OffsetDateTime;
use typed_builder::TypedBuilder;
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Serialize, TypedBuilder,
)]
#[builder(field_defaults(setter(into)))]
pub struct GetUsers<'a> {
#[serde(skip)]
pub project_id: &'a str,
#[builder(default, setter(strip_option))]
pub user_key: Option<&'a str>,
#[builder(default, setter(strip_option))]
pub discord_id: Option<&'a str>,
#[builder(default, setter(strip_option))]
pub identifier: Option<&'a str>,
#[builder(default, setter(strip_option))]
pub search: Option<&'a str>,
#[builder(default, setter(strip_option))]
pub from: Option<u32>,
#[builder(default, setter(strip_option))]
pub until: Option<u32>,
}
#[api_endpoint(method = GET, path = format!("/v3/projects/{}/users", self.project_id), self_as_body = "application/json")]
impl Endpoint for GetUsers<'_> {
fn query_params(&self) -> Option<QueryParamPairs> {
let mut params = QueryParamPairs::default();
if let Some(x) = self.user_key {
params.push(QueryParamPair::new("user_key", x.to_string()));
}
if let Some(x) = self.discord_id {
params.push(QueryParamPair::new("discord_id", x.to_string()));
}
if let Some(x) = self.identifier {
params.push(QueryParamPair::new("identifier", x.to_string()));
}
if let Some(x) = self.search {
params.push(QueryParamPair::new("search", x.to_string()));
}
if let Some(x) = self.from {
params.push(QueryParamPair::new("from", x.to_string()));
}
if let Some(x) = self.until {
params.push(QueryParamPair::new("until", x.to_string()));
}
Some(params)
}
}
fn deserialize_empty_string<'a, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'a>,
{
let x = String::deserialize(deserializer)?;
match x.as_str() {
"" => Ok(None),
_ => Ok(Some(x)),
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub enum UserIdentifierType {
HWID,
#[default]
None,
}
impl<'de> Deserialize<'de> for UserIdentifierType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match String::deserialize(deserializer)?.as_str() {
"HWID" => Ok(Self::HWID),
_ => Ok(Self::None),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum UserStatus {
Active,
Reset,
Banned,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum UserExpiration {
Never,
Specified(OffsetDateTime),
}
impl<'de> Deserialize<'de> for UserExpiration {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match i64::deserialize(deserializer)? {
x if x < 0 => Ok(Self::Never),
x => Ok(Self::Specified(
OffsetDateTime::from_unix_timestamp(x)
.map_err(|err| serde::de::Error::custom(err))?,
)),
}
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
pub struct User {
#[serde(deserialize_with = "deserialize_empty_string")]
pub user_key: Option<String>,
#[serde(deserialize_with = "deserialize_empty_string")]
pub identifier: Option<String>,
pub identifier_type: UserIdentifierType,
#[serde(deserialize_with = "deserialize_empty_string")]
pub discord_id: Option<String>,
pub status: UserStatus,
#[serde(with = "time::serde::timestamp")]
pub last_reset: OffsetDateTime,
pub total_resets: u32,
pub auth_expire: UserExpiration,
#[serde(deserialize_with = "crate::models::v3::deserialize_number_as_bool")]
pub banned: bool,
#[serde(deserialize_with = "deserialize_empty_string")]
pub ban_reason: Option<String>,
#[serde(with = "time::serde::timestamp")]
pub ban_expire: OffsetDateTime,
#[serde(deserialize_with = "deserialize_empty_string")]
pub unban_token: Option<String>,
pub total_executions: u64,
#[serde(deserialize_with = "deserialize_empty_string")]
pub note: Option<String>,
#[serde(deserialize_with = "deserialize_empty_string")]
pub ban_ip: Option<String>,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)]
pub struct GetUsersResponse {
pub users: Vec<User>,
}