Skip to main content

vn_core/model/
release.rs

1use super::producer::Producer;
2use super::schema::Language;
3use super::visual_novel::{VisualNovel, VisualNovelId, VisualNovelImage};
4use super::{QueryField, SortQueryBy};
5use crate::{impl_id_newtype, impl_into_field_set};
6use regex::Regex;
7use serde::{Deserialize, Deserializer, Serialize};
8use serde_json::Value as JsonValue;
9use std::sync::{Arc, LazyLock};
10use strum::{Display, EnumIs, EnumString, VariantArray};
11
12static ID_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^r\d+$").unwrap());
13
14#[remain::sorted]
15#[derive(Clone, Debug, Deserialize, Serialize)]
16#[cfg_attr(feature = "specta", derive(specta::Type))]
17pub struct Release {
18  pub alttitle: Option<String>,
19  pub catalog: Option<String>,
20  pub engine: Option<String>,
21  pub extlinks: Option<Vec<ExternalLink>>,
22  pub freeware: Option<bool>,
23  pub gtin: Option<String>,
24  pub has_ero: Option<bool>,
25  pub id: ReleaseId,
26  pub images: Option<Vec<ReleaseImage>>,
27  pub languages: Option<Vec<ReleaseLanguage>>,
28  pub media: Option<Vec<ReleaseMedia>>,
29  pub minage: Option<u32>,
30  pub notes: Option<String>,
31  pub official: Option<bool>,
32  pub patch: Option<bool>,
33  pub platforms: Option<Vec<String>>,
34  pub released: Option<String>,
35  pub resolution: Option<ReleaseResolution>,
36  pub title: Option<String>,
37  pub uncensored: Option<bool>,
38  pub voiced: Option<ReleaseVoiced>,
39}
40
41impl From<Release> for ReleaseId {
42  fn from(r: Release) -> Self {
43    r.id
44  }
45}
46
47#[derive(
48  Debug,
49  Deserialize,
50  Serialize,
51  PartialEq,
52  Eq,
53  PartialOrd,
54  Ord,
55  Hash,
56  derive_more::Display,
57  derive_more::Into,
58)]
59#[cfg_attr(feature = "specta", derive(specta::Type))]
60pub struct ReleaseId(#[cfg_attr(feature = "specta", specta(type = String))] Arc<str>);
61
62impl ReleaseId {
63  pub const PREFIX: char = 'r';
64}
65
66impl_id_newtype!(Release, ReleaseId, ID_REGEX);
67
68#[remain::sorted]
69#[derive(Clone, Debug, Deserialize, Serialize)]
70#[cfg_attr(feature = "specta", derive(specta::Type))]
71pub struct ReleaseImage {
72  #[serde(flatten)]
73  pub image: VisualNovelImage,
74  pub languages: Option<Vec<Language>>,
75  pub photo: Option<bool>,
76  pub r#type: Option<ReleaseImageType>,
77  pub vn: Option<VisualNovelId>,
78}
79
80impl From<ReleaseImage> for VisualNovelImage {
81  fn from(ri: ReleaseImage) -> Self {
82    ri.image
83  }
84}
85
86#[non_exhaustive]
87#[remain::sorted]
88#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash, Display, EnumIs)]
89#[cfg_attr(feature = "specta", derive(specta::Type))]
90pub enum ReleaseImageType {
91  Dig,
92  PkgBack,
93  PkgContent,
94  PkgFront,
95  PkgMed,
96  PkgSide,
97}
98
99#[remain::sorted]
100#[derive(Clone, Debug, Deserialize, Serialize)]
101#[cfg_attr(feature = "specta", derive(specta::Type))]
102pub struct ReleaseLanguage {
103  pub lang: Option<Language>,
104  pub latin: Option<String>,
105  pub main: Option<bool>,
106  pub mtl: Option<bool>,
107  pub title: Option<String>,
108}
109
110#[remain::sorted]
111#[derive(Clone, Debug, Deserialize, Serialize)]
112#[cfg_attr(feature = "specta", derive(specta::Type))]
113pub struct ReleaseMedia {
114  pub medium: Option<String>,
115  pub qty: Option<u32>,
116}
117
118#[remain::sorted]
119#[derive(Clone, Debug, Deserialize, Serialize)]
120#[cfg_attr(feature = "specta", derive(specta::Type))]
121pub struct ReleaseProducer {
122  pub developer: Option<bool>,
123  #[serde(flatten)]
124  pub producer: Producer,
125  pub publisher: Option<bool>,
126}
127
128impl From<ReleaseProducer> for Producer {
129  fn from(rp: ReleaseProducer) -> Self {
130    rp.producer
131  }
132}
133
134#[non_exhaustive]
135#[remain::sorted]
136#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Display, EnumIs)]
137#[cfg_attr(feature = "specta", derive(specta::Type))]
138#[serde(untagged)]
139pub enum ReleaseResolution {
140  NonStandard(String),
141  Standard([u32; 2]),
142}
143
144#[non_exhaustive]
145#[remain::sorted]
146#[derive(
147  Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash, Display, EnumIs, EnumString,
148)]
149#[cfg_attr(feature = "specta", derive(specta::Type))]
150pub enum ReleaseType {
151  #[serde(rename = "complete")]
152  #[strum(serialize = "complete")]
153  Complete,
154
155  #[serde(rename = "partial")]
156  #[strum(serialize = "partial")]
157  Partial,
158
159  #[serde(rename = "trial")]
160  #[strum(serialize = "trial")]
161  Trial,
162}
163
164#[remain::sorted]
165#[derive(Clone, Debug, Deserialize, Serialize)]
166#[cfg_attr(feature = "specta", derive(specta::Type))]
167pub struct ReleaseVisualNovel {
168  pub rtype: Option<ReleaseType>,
169  #[serde(flatten)]
170  pub visual_novel: VisualNovel,
171}
172
173impl From<ReleaseVisualNovel> for VisualNovel {
174  fn from(rvn: ReleaseVisualNovel) -> Self {
175    rvn.visual_novel
176  }
177}
178
179#[non_exhaustive]
180#[remain::sorted]
181#[derive(Copy, Clone, Debug, Serialize, PartialEq, Eq, Hash, Display, EnumIs)]
182#[cfg_attr(feature = "specta", derive(specta::Type))]
183pub enum ReleaseVoiced {
184  FullyVoiced,
185  NotVoiced,
186  OnlyEroScenes,
187  PartiallyVoiced,
188}
189
190impl<'de> Deserialize<'de> for ReleaseVoiced {
191  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
192  where
193    D: Deserializer<'de>,
194  {
195    use serde::de::Error;
196    match u8::deserialize(deserializer)? {
197      1 => Ok(ReleaseVoiced::NotVoiced),
198      2 => Ok(ReleaseVoiced::OnlyEroScenes),
199      3 => Ok(ReleaseVoiced::PartiallyVoiced),
200      4 => Ok(ReleaseVoiced::FullyVoiced),
201      _ => Err(D::Error::custom("Invalid release voiced value")),
202    }
203  }
204}
205
206#[remain::sorted]
207#[derive(Clone, Debug, Deserialize, Serialize)]
208#[cfg_attr(feature = "specta", derive(specta::Type))]
209pub struct ExternalLink {
210  pub id: Option<JsonValue>,
211  pub label: Option<String>,
212  pub name: Option<String>,
213  pub url: Option<String>,
214}
215
216#[non_exhaustive]
217#[remain::sorted]
218#[derive(Clone, Copy, Debug, Deserialize, Serialize, Display, EnumString, VariantArray)]
219#[cfg_attr(feature = "specta", derive(specta::Type))]
220pub enum ReleaseField {
221  #[serde(rename = "alttitle")]
222  #[strum(serialize = "alttitle")]
223  AltTitle,
224
225  #[serde(rename = "catalog")]
226  #[strum(serialize = "catalog")]
227  Catalog,
228
229  #[serde(rename = "engine")]
230  #[strum(serialize = "engine")]
231  Engine,
232
233  #[serde(rename = "extlinks.id")]
234  #[strum(serialize = "extlinks.id")]
235  ExtlinksId,
236
237  #[serde(rename = "extlinks.label")]
238  #[strum(serialize = "extlinks.label")]
239  ExtlinksLabel,
240
241  #[serde(rename = "extlinks.name")]
242  #[strum(serialize = "extlinks.name")]
243  ExtlinksName,
244
245  #[serde(rename = "extlinks.url")]
246  #[strum(serialize = "extlinks.url")]
247  ExtlinksUrl,
248
249  #[serde(rename = "freeware")]
250  #[strum(serialize = "freeware")]
251  Freeware,
252
253  #[serde(rename = "gtin")]
254  #[strum(serialize = "gtin")]
255  Gtin,
256
257  #[serde(rename = "has_ero")]
258  #[strum(serialize = "has_ero")]
259  HasEro,
260
261  #[serde(rename = "id")]
262  #[strum(serialize = "id")]
263  Id,
264
265  #[serde(rename = "images.id")]
266  #[strum(serialize = "images.id")]
267  ImagesId,
268
269  #[serde(rename = "images.languages")]
270  #[strum(serialize = "images.languages")]
271  ImagesLanguages,
272
273  #[serde(rename = "images.photo")]
274  #[strum(serialize = "images.photo")]
275  ImagesPhoto,
276
277  #[serde(rename = "images.type")]
278  #[strum(serialize = "images.type")]
279  ImagesType,
280
281  #[serde(rename = "images.url")]
282  #[strum(serialize = "images.url")]
283  ImagesUrl,
284
285  #[serde(rename = "images.vn")]
286  #[strum(serialize = "images.vn")]
287  ImagesVn,
288
289  #[serde(rename = "languages.lang")]
290  #[strum(serialize = "languages.lang")]
291  LanguagesLang,
292
293  #[serde(rename = "languages.latin")]
294  #[strum(serialize = "languages.latin")]
295  LanguagesLatin,
296
297  #[serde(rename = "languages.main")]
298  #[strum(serialize = "languages.main")]
299  LanguagesMain,
300
301  #[serde(rename = "languages.mtl")]
302  #[strum(serialize = "languages.mtl")]
303  LanguagesMtl,
304
305  #[serde(rename = "languages.title")]
306  #[strum(serialize = "languages.title")]
307  LanguagesTitle,
308
309  #[serde(rename = "media.medium")]
310  #[strum(serialize = "media.medium")]
311  MediaMedium,
312
313  #[serde(rename = "media.qty")]
314  #[strum(serialize = "media.qty")]
315  MediaQty,
316
317  #[serde(rename = "minage")]
318  #[strum(serialize = "minage")]
319  MinAge,
320
321  #[serde(rename = "notes")]
322  #[strum(serialize = "notes")]
323  Notes,
324
325  #[serde(rename = "official")]
326  #[strum(serialize = "official")]
327  Official,
328
329  #[serde(rename = "patch")]
330  #[strum(serialize = "patch")]
331  Patch,
332
333  #[serde(rename = "platforms")]
334  #[strum(serialize = "platforms")]
335  Platforms,
336
337  #[serde(rename = "producers.developer")]
338  #[strum(serialize = "producers.developer")]
339  ProducersDeveloper,
340
341  #[serde(rename = "producers.id")]
342  #[strum(serialize = "producers.id")]
343  ProducersId,
344
345  #[serde(rename = "producers.publisher")]
346  #[strum(serialize = "producers.publisher")]
347  ProducersPublisher,
348
349  #[serde(rename = "released")]
350  #[strum(serialize = "released")]
351  Released,
352
353  #[serde(rename = "resolution")]
354  #[strum(serialize = "resolution")]
355  Resolution,
356
357  #[serde(rename = "title")]
358  #[strum(serialize = "title")]
359  Title,
360
361  #[serde(rename = "uncensored")]
362  #[strum(serialize = "uncensored")]
363  Uncensored,
364
365  #[serde(rename = "vns.id")]
366  #[strum(serialize = "vns.id")]
367  VisualNovelId,
368
369  #[serde(rename = "vns.rtype")]
370  #[strum(serialize = "vns.rtype")]
371  VisualNovelRType,
372
373  #[serde(rename = "voiced")]
374  #[strum(serialize = "voiced")]
375  Voiced,
376}
377
378impl QueryField for ReleaseField {}
379
380impl_into_field_set!(ReleaseField);
381
382#[non_exhaustive]
383#[remain::sorted]
384#[derive(Clone, Copy, Debug, Deserialize, Serialize, Display, EnumString)]
385#[cfg_attr(feature = "specta", derive(specta::Type))]
386pub enum SortReleaseBy {
387  #[serde(rename = "id")]
388  #[strum(serialize = "id")]
389  Id,
390
391  #[serde(rename = "released")]
392  #[strum(serialize = "released")]
393  Released,
394
395  #[serde(rename = "searchrank")]
396  #[strum(serialize = "searchrank")]
397  SearchRank,
398
399  #[serde(rename = "title")]
400  #[strum(serialize = "title")]
401  Title,
402}
403
404impl SortQueryBy for SortReleaseBy {}