subsonic_types/
common.rs

1//! Module for Subsonic API common types.
2
3use std::{str::FromStr, time::Duration};
4
5use serde::{Deserialize, Serialize};
6use subsonic_types_macro::SubsonicType;
7use time::{OffsetDateTime, PrimitiveDateTime};
8
9use crate::{impl_from_query_value_for_parse, impl_to_query_value_for_display};
10
11#[derive(Debug)]
12pub struct InvalidFormat;
13
14impl std::fmt::Display for InvalidFormat {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        write!(f, "Invalid format")
17    }
18}
19
20impl std::error::Error for InvalidFormat {}
21
22/// A serialization format for the responses. `jsonp` is not supported.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[serde(rename_all = "camelCase")]
25pub enum Format {
26    Json,
27    Xml,
28}
29
30impl std::fmt::Display for Format {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Format::Json => write!(f, "json"),
34            Format::Xml => write!(f, "xml"),
35        }
36    }
37}
38
39impl FromStr for Format {
40    type Err = InvalidFormat;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        match s {
44            "json" => Ok(Format::Json),
45            "xml" => Ok(Format::Xml),
46            _ => Err(InvalidFormat),
47        }
48    }
49}
50
51/// A date and time.
52/// Use [`time::OffsetDateTime`] to convert to and from [`DateTime`].
53#[derive(Debug, Clone, PartialEq, SubsonicType)]
54#[subsonic(serde)]
55pub struct DateTime(OffsetDateTime);
56impl_to_query_value_for_display!(DateTime);
57impl_from_query_value_for_parse!(DateTime);
58
59impl From<PrimitiveDateTime> for DateTime {
60    fn from(value: PrimitiveDateTime) -> Self {
61        Self(value.assume_utc())
62    }
63}
64
65impl From<OffsetDateTime> for DateTime {
66    fn from(datetime: OffsetDateTime) -> Self {
67        Self(datetime)
68    }
69}
70
71impl From<DateTime> for OffsetDateTime {
72    fn from(datetime: DateTime) -> Self {
73        datetime.0
74    }
75}
76
77impl Default for DateTime {
78    fn default() -> Self {
79        Self::from(OffsetDateTime::from_unix_timestamp(0).unwrap())
80    }
81}
82
83impl FromStr for DateTime {
84    type Err = time::error::Parse;
85
86    fn from_str(s: &str) -> Result<Self, Self::Err> {
87        let first_try = PrimitiveDateTime::parse(
88            s,
89            time::macros::format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]"),
90        )
91        .map(Self::from);
92        if let Ok(datetime) = first_try {
93            return Ok(datetime);
94        }
95
96        OffsetDateTime::parse(s, &time::format_description::well_known::Iso8601::PARSING)
97            .map(Self::from)
98    }
99}
100
101impl std::fmt::Display for DateTime {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        let string = self
104            .0
105            .format(time::macros::format_description!(
106                "[year]-[month]-[day]T[hour]:[minute]:[second]"
107            ))
108            .map_err(|_| std::fmt::Error)?;
109        write!(f, "{string}")
110    }
111}
112
113impl Serialize for DateTime {
114    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115    where
116        S: serde::Serializer,
117    {
118        serializer.serialize_str(&self.to_string())
119    }
120}
121
122impl<'de> Deserialize<'de> for DateTime {
123    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
124    where
125        D: serde::Deserializer<'de>,
126    {
127        let s = String::deserialize(deserializer)?;
128        Self::from_str(&s).map_err(serde::de::Error::custom)
129    }
130}
131
132/// A duration in milliseconds.
133/// When used to represent an instant in time, it is relative to the Unix epoch.
134#[derive(
135    Debug,
136    Default,
137    Clone,
138    Copy,
139    PartialEq,
140    Eq,
141    PartialOrd,
142    Ord,
143    Hash,
144    Serialize,
145    Deserialize,
146    SubsonicType,
147)]
148#[serde(transparent)]
149#[subsonic(serde)]
150pub struct Milliseconds(u64);
151impl_to_query_value_for_display!(Milliseconds);
152impl_from_query_value_for_parse!(Milliseconds);
153
154impl Milliseconds {
155    pub fn new(milliseconds: u64) -> Self {
156        Self(milliseconds)
157    }
158
159    pub fn to_duration(&self) -> std::time::Duration {
160        self.into_duration()
161    }
162
163    pub fn into_duration(self) -> std::time::Duration {
164        Duration::from_millis(self.0)
165    }
166}
167
168// impl serde::Serialize for Milliseconds {
169//     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
170//     where
171//         S: serde::Serializer,
172//     {
173//         serializer.serialize_u64(self.0)
174//     }
175// }
176
177// impl<'de> serde::Deserialize<'de> for Milliseconds {
178//     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
179//     where
180//         D: serde::Deserializer<'de> {
181//         deserializer.deserialize_u64(visitor)
182//     }
183// }
184
185impl From<u64> for Milliseconds {
186    fn from(milliseconds: u64) -> Self {
187        Self::new(milliseconds)
188    }
189}
190
191impl From<Milliseconds> for Duration {
192    fn from(milliseconds: Milliseconds) -> Self {
193        milliseconds.into_duration()
194    }
195}
196
197impl std::fmt::Display for Milliseconds {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        write!(f, "{}", self.0)
200    }
201}
202
203impl FromStr for Milliseconds {
204    type Err = std::num::ParseIntError;
205
206    fn from_str(s: &str) -> Result<Self, Self::Err> {
207        Ok(Self::new(s.parse()?))
208    }
209}
210
211/// A duration in seconds.
212/// When used to represent an instant in time, it is relative to the Unix epoch.
213#[derive(
214    Debug,
215    Default,
216    Clone,
217    Copy,
218    PartialEq,
219    Eq,
220    PartialOrd,
221    Ord,
222    Hash,
223    Serialize,
224    Deserialize,
225    SubsonicType,
226)]
227#[subsonic(serde)]
228pub struct Seconds(u64);
229impl_to_query_value_for_display!(Seconds);
230impl_from_query_value_for_parse!(Seconds);
231
232impl Seconds {
233    pub fn new(seconds: u64) -> Self {
234        Self(seconds)
235    }
236
237    pub fn to_duration(&self) -> std::time::Duration {
238        self.into_duration()
239    }
240
241    pub fn into_duration(self) -> std::time::Duration {
242        Duration::from_secs(self.0)
243    }
244}
245
246impl From<u64> for Seconds {
247    fn from(seconds: u64) -> Self {
248        Self::new(seconds)
249    }
250}
251
252impl From<Seconds> for Duration {
253    fn from(seconds: Seconds) -> Self {
254        seconds.into_duration()
255    }
256}
257
258impl std::fmt::Display for Seconds {
259    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260        write!(f, "{}", self.0)
261    }
262}
263
264impl FromStr for Seconds {
265    type Err = std::num::ParseIntError;
266
267    fn from_str(s: &str) -> Result<Self, Self::Err> {
268        Ok(Self::new(s.parse()?))
269    }
270}
271
272#[derive(Debug)]
273pub struct InvalidVideoSize;
274
275impl std::fmt::Display for InvalidVideoSize {
276    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277        write!(f, "invalid video size")
278    }
279}
280
281impl std::error::Error for InvalidVideoSize {}
282
283/// A video size in pixels containing a width and a height.
284#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SubsonicType)]
285#[subsonic(serde)]
286pub struct VideoSize {
287    pub width: u32,
288    pub height: u32,
289}
290impl_to_query_value_for_display!(VideoSize);
291impl_from_query_value_for_parse!(VideoSize);
292
293impl VideoSize {
294    pub fn new(width: u32, height: u32) -> Self {
295        Self { width, height }
296    }
297}
298
299impl From<(u32, u32)> for VideoSize {
300    fn from((width, height): (u32, u32)) -> Self {
301        Self::new(width, height)
302    }
303}
304
305impl From<VideoSize> for (u32, u32) {
306    fn from(size: VideoSize) -> Self {
307        (size.width, size.height)
308    }
309}
310
311impl std::fmt::Display for VideoSize {
312    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313        write!(f, "{}x{}", self.width, self.height)
314    }
315}
316
317impl std::str::FromStr for VideoSize {
318    type Err = InvalidVideoSize;
319
320    fn from_str(s: &str) -> Result<Self, Self::Err> {
321        let mut parts = s.split('x');
322        let width = parts
323            .next()
324            .ok_or(InvalidVideoSize)?
325            .parse()
326            .map_err(|_| InvalidVideoSize)?;
327        let height = parts
328            .next()
329            .ok_or(InvalidVideoSize)?
330            .parse()
331            .map_err(|_| InvalidVideoSize)?;
332        if parts.next().is_some() {
333            return Err(InvalidVideoSize);
334        }
335        Ok(Self::new(width, height))
336    }
337}
338
339impl Serialize for VideoSize {
340    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
341    where
342        S: serde::Serializer,
343    {
344        serializer.serialize_str(&self.to_string())
345    }
346}
347
348impl<'de> Deserialize<'de> for VideoSize {
349    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
350    where
351        D: serde::Deserializer<'de>,
352    {
353        let s = String::deserialize(deserializer)?;
354        s.parse().map_err(serde::de::Error::custom)
355    }
356}
357
358#[derive(Debug)]
359pub struct InvalidVideoBitrate;
360
361impl std::fmt::Display for InvalidVideoBitrate {
362    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363        write!(f, "invalid video bitrate")
364    }
365}
366
367impl std::error::Error for InvalidVideoBitrate {}
368
369/// A video bitrate, in kilobits per second, optionally containing a video size.
370#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SubsonicType)]
371#[subsonic(serde)]
372pub struct VideoBitrate {
373    pub bitrate: u32,
374    pub size: Option<VideoSize>,
375}
376impl_to_query_value_for_display!(VideoBitrate);
377impl_from_query_value_for_parse!(VideoBitrate);
378
379impl VideoBitrate {
380    pub fn new(bitrate: u32, size: Option<VideoSize>) -> Self {
381        Self { bitrate, size }
382    }
383
384    pub fn without_size(bitrate: u32) -> Self {
385        Self::new(bitrate, None)
386    }
387}
388
389impl std::fmt::Display for VideoBitrate {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        if let Some(size) = self.size {
392            write!(f, "{}@{}", self.bitrate, size)
393        } else {
394            write!(f, "{}", self.bitrate)
395        }
396    }
397}
398
399impl std::str::FromStr for VideoBitrate {
400    type Err = InvalidVideoBitrate;
401
402    fn from_str(s: &str) -> Result<Self, Self::Err> {
403        let mut parts = s.split('@');
404        let bitrate = parts
405            .next()
406            .ok_or(InvalidVideoBitrate)?
407            .parse()
408            .map_err(|_| InvalidVideoBitrate)?;
409        let size = parts
410            .next()
411            .map(|s| s.parse())
412            .transpose()
413            .map_err(|_| InvalidVideoBitrate)?;
414        if parts.next().is_some() {
415            return Err(InvalidVideoBitrate);
416        }
417        Ok(Self::new(bitrate, size))
418    }
419}
420
421impl Serialize for VideoBitrate {
422    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
423    where
424        S: serde::Serializer,
425    {
426        serializer.serialize_str(&self.to_string())
427    }
428}
429
430impl<'de> Deserialize<'de> for VideoBitrate {
431    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
432    where
433        D: serde::Deserializer<'de>,
434    {
435        let s = String::deserialize(deserializer)?;
436        s.parse().map_err(serde::de::Error::custom)
437    }
438}
439
440#[derive(Debug)]
441pub struct InvalidAudioBitrate;
442
443impl std::fmt::Display for InvalidAudioBitrate {
444    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445        write!(f, "invalid audio bitrate")
446    }
447}
448
449impl std::error::Error for InvalidAudioBitrate {}
450
451/// An audio bitrate in kbit/s.
452#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SubsonicType)]
453#[subsonic(serde)]
454pub enum AudioBitrate {
455    /// No limit.
456    NoLimit,
457    /// 32 kbit/s.
458    Kbps32,
459    /// 40 kbit/s.
460    Kbps40,
461    /// 48 kbit/s.
462    Kbps48,
463    /// 56 kbit/s.
464    Kbps56,
465    /// 64 kbit/s.
466    Kbps64,
467    /// 80 kbit/s.
468    Kbps80,
469    /// 96 kbit/s.
470    Kbps96,
471    /// 112 kbit/s.
472    Kbps112,
473    /// 128 kbit/s.
474    Kbps128,
475    /// 160 kbit/s.
476    Kbps160,
477    /// 192 kbit/s.
478    Kbps192,
479    /// 224 kbit/s.
480    Kbps224,
481    /// 256 kbit/s.
482    Kbps256,
483    /// 320 kbit/s.
484    Kbps320,
485    /// Other bitrate.
486    Other(u32),
487}
488impl_to_query_value_for_display!(AudioBitrate);
489impl_from_query_value_for_parse!(AudioBitrate);
490
491impl AudioBitrate {
492    pub fn new(bitrate: u32) -> Self {
493        match bitrate {
494            0 => Self::NoLimit,
495            32 => Self::Kbps32,
496            40 => Self::Kbps40,
497            48 => Self::Kbps48,
498            56 => Self::Kbps56,
499            64 => Self::Kbps64,
500            80 => Self::Kbps80,
501            96 => Self::Kbps96,
502            112 => Self::Kbps112,
503            128 => Self::Kbps128,
504            160 => Self::Kbps160,
505            192 => Self::Kbps192,
506            224 => Self::Kbps224,
507            256 => Self::Kbps256,
508            320 => Self::Kbps320,
509            _ => Self::Other(bitrate),
510        }
511    }
512
513    pub fn to_kbps(&self) -> u32 {
514        match self {
515            Self::NoLimit => 0,
516            Self::Kbps32 => 32,
517            Self::Kbps40 => 40,
518            Self::Kbps48 => 48,
519            Self::Kbps56 => 56,
520            Self::Kbps64 => 64,
521            Self::Kbps80 => 80,
522            Self::Kbps96 => 96,
523            Self::Kbps112 => 112,
524            Self::Kbps128 => 128,
525            Self::Kbps160 => 160,
526            Self::Kbps192 => 192,
527            Self::Kbps224 => 224,
528            Self::Kbps256 => 256,
529            Self::Kbps320 => 320,
530            Self::Other(bitrate) => *bitrate,
531        }
532    }
533}
534
535impl From<u32> for AudioBitrate {
536    fn from(bitrate: u32) -> Self {
537        Self::new(bitrate)
538    }
539}
540
541impl From<AudioBitrate> for u32 {
542    fn from(bitrate: AudioBitrate) -> Self {
543        bitrate.to_kbps()
544    }
545}
546
547impl std::fmt::Display for AudioBitrate {
548    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
549        write!(f, "{}", self.to_kbps())
550    }
551}
552
553impl std::str::FromStr for AudioBitrate {
554    type Err = InvalidAudioBitrate;
555
556    fn from_str(s: &str) -> Result<Self, Self::Err> {
557        let bitrate = s.parse().map_err(|_| InvalidAudioBitrate)?;
558        Ok(Self::new(bitrate))
559    }
560}
561
562impl Serialize for AudioBitrate {
563    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
564    where
565        S: serde::Serializer,
566    {
567        serializer.serialize_u32(self.to_kbps())
568    }
569}
570
571impl<'de> Deserialize<'de> for AudioBitrate {
572    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
573    where
574        D: serde::Deserializer<'de>,
575    {
576        let bitrate = u32::deserialize(deserializer)?;
577        Ok(Self::new(bitrate))
578    }
579}
580
581#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, SubsonicType)]
582#[serde(rename_all = "camelCase")]
583#[subsonic(serde)]
584pub enum MediaType {
585    Music,
586    Podcast,
587    AudioBook,
588    Video,
589}
590
591#[derive(Debug)]
592pub struct InvalidUserRating;
593
594impl std::fmt::Display for InvalidUserRating {
595    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
596        write!(f, "Invalid user rating")
597    }
598}
599
600impl std::error::Error for InvalidUserRating {}
601
602/// A user rating. It is an integer between 1 and 5.
603#[derive(Debug, Clone, Copy, PartialEq, Hash, SubsonicType)]
604#[subsonic(serde)]
605pub struct UserRating(u32);
606impl_to_query_value_for_display!(UserRating);
607impl_from_query_value_for_parse!(UserRating);
608
609impl UserRating {
610    pub fn new(value: u32) -> Result<Self, InvalidUserRating> {
611        if !(1..=5).contains(&value) {
612            Err(InvalidUserRating)
613        } else {
614            Ok(UserRating(value))
615        }
616    }
617
618    pub fn value(self) -> u32 {
619        self.0
620    }
621}
622
623impl std::fmt::Display for UserRating {
624    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
625        write!(f, "{}", self.0)
626    }
627}
628
629impl std::str::FromStr for UserRating {
630    type Err = InvalidUserRating;
631
632    fn from_str(s: &str) -> Result<Self, Self::Err> {
633        let value = s.parse().map_err(|_| InvalidUserRating)?;
634        UserRating::new(value)
635    }
636}
637
638impl From<UserRating> for u32 {
639    fn from(value: UserRating) -> Self {
640        value.0
641    }
642}
643
644impl Serialize for UserRating {
645    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
646    where
647        S: serde::Serializer,
648    {
649        serializer.serialize_u32(self.0)
650    }
651}
652
653impl<'de> Deserialize<'de> for UserRating {
654    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
655    where
656        D: serde::Deserializer<'de>,
657    {
658        let value = u32::deserialize(deserializer)?;
659        UserRating::new(value).map_err(serde::de::Error::custom)
660    }
661}
662
663#[derive(Debug)]
664pub struct InvalidAverageRating;
665
666impl std::fmt::Display for InvalidAverageRating {
667    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
668        write!(f, "Invalid average rating")
669    }
670}
671
672impl std::error::Error for InvalidAverageRating {}
673/// An average rating. It is a float between 1.0 and 5.0.
674#[derive(Debug, Clone, Copy, PartialEq, SubsonicType)]
675#[subsonic(serde)]
676pub struct AverageRating(f32);
677
678impl AverageRating {
679    pub fn new(value: f32) -> Result<Self, InvalidAverageRating> {
680        if !(1.0..=5.0).contains(&value) {
681            Err(InvalidAverageRating)
682        } else {
683            Ok(AverageRating(value))
684        }
685    }
686
687    pub fn value(self) -> f32 {
688        self.0
689    }
690}
691
692impl std::fmt::Display for AverageRating {
693    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
694        write!(f, "{}", self.0)
695    }
696}
697
698impl std::str::FromStr for AverageRating {
699    type Err = InvalidAverageRating;
700
701    fn from_str(s: &str) -> Result<Self, Self::Err> {
702        let value = s.parse().map_err(|_| InvalidAverageRating)?;
703        AverageRating::new(value)
704    }
705}
706
707impl From<AverageRating> for f32 {
708    fn from(value: AverageRating) -> Self {
709        value.0
710    }
711}
712
713impl Serialize for AverageRating {
714    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
715    where
716        S: serde::Serializer,
717    {
718        serializer.serialize_f32(self.0)
719    }
720}
721
722impl<'de> Deserialize<'de> for AverageRating {
723    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
724    where
725        D: serde::Deserializer<'de>,
726    {
727        let value = f32::deserialize(deserializer)?;
728        AverageRating::new(value).map_err(serde::de::Error::custom)
729    }
730}
731
732impl std::hash::Hash for AverageRating {
733    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
734        self.0.to_bits().hash(state)
735    }
736}
737
738#[derive(Debug)]
739pub struct InvalidVersion;
740
741impl std::fmt::Display for InvalidVersion {
742    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
743        write!(f, "Invalid version")
744    }
745}
746
747impl std::error::Error for InvalidVersion {}
748
749/// An API version.
750#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, SubsonicType)]
751#[subsonic(serde)]
752pub struct Version {
753    pub major: u8,
754    pub minor: u8,
755    pub patch: u8,
756}
757impl_from_query_value_for_parse!(Version);
758impl_to_query_value_for_display!(Version);
759
760impl Version {
761    pub const LATEST: Self = Self::V1_16_1;
762    pub const V1_16_1: Self = Self::new(1, 16, 1);
763    pub const V1_16_0: Self = Self::new(1, 16, 0);
764    pub const V1_15_0: Self = Self::new(1, 15, 0);
765    pub const V1_14_0: Self = Self::new(1, 14, 0);
766    pub const V1_13_0: Self = Self::new(1, 13, 0);
767    pub const V1_12_0: Self = Self::new(1, 12, 0);
768    pub const V1_11_0: Self = Self::new(1, 11, 0);
769    pub const V1_10_2: Self = Self::new(1, 10, 2);
770    pub const V1_9_0: Self = Self::new(1, 9, 0);
771    pub const V1_8_0: Self = Self::new(1, 8, 0);
772    pub const V1_7_0: Self = Self::new(1, 7, 0);
773    pub const V1_6_0: Self = Self::new(1, 6, 0);
774    pub const V1_5_0: Self = Self::new(1, 5, 0);
775    pub const V1_4_0: Self = Self::new(1, 4, 0);
776    pub const V1_3_0: Self = Self::new(1, 3, 0);
777    pub const V1_2_0: Self = Self::new(1, 2, 0);
778    pub const V1_1_1: Self = Self::new(1, 1, 1);
779    pub const V1_1_0: Self = Self::new(1, 1, 0);
780
781    pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
782        Self {
783            major,
784            minor,
785            patch,
786        }
787    }
788
789    pub const fn as_u32(self) -> u32 {
790        (self.major as u32) << 16 | (self.minor as u32) << 8 | (self.patch as u32)
791    }
792}
793
794impl std::fmt::Display for Version {
795    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
796        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
797    }
798}
799
800impl std::str::FromStr for Version {
801    type Err = InvalidVersion;
802
803    fn from_str(s: &str) -> Result<Self, Self::Err> {
804        let mut parts = s.split('.');
805        let major = parts
806            .next()
807            .ok_or(InvalidVersion)?
808            .parse()
809            .map_err(|_| InvalidVersion)?;
810        let minor = parts
811            .next()
812            .ok_or(InvalidVersion)?
813            .parse()
814            .map_err(|_| InvalidVersion)?;
815        let patch = parts
816            .next()
817            .ok_or(InvalidVersion)?
818            .parse()
819            .map_err(|_| InvalidVersion)?;
820        Ok(Self::new(major, minor, patch))
821    }
822}
823
824impl<N1, N2, N3> From<(N1, N2, N3)> for Version
825where
826    N1: Into<u8>,
827    N2: Into<u8>,
828    N3: Into<u8>,
829{
830    fn from(value: (N1, N2, N3)) -> Self {
831        Self::new(value.0.into(), value.1.into(), value.2.into())
832    }
833}
834
835impl serde::Serialize for Version {
836    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
837    where
838        S: serde::Serializer,
839    {
840        self.to_string().serialize(serializer)
841    }
842}
843
844impl<'de> serde::Deserialize<'de> for Version {
845    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
846    where
847        D: serde::Deserializer<'de>,
848    {
849        let s = String::deserialize(deserializer)?;
850        s.parse().map_err(serde::de::Error::custom)
851    }
852}
853
854#[cfg(test)]
855mod tests {
856    use super::*;
857
858    #[test]
859    fn test_milliseconds() {
860        let ms = "123456789";
861        let ms = ms.parse::<Milliseconds>().unwrap();
862        assert_eq!(ms.0, 123456789);
863
864        let ms = Milliseconds::new(123456789);
865        let ms = serde_json::to_string(&ms).unwrap();
866        assert_eq!(ms, "123456789");
867    }
868
869    #[test]
870    fn test_seconds() {
871        let s = "123456789";
872        let s = s.parse::<Seconds>().unwrap();
873        assert_eq!(s.0, 123456789);
874
875        let s = Seconds::new(123456789);
876        let s = serde_json::to_string(&s).unwrap();
877        assert_eq!(s, "123456789");
878    }
879
880    #[test]
881    fn test_video_size() {
882        let s = "1920x1080";
883        let s = s.parse::<VideoSize>().unwrap();
884        assert_eq!(s.width, 1920);
885        assert_eq!(s.height, 1080);
886
887        let s = VideoSize::new(1920, 1080);
888        let s = serde_json::to_string(&s).unwrap();
889        assert_eq!(s, "\"1920x1080\"");
890    }
891
892    #[test]
893    fn test_video_bitrate() {
894        let b = "123456789";
895        let b = b.parse::<VideoBitrate>().unwrap();
896        assert_eq!(b.bitrate, 123456789);
897        assert_eq!(b.size, None);
898
899        let b = "123456789@1920x1080";
900        let b = b.parse::<VideoBitrate>().unwrap();
901        assert_eq!(b.bitrate, 123456789);
902        assert_eq!(b.size, Some(VideoSize::new(1920, 1080)));
903    }
904}