Skip to main content

vn_core/model/
user.rs

1use super::QueryField;
2use crate::http::UrlQueryParams;
3use crate::{
4  impl_id_newtype, impl_into_field_set, impl_string_set, impl_string_set_from_newtype,
5  impl_string_set_from_numeric,
6};
7use regex::Regex;
8use serde::{Deserialize, Deserializer, Serialize};
9use std::collections::{HashMap, HashSet};
10use std::ops::{Deref, DerefMut};
11use std::result::Result as StdResult;
12use std::sync::{Arc, LazyLock};
13use strum::{Display, EnumString, VariantArray};
14
15static ID_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^u\d+$").unwrap());
16
17#[remain::sorted]
18#[derive(Clone, Debug, Deserialize, Serialize)]
19#[cfg_attr(feature = "specta", derive(specta::Type))]
20pub struct User {
21  pub id: UserId,
22  pub lengthvotes: Option<u32>,
23  pub lengthvotes_sum: Option<u32>,
24  pub username: String,
25}
26
27impl From<User> for UserId {
28  fn from(u: User) -> Self {
29    u.id
30  }
31}
32
33#[derive(
34  Debug,
35  Deserialize,
36  Serialize,
37  PartialEq,
38  Eq,
39  PartialOrd,
40  Ord,
41  Hash,
42  derive_more::Display,
43  derive_more::Into,
44)]
45#[cfg_attr(feature = "specta", derive(specta::Type))]
46pub struct UserId(#[cfg_attr(feature = "specta", specta(type = String))] Arc<str>);
47
48impl UserId {
49  pub const PREFIX: char = 'u';
50}
51
52impl_id_newtype!(User, UserId, ID_REGEX);
53
54#[derive(Clone, Debug, Default, Serialize)]
55#[cfg_attr(feature = "specta", derive(specta::Type))]
56pub struct Users(HashMap<String, User>);
57
58impl<'de> Deserialize<'de> for Users {
59  fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
60  where
61    D: Deserializer<'de>,
62  {
63    type Optional = HashMap<String, Option<User>>;
64    Ok(Users(
65      Optional::deserialize(deserializer)?
66        .into_iter()
67        .filter_map(|(k, v)| v.map(|v| (k, v)))
68        .collect(),
69    ))
70  }
71}
72
73impl Users {
74  pub fn into_inner(self) -> HashMap<String, User> {
75    self.0
76  }
77}
78
79impl Deref for Users {
80  type Target = HashMap<String, User>;
81
82  fn deref(&self) -> &Self::Target {
83    &self.0
84  }
85}
86
87impl DerefMut for Users {
88  fn deref_mut(&mut self) -> &mut Self::Target {
89    &mut self.0
90  }
91}
92
93#[non_exhaustive]
94#[remain::sorted]
95#[derive(Clone, Copy, Debug, Deserialize, Serialize, Display, EnumString, VariantArray)]
96#[cfg_attr(feature = "specta", derive(specta::Type))]
97pub enum UserField {
98  #[serde(rename = "lengthvotes")]
99  #[strum(serialize = "lengthvotes")]
100  LengthVotes,
101
102  #[serde(rename = "lengthvotes_sum")]
103  #[strum(serialize = "lengthvotes_sum")]
104  LengthVotesSum,
105}
106
107impl QueryField for UserField {}
108
109impl_into_field_set!(UserField);
110
111// We should not use `UserId` here, because the query is not restricted to user ids.
112#[derive(Clone, Debug, Default)]
113pub struct UserUrlQuery(HashSet<String>);
114
115impl UserUrlQuery {
116  pub const URL_QUERY_PARAM: &'static str = "q";
117
118  pub fn into_query(self) -> UrlQueryParams {
119    UrlQueryParams(
120      self
121        .0
122        .into_iter()
123        .map(|user| (Self::URL_QUERY_PARAM, user))
124        .collect(),
125    )
126  }
127}
128
129impl_string_set!(UserUrlQuery);
130impl_string_set_from_numeric!(UserId::PREFIX, UserUrlQuery);
131impl_string_set_from_newtype!(UserUrlQuery, UserId);