Skip to main content

ferrex_model/
ids.rs

1use crate::error::ModelError as MediaError;
2use std::num::NonZeroU32;
3use uuid::Uuid;
4
5/// Strongly typed ID for libraries with validation
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(
9    feature = "rkyv",
10    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
11)]
12#[cfg_attr(
13    feature = "rkyv",
14    rkyv(derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy))
15)]
16pub struct LibraryId(pub Uuid);
17
18impl Default for LibraryId {
19    fn default() -> Self {
20        Self::new()
21    }
22}
23
24impl LibraryId {
25    pub fn new() -> Self {
26        LibraryId(Uuid::now_v7())
27    }
28
29    pub fn as_str(&self) -> String {
30        self.0.to_string()
31    }
32
33    pub fn as_uuid(&self) -> &Uuid {
34        &self.0
35    }
36
37    pub fn to_uuid(&self) -> Uuid {
38        self.0
39    }
40}
41
42impl AsRef<Uuid> for LibraryId {
43    fn as_ref(&self) -> &Uuid {
44        &self.0
45    }
46}
47
48#[cfg(feature = "rkyv")]
49impl ArchivedLibraryId {
50    pub fn as_str(&self) -> String {
51        self.0.to_string()
52    }
53
54    pub fn as_uuid(&self) -> Uuid {
55        self.0
56    }
57}
58
59impl std::fmt::Display for LibraryId {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "{}", self.0)
62    }
63}
64
65/// Strongly typed ID for movies with validation
66#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[cfg_attr(
69    feature = "rkyv",
70    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
71)]
72#[cfg_attr(
73    feature = "rkyv",
74    rkyv(derive(Debug, Clone, PartialEq, Eq, Hash, Copy))
75)]
76pub struct MovieID(pub Uuid);
77
78/// Per-library monotonically increasing batch id for movie references.
79#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81#[cfg_attr(
82    feature = "rkyv",
83    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
84)]
85#[cfg_attr(
86    feature = "rkyv",
87    rkyv(derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord))
88)]
89pub struct MovieBatchId(pub u32);
90
91impl MovieBatchId {
92    pub fn new(value: u32) -> Result<Self, MediaError> {
93        if value == 0 {
94            return Err(MediaError::InvalidMedia(
95                "Movie batch id cannot be 0".to_string(),
96            ));
97        }
98        Ok(Self(value))
99    }
100
101    pub fn as_u32(&self) -> u32 {
102        self.0
103    }
104
105    pub fn as_i64(&self) -> i64 {
106        self.0 as i64
107    }
108}
109
110impl std::fmt::Display for MovieBatchId {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        write!(f, "{}", self.0)
113    }
114}
115
116/// Fixed per-library batch size for movie reference ingestion.
117#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
119#[cfg_attr(
120    feature = "rkyv",
121    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
122)]
123#[cfg_attr(
124    feature = "rkyv",
125    rkyv(derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord))
126)]
127pub struct MovieReferenceBatchSize(pub NonZeroU32);
128
129impl Default for MovieReferenceBatchSize {
130    fn default() -> Self {
131        Self(NonZeroU32::new(100).unwrap())
132    }
133}
134
135impl MovieReferenceBatchSize {
136    pub fn new(value: u32) -> Result<Self, MediaError> {
137        let nz = NonZeroU32::new(value).ok_or_else(|| {
138            MediaError::InvalidMedia(
139                "Movie reference batch size must be > 0".to_string(),
140            )
141        })?;
142        Ok(Self(nz))
143    }
144
145    pub fn get(&self) -> u32 {
146        self.0.get()
147    }
148}
149
150impl std::fmt::Display for MovieReferenceBatchSize {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        write!(f, "{}", self.0.get())
153    }
154}
155
156impl Default for MovieID {
157    fn default() -> Self {
158        Self::new()
159    }
160}
161
162impl From<Uuid> for MovieID {
163    fn from(id: Uuid) -> Self {
164        MovieID(id)
165    }
166}
167
168impl MovieID {
169    pub fn new() -> Self {
170        MovieID(Uuid::now_v7())
171    }
172
173    pub fn new_u64(id: Uuid) -> Result<Self, MediaError> {
174        Ok(MovieID(id))
175    }
176
177    pub fn new_uuid() -> Self {
178        MovieID(Uuid::now_v7())
179    }
180
181    pub fn from_string(id: String) -> Result<Self, MediaError> {
182        if id.is_empty() {
183            return Err(MediaError::InvalidMedia(
184                "Movie ID cannot be empty".to_string(),
185            ));
186        }
187        Ok(MovieID(id.parse().expect("Failed to parse movie ID")))
188    }
189
190    pub fn as_str(&self) -> String {
191        self.0.to_string()
192    }
193
194    pub fn as_uuid(&self) -> &Uuid {
195        &self.0
196    }
197
198    pub fn to_uuid(&self) -> Uuid {
199        self.0
200    }
201}
202
203impl AsRef<Uuid> for MovieID {
204    fn as_ref(&self) -> &Uuid {
205        &self.0
206    }
207}
208
209impl std::fmt::Display for MovieID {
210    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211        write!(f, "{}", self.0)
212    }
213}
214
215#[cfg(feature = "rkyv")]
216impl ArchivedMovieID {
217    pub fn as_uuid(&self) -> &Uuid {
218        &self.0
219    }
220
221    pub fn to_uuid(&self) -> Uuid {
222        self.0
223    }
224}
225
226/// Strongly typed ID for series with validation
227#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229#[cfg_attr(
230    feature = "rkyv",
231    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
232)]
233#[cfg_attr(
234    feature = "rkyv",
235    rkyv(derive(Debug, Clone, PartialEq, Eq, Hash, Copy))
236)]
237pub struct SeriesID(pub Uuid);
238
239impl Default for SeriesID {
240    fn default() -> Self {
241        Self::new()
242    }
243}
244
245impl From<Uuid> for SeriesID {
246    fn from(id: Uuid) -> Self {
247        SeriesID(id)
248    }
249}
250
251impl SeriesID {
252    pub fn new() -> Self {
253        SeriesID(Uuid::now_v7())
254    }
255
256    pub fn new_u64(id: Uuid) -> Result<Self, MediaError> {
257        Ok(SeriesID(id))
258    }
259
260    pub fn new_uuid() -> Self {
261        SeriesID(Uuid::now_v7())
262    }
263
264    pub fn from_string(id: String) -> Result<Self, MediaError> {
265        if id.is_empty() {
266            return Err(MediaError::InvalidMedia(
267                "Movie ID cannot be empty".to_string(),
268            ));
269        }
270        Ok(SeriesID(id.parse().expect("Failed to parse movie ID")))
271    }
272
273    pub fn as_uuid(&self) -> &Uuid {
274        &self.0
275    }
276
277    pub fn as_str(&self) -> String {
278        self.0.to_string()
279    }
280
281    pub fn to_uuid(&self) -> Uuid {
282        self.0
283    }
284}
285
286impl AsRef<Uuid> for SeriesID {
287    fn as_ref(&self) -> &Uuid {
288        &self.0
289    }
290}
291
292impl std::fmt::Display for SeriesID {
293    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294        write!(f, "{}", self.0)
295    }
296}
297
298#[cfg(feature = "rkyv")]
299impl ArchivedSeriesID {
300    pub fn as_uuid(&self) -> &Uuid {
301        &self.0
302    }
303
304    pub fn to_uuid(&self) -> Uuid {
305        self.0
306    }
307}
308
309/// Strongly typed ID for seasons with validation
310#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
311#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
312#[cfg_attr(
313    feature = "rkyv",
314    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
315)]
316#[cfg_attr(
317    feature = "rkyv",
318    rkyv(derive(Debug, Clone, PartialEq, Eq, Hash, Copy))
319)]
320pub struct SeasonID(pub Uuid);
321
322impl Default for SeasonID {
323    fn default() -> Self {
324        Self::new()
325    }
326}
327
328impl From<Uuid> for SeasonID {
329    fn from(id: Uuid) -> Self {
330        SeasonID(id)
331    }
332}
333
334impl SeasonID {
335    pub fn new() -> Self {
336        SeasonID(Uuid::now_v7())
337    }
338
339    pub fn new_u64(id: Uuid) -> Result<Self, MediaError> {
340        Ok(SeasonID(id))
341    }
342
343    pub fn new_uuid() -> Self {
344        SeasonID(Uuid::now_v7())
345    }
346
347    pub fn from(id: String) -> Result<Self, MediaError> {
348        if id.is_empty() {
349            return Err(MediaError::InvalidMedia(
350                "Movie ID cannot be empty".to_string(),
351            ));
352        }
353        Ok(SeasonID(id.parse().expect("Failed to parse movie ID")))
354    }
355
356    pub fn as_str(&self) -> String {
357        self.0.to_string()
358    }
359
360    pub fn as_uuid(&self) -> &Uuid {
361        &self.0
362    }
363
364    pub fn to_uuid(&self) -> Uuid {
365        self.0
366    }
367}
368
369impl AsRef<Uuid> for SeasonID {
370    fn as_ref(&self) -> &Uuid {
371        &self.0
372    }
373}
374
375impl std::fmt::Display for SeasonID {
376    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377        write!(f, "{}", self.0)
378    }
379}
380
381#[cfg(feature = "rkyv")]
382impl ArchivedSeasonID {
383    pub fn as_uuid(&self) -> &Uuid {
384        &self.0
385    }
386
387    pub fn to_uuid(&self) -> Uuid {
388        self.0
389    }
390}
391
392/// Strongly typed ID for episodes with validation
393#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
394#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
395#[cfg_attr(
396    feature = "rkyv",
397    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
398)]
399#[cfg_attr(
400    feature = "rkyv",
401    rkyv(derive(Debug, Clone, PartialEq, Eq, Hash, Copy))
402)]
403pub struct EpisodeID(pub Uuid);
404
405impl Default for EpisodeID {
406    fn default() -> Self {
407        Self::new()
408    }
409}
410
411impl From<Uuid> for EpisodeID {
412    fn from(id: Uuid) -> Self {
413        EpisodeID(id)
414    }
415}
416
417impl EpisodeID {
418    pub fn new() -> Self {
419        EpisodeID(Uuid::now_v7())
420    }
421
422    pub fn new_u64(id: Uuid) -> Result<Self, MediaError> {
423        Ok(EpisodeID(id))
424    }
425
426    pub fn new_uuid() -> Self {
427        EpisodeID(Uuid::now_v7())
428    }
429
430    pub fn as_str(&self) -> String {
431        self.0.to_string()
432    }
433
434    pub fn as_uuid(&self) -> &Uuid {
435        &self.0
436    }
437
438    pub fn to_uuid(&self) -> Uuid {
439        self.0
440    }
441}
442
443impl AsRef<Uuid> for EpisodeID {
444    fn as_ref(&self) -> &Uuid {
445        &self.0
446    }
447}
448
449impl std::fmt::Display for EpisodeID {
450    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451        write!(f, "{}", self.0)
452    }
453}
454
455#[cfg(feature = "rkyv")]
456impl ArchivedEpisodeID {
457    pub fn as_uuid(&self) -> &Uuid {
458        &self.0
459    }
460
461    pub fn to_uuid(&self) -> Uuid {
462        self.0
463    }
464}
465
466/// Strongly typed ID for persons with validation
467#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
468#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
469#[cfg_attr(
470    feature = "rkyv",
471    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
472)]
473#[cfg_attr(
474    feature = "rkyv",
475    rkyv(derive(Debug, Clone, PartialEq, Eq, Hash, Copy))
476)]
477pub struct PersonID(pub Uuid);
478
479impl From<Uuid> for PersonID {
480    fn from(id: Uuid) -> Self {
481        PersonID(id)
482    }
483}
484
485impl PersonID {
486    pub fn new(id: String) -> Result<Self, MediaError> {
487        if id.is_empty() {
488            return Err(MediaError::InvalidMedia(
489                "Movie ID cannot be empty".to_string(),
490            ));
491        }
492        Ok(PersonID(id.parse().expect("Failed to parse movie ID")))
493    }
494
495    pub fn new_u64(id: Uuid) -> Result<Self, MediaError> {
496        Ok(PersonID(id))
497    }
498
499    pub fn new_uuid() -> Self {
500        PersonID(Uuid::now_v7())
501    }
502}
503
504impl AsRef<Uuid> for PersonID {
505    fn as_ref(&self) -> &Uuid {
506        &self.0
507    }
508}
509
510impl std::fmt::Display for PersonID {
511    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512        write!(f, "{}", self.0)
513    }
514}