1use 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#[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#[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#[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
168impl 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#[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#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SubsonicType)]
453#[subsonic(serde)]
454pub enum AudioBitrate {
455 NoLimit,
457 Kbps32,
459 Kbps40,
461 Kbps48,
463 Kbps56,
465 Kbps64,
467 Kbps80,
469 Kbps96,
471 Kbps112,
473 Kbps128,
475 Kbps160,
477 Kbps192,
479 Kbps224,
481 Kbps256,
483 Kbps320,
485 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#[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#[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#[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}