use super::QueryField;
use crate::http::UrlQueryParams;
use crate::{
impl_id_newtype, impl_into_field_set, impl_string_set, impl_string_set_from_newtype,
impl_string_set_from_numeric,
};
use regex::Regex;
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::{HashMap, HashSet};
use std::ops::{Deref, DerefMut};
use std::result::Result as StdResult;
use std::sync::{Arc, LazyLock};
use strum::{Display, EnumString, VariantArray};
static ID_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^u\d+$").unwrap());
#[remain::sorted]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
pub struct User {
pub id: UserId,
pub lengthvotes: Option<u32>,
pub lengthvotes_sum: Option<u32>,
pub username: String,
}
impl From<User> for UserId {
fn from(u: User) -> Self {
u.id
}
}
#[derive(
Debug,
Deserialize,
Serialize,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
derive_more::Display,
derive_more::Into,
)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
pub struct UserId(#[cfg_attr(feature = "specta", specta(type = String))] Arc<str>);
impl UserId {
pub const PREFIX: char = 'u';
}
impl_id_newtype!(User, UserId, ID_REGEX);
#[derive(Clone, Debug, Default, Serialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
pub struct Users(HashMap<String, User>);
impl<'de> Deserialize<'de> for Users {
fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
where
D: Deserializer<'de>,
{
type Optional = HashMap<String, Option<User>>;
Ok(Users(
Optional::deserialize(deserializer)?
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect(),
))
}
}
impl Users {
pub fn into_inner(self) -> HashMap<String, User> {
self.0
}
}
impl Deref for Users {
type Target = HashMap<String, User>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Users {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[non_exhaustive]
#[remain::sorted]
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Display, EnumString, VariantArray)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
pub enum UserField {
#[serde(rename = "lengthvotes")]
#[strum(serialize = "lengthvotes")]
LengthVotes,
#[serde(rename = "lengthvotes_sum")]
#[strum(serialize = "lengthvotes_sum")]
LengthVotesSum,
}
impl QueryField for UserField {}
impl_into_field_set!(UserField);
#[derive(Clone, Debug, Default)]
pub struct UserUrlQuery(HashSet<String>);
impl UserUrlQuery {
pub const URL_QUERY_PARAM: &'static str = "q";
pub fn into_query(self) -> UrlQueryParams {
UrlQueryParams(
self
.0
.into_iter()
.map(|user| (Self::URL_QUERY_PARAM, user))
.collect(),
)
}
}
impl_string_set!(UserUrlQuery);
impl_string_set_from_numeric!(UserId::PREFIX, UserUrlQuery);
impl_string_set_from_newtype!(UserUrlQuery, UserId);