Skip to main content

vn_core/model/
character.rs

1use super::release::Release;
2use super::r#trait::Trait;
3use super::visual_novel::VisualNovel;
4use super::{QueryField, SortQueryBy};
5use crate::{impl_id_newtype, impl_into_field_set};
6use regex::Regex;
7use serde::{Deserialize, Deserializer, Serialize};
8use std::result::Result as StdResult;
9use std::sync::{Arc, LazyLock};
10use strum::{Display, EnumIs, EnumString, VariantArray};
11
12static ID_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^c\d+$").unwrap());
13
14#[remain::sorted]
15#[derive(Clone, Debug, Deserialize, Serialize)]
16#[cfg_attr(feature = "specta", derive(specta::Type))]
17pub struct Character {
18  pub age: Option<u32>,
19  pub aliases: Option<Vec<String>>,
20  pub birthday: Option<CharacterBirthday>,
21  pub blood_type: Option<String>,
22  pub bust: Option<u32>,
23  pub cup: Option<String>,
24  pub description: Option<String>,
25  pub gender: Option<CharacterGender>,
26  pub height: Option<u32>,
27  pub hips: Option<u32>,
28  pub id: CharacterId,
29  pub image: Option<CharacterImage>,
30  pub name: Option<String>,
31  pub original: Option<String>,
32  pub sex: Option<CharacterSex>,
33  pub traits: Option<Vec<CharacterTrait>>,
34  pub vns: Option<Vec<CharacterVisualNovel>>,
35  pub waist: Option<u32>,
36  pub weight: Option<u32>,
37}
38
39impl From<Character> for CharacterId {
40  fn from(c: Character) -> Self {
41    c.id
42  }
43}
44
45#[derive(
46  Debug,
47  Deserialize,
48  Serialize,
49  PartialEq,
50  Eq,
51  PartialOrd,
52  Ord,
53  Hash,
54  derive_more::Display,
55  derive_more::Into,
56)]
57#[cfg_attr(feature = "specta", derive(specta::Type))]
58pub struct CharacterId(#[cfg_attr(feature = "specta", specta(type = String))] Arc<str>);
59
60impl CharacterId {
61  pub const PREFIX: char = 'c';
62}
63
64impl_id_newtype!(Character, CharacterId, ID_REGEX);
65
66#[remain::sorted]
67#[derive(Clone, Debug, Serialize)]
68#[cfg_attr(feature = "specta", derive(specta::Type))]
69pub struct CharacterBirthday {
70  pub day: u32,
71  pub month: u32,
72}
73
74impl<'de> Deserialize<'de> for CharacterBirthday {
75  fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
76  where
77    D: Deserializer<'de>,
78  {
79    let date: [u32; 2] = Deserialize::deserialize(deserializer)?;
80    Ok(Self { day: date[1], month: date[0] })
81  }
82}
83
84#[remain::sorted]
85#[derive(Clone, Debug, Serialize)]
86#[cfg_attr(feature = "specta", derive(specta::Type))]
87pub struct CharacterGender {
88  pub apparent: Option<CharacterGenderValue>,
89  pub real: Option<CharacterGenderValue>,
90}
91
92impl<'de> Deserialize<'de> for CharacterGender {
93  fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
94  where
95    D: Deserializer<'de>,
96  {
97    type Value = Option<CharacterGenderValue>;
98    let array: [Value; 2] = Deserialize::deserialize(deserializer)?;
99    Ok(Self { apparent: array[0], real: array[1] })
100  }
101}
102
103#[remain::sorted]
104#[derive(
105  Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash, Display, EnumIs, EnumString,
106)]
107#[cfg_attr(feature = "specta", derive(specta::Type))]
108pub enum CharacterGenderValue {
109  #[serde(rename = "a")]
110  #[strum(serialize = "a")]
111  Ambiguous,
112
113  #[serde(rename = "f")]
114  #[strum(serialize = "f")]
115  Female,
116
117  #[serde(rename = "m")]
118  #[strum(serialize = "m")]
119  Male,
120
121  #[serde(rename = "o")]
122  #[strum(serialize = "o")]
123  NonBinary,
124}
125
126#[remain::sorted]
127#[derive(Clone, Debug, Deserialize, Serialize)]
128#[cfg_attr(feature = "specta", derive(specta::Type))]
129pub struct CharacterImage {
130  pub dims: Option<[u32; 2]>,
131  pub id: Option<String>,
132  pub sexual: Option<f32>,
133  pub url: Option<String>,
134  pub violence: Option<f32>,
135  pub votecount: Option<u32>,
136}
137
138#[remain::sorted]
139#[derive(Clone, Debug, Serialize)]
140#[cfg_attr(feature = "specta", derive(specta::Type))]
141pub struct CharacterSex {
142  pub apparent: Option<CharacterSexValue>,
143  pub real: Option<CharacterSexValue>,
144}
145
146impl<'de> Deserialize<'de> for CharacterSex {
147  fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
148  where
149    D: Deserializer<'de>,
150  {
151    type Value = Option<CharacterSexValue>;
152    let array: [Value; 2] = Deserialize::deserialize(deserializer)?;
153    Ok(Self { apparent: array[0], real: array[1] })
154  }
155}
156
157#[remain::sorted]
158#[derive(
159  Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash, Display, EnumIs, EnumString,
160)]
161#[cfg_attr(feature = "specta", derive(specta::Type))]
162pub enum CharacterSexValue {
163  #[serde(rename = "b")]
164  #[strum(serialize = "b")]
165  Both,
166
167  #[serde(rename = "f")]
168  #[strum(serialize = "f")]
169  Female,
170
171  #[serde(rename = "m")]
172  #[strum(serialize = "m")]
173  Male,
174
175  #[serde(rename = "n")]
176  #[strum(serialize = "n")]
177  None,
178}
179
180#[remain::sorted]
181#[derive(Clone, Debug, Deserialize, Serialize)]
182#[cfg_attr(feature = "specta", derive(specta::Type))]
183pub struct CharacterTrait {
184  pub lie: Option<bool>,
185  #[serde(flatten)]
186  pub r#trait: Trait,
187  pub spoiler: Option<u8>,
188}
189
190impl From<CharacterTrait> for Trait {
191  fn from(ct: CharacterTrait) -> Self {
192    ct.r#trait
193  }
194}
195
196#[remain::sorted]
197#[derive(Clone, Debug, Deserialize, Serialize)]
198#[cfg_attr(feature = "specta", derive(specta::Type))]
199pub struct CharacterVisualNovel {
200  pub release: Option<Release>,
201  pub role: Option<String>,
202  pub spoiler: Option<u8>,
203  #[serde(flatten)]
204  pub visual_novel: VisualNovel,
205}
206
207impl From<CharacterVisualNovel> for VisualNovel {
208  fn from(cvn: CharacterVisualNovel) -> Self {
209    cvn.visual_novel
210  }
211}
212
213#[non_exhaustive]
214#[remain::sorted]
215#[derive(Clone, Copy, Debug, Deserialize, Serialize, Display, EnumString, VariantArray)]
216#[cfg_attr(feature = "specta", derive(specta::Type))]
217pub enum CharacterField {
218  #[serde(rename = "age")]
219  #[strum(serialize = "age")]
220  Age,
221
222  #[serde(rename = "aliases")]
223  #[strum(serialize = "aliases")]
224  Aliases,
225
226  #[serde(rename = "birthday")]
227  #[strum(serialize = "birthday")]
228  Birthday,
229
230  #[serde(rename = "blood_type")]
231  #[strum(serialize = "blood_type")]
232  BloodType,
233
234  #[serde(rename = "bust")]
235  #[strum(serialize = "bust")]
236  Bust,
237
238  #[serde(rename = "cup")]
239  #[strum(serialize = "cup")]
240  Cup,
241
242  #[serde(rename = "description")]
243  #[strum(serialize = "description")]
244  Description,
245
246  #[serde(rename = "gender")]
247  #[strum(serialize = "gender")]
248  Gender,
249
250  #[serde(rename = "height")]
251  #[strum(serialize = "height")]
252  Height,
253
254  #[serde(rename = "hips")]
255  #[strum(serialize = "hips")]
256  Hips,
257
258  #[serde(rename = "id")]
259  #[strum(serialize = "id")]
260  Id,
261
262  #[serde(rename = "image.id")]
263  #[strum(serialize = "image.id")]
264  ImageId,
265
266  #[serde(rename = "image.url")]
267  #[strum(serialize = "image.url")]
268  ImageUrl,
269
270  #[serde(rename = "name")]
271  #[strum(serialize = "name")]
272  Name,
273
274  #[serde(rename = "original")]
275  #[strum(serialize = "original")]
276  Original,
277
278  #[serde(rename = "sex")]
279  #[strum(serialize = "sex")]
280  Sex,
281
282  #[serde(rename = "traits.id")]
283  #[strum(serialize = "traits.id")]
284  TraitId,
285
286  #[serde(rename = "traits.lie")]
287  #[strum(serialize = "traits.lie")]
288  TraitLie,
289
290  #[serde(rename = "traits.spoiler")]
291  #[strum(serialize = "traits.spoiler")]
292  TraitSpoiler,
293
294  #[serde(rename = "vns.aliases")]
295  #[strum(serialize = "vns.aliases")]
296  VisualNovelAliases,
297
298  #[serde(rename = "vns.alttitle")]
299  #[strum(serialize = "vns.alttitle")]
300  VisualNovelAltTitle,
301
302  #[serde(rename = "vns.id")]
303  #[strum(serialize = "vns.id")]
304  VisualNovelId,
305
306  #[serde(rename = "vns.release.id")]
307  #[strum(serialize = "vns.release.id")]
308  VisualNovelReleaseId,
309
310  #[serde(rename = "vns.role")]
311  #[strum(serialize = "vns.role")]
312  VisualNovelRole,
313
314  #[serde(rename = "vns.spoiler")]
315  #[strum(serialize = "vns.spoiler")]
316  VisualNovelSpoiler,
317
318  #[serde(rename = "vns.title")]
319  #[strum(serialize = "vns.title")]
320  VisualNovelTitle,
321
322  #[serde(rename = "waist")]
323  #[strum(serialize = "waist")]
324  Waist,
325
326  #[serde(rename = "weight")]
327  #[strum(serialize = "weight")]
328  Weight,
329}
330
331impl QueryField for CharacterField {}
332
333impl_into_field_set!(CharacterField);
334
335#[non_exhaustive]
336#[remain::sorted]
337#[derive(Clone, Copy, Debug, Deserialize, Serialize, Display, EnumString)]
338#[cfg_attr(feature = "specta", derive(specta::Type))]
339pub enum SortCharacterBy {
340  #[serde(rename = "id")]
341  #[strum(serialize = "id")]
342  Id,
343
344  #[serde(rename = "name")]
345  #[strum(serialize = "name")]
346  Name,
347
348  #[serde(rename = "searchrank")]
349  #[strum(serialize = "searchrank")]
350  SearchRank,
351}
352
353impl SortQueryBy for SortCharacterBy {}