1use std::fmt;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
35#[non_exhaustive]
36pub enum VideoCodec {
37 #[default]
39 H264,
40 H265,
42 Vp8,
44 Vp9,
46 Av1,
48 ProRes,
50 Mpeg4,
52 Mpeg2,
54 Mjpeg,
56 Unknown,
58}
59
60impl VideoCodec {
61 #[must_use]
72 pub const fn name(&self) -> &'static str {
73 match self {
74 Self::H264 => "h264",
75 Self::H265 => "hevc",
76 Self::Vp8 => "vp8",
77 Self::Vp9 => "vp9",
78 Self::Av1 => "av1",
79 Self::ProRes => "prores",
80 Self::Mpeg4 => "mpeg4",
81 Self::Mpeg2 => "mpeg2video",
82 Self::Mjpeg => "mjpeg",
83 Self::Unknown => "unknown",
84 }
85 }
86
87 #[must_use]
98 pub const fn display_name(&self) -> &'static str {
99 match self {
100 Self::H264 => "H.264/AVC",
101 Self::H265 => "H.265/HEVC",
102 Self::Vp8 => "VP8",
103 Self::Vp9 => "VP9",
104 Self::Av1 => "AV1",
105 Self::ProRes => "Apple ProRes",
106 Self::Mpeg4 => "MPEG-4 Part 2",
107 Self::Mpeg2 => "MPEG-2",
108 Self::Mjpeg => "Motion JPEG",
109 Self::Unknown => "Unknown",
110 }
111 }
112
113 #[must_use]
124 pub const fn is_h264_family(&self) -> bool {
125 matches!(self, Self::H264)
126 }
127
128 #[must_use]
139 pub const fn is_h265_family(&self) -> bool {
140 matches!(self, Self::H265)
141 }
142
143 #[must_use]
155 pub const fn is_vp_family(&self) -> bool {
156 matches!(self, Self::Vp8 | Self::Vp9)
157 }
158
159 #[must_use]
170 pub const fn is_professional(&self) -> bool {
171 matches!(self, Self::ProRes)
172 }
173
174 #[must_use]
186 pub const fn has_hardware_support(&self) -> bool {
187 matches!(self, Self::H264 | Self::H265 | Self::Vp9 | Self::Av1)
188 }
189
190 #[must_use]
201 pub const fn is_unknown(&self) -> bool {
202 matches!(self, Self::Unknown)
203 }
204}
205
206impl fmt::Display for VideoCodec {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(f, "{}", self.display_name())
209 }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
226#[non_exhaustive]
227pub enum AudioCodec {
228 #[default]
230 Aac,
231 Mp3,
233 Opus,
235 Flac,
237 Pcm,
239 Vorbis,
241 Ac3,
243 Eac3,
245 Dts,
247 Alac,
249 Unknown,
251}
252
253impl AudioCodec {
254 #[must_use]
265 pub const fn name(&self) -> &'static str {
266 match self {
267 Self::Aac => "aac",
268 Self::Mp3 => "mp3",
269 Self::Opus => "opus",
270 Self::Flac => "flac",
271 Self::Pcm => "pcm",
272 Self::Vorbis => "vorbis",
273 Self::Ac3 => "ac3",
274 Self::Eac3 => "eac3",
275 Self::Dts => "dts",
276 Self::Alac => "alac",
277 Self::Unknown => "unknown",
278 }
279 }
280
281 #[must_use]
292 pub const fn display_name(&self) -> &'static str {
293 match self {
294 Self::Aac => "AAC",
295 Self::Mp3 => "MP3",
296 Self::Opus => "Opus",
297 Self::Flac => "FLAC",
298 Self::Pcm => "PCM",
299 Self::Vorbis => "Vorbis",
300 Self::Ac3 => "Dolby Digital (AC-3)",
301 Self::Eac3 => "Dolby Digital Plus (E-AC-3)",
302 Self::Dts => "DTS",
303 Self::Alac => "Apple Lossless",
304 Self::Unknown => "Unknown",
305 }
306 }
307
308 #[must_use]
322 pub const fn is_lossy(&self) -> bool {
323 matches!(
324 self,
325 Self::Aac | Self::Mp3 | Self::Opus | Self::Vorbis | Self::Ac3 | Self::Eac3 | Self::Dts
326 )
327 }
328
329 #[must_use]
344 pub const fn is_lossless(&self) -> bool {
345 matches!(self, Self::Flac | Self::Pcm | Self::Alac)
346 }
347
348 #[must_use]
360 pub const fn is_surround(&self) -> bool {
361 matches!(self, Self::Ac3 | Self::Eac3 | Self::Dts)
362 }
363
364 #[must_use]
375 pub const fn is_unknown(&self) -> bool {
376 matches!(self, Self::Unknown)
377 }
378}
379
380impl fmt::Display for AudioCodec {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 write!(f, "{}", self.display_name())
383 }
384}
385
386#[derive(Debug, Clone, PartialEq, Eq, Hash)]
394pub enum SubtitleCodec {
395 Srt,
397 Ass,
399 Dvb,
401 Hdmv,
403 Webvtt,
405 Other(String),
407}
408
409impl SubtitleCodec {
410 #[must_use]
421 pub fn name(&self) -> &str {
422 match self {
423 Self::Srt => "srt",
424 Self::Ass => "ass",
425 Self::Dvb => "dvb_subtitle",
426 Self::Hdmv => "hdmv_pgs_subtitle",
427 Self::Webvtt => "webvtt",
428 Self::Other(name) => name.as_str(),
429 }
430 }
431
432 #[must_use]
443 pub fn display_name(&self) -> &str {
444 match self {
445 Self::Srt => "SubRip (SRT)",
446 Self::Ass => "ASS/SSA",
447 Self::Dvb => "DVB Subtitle",
448 Self::Hdmv => "HDMV/PGS",
449 Self::Webvtt => "WebVTT",
450 Self::Other(name) => name.as_str(),
451 }
452 }
453
454 #[must_use]
466 pub fn is_text_based(&self) -> bool {
467 matches!(self, Self::Srt | Self::Ass | Self::Webvtt)
468 }
469
470 #[must_use]
482 pub fn is_bitmap_based(&self) -> bool {
483 matches!(self, Self::Dvb | Self::Hdmv)
484 }
485
486 #[must_use]
497 pub fn is_unknown(&self) -> bool {
498 matches!(self, Self::Other(_))
499 }
500}
501
502impl fmt::Display for SubtitleCodec {
503 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504 write!(f, "{}", self.display_name())
505 }
506}
507
508#[cfg(test)]
509mod tests {
510 use super::*;
511
512 mod video_codec_tests {
513 use super::*;
514
515 #[test]
516 fn test_names() {
517 assert_eq!(VideoCodec::H264.name(), "h264");
518 assert_eq!(VideoCodec::H265.name(), "hevc");
519 assert_eq!(VideoCodec::Vp8.name(), "vp8");
520 assert_eq!(VideoCodec::Vp9.name(), "vp9");
521 assert_eq!(VideoCodec::Av1.name(), "av1");
522 assert_eq!(VideoCodec::ProRes.name(), "prores");
523 assert_eq!(VideoCodec::Mpeg4.name(), "mpeg4");
524 assert_eq!(VideoCodec::Mpeg2.name(), "mpeg2video");
525 assert_eq!(VideoCodec::Mjpeg.name(), "mjpeg");
526 assert_eq!(VideoCodec::Unknown.name(), "unknown");
527 }
528
529 #[test]
530 fn test_display_names() {
531 assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
532 assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
533 assert_eq!(VideoCodec::ProRes.display_name(), "Apple ProRes");
534 }
535
536 #[test]
537 fn test_display() {
538 assert_eq!(format!("{}", VideoCodec::H264), "H.264/AVC");
539 assert_eq!(format!("{}", VideoCodec::Av1), "AV1");
540 }
541
542 #[test]
543 fn test_default() {
544 assert_eq!(VideoCodec::default(), VideoCodec::H264);
545 }
546
547 #[test]
548 fn test_codec_families() {
549 assert!(VideoCodec::H264.is_h264_family());
550 assert!(!VideoCodec::H265.is_h264_family());
551
552 assert!(VideoCodec::H265.is_h265_family());
553 assert!(!VideoCodec::H264.is_h265_family());
554
555 assert!(VideoCodec::Vp8.is_vp_family());
556 assert!(VideoCodec::Vp9.is_vp_family());
557 assert!(!VideoCodec::H264.is_vp_family());
558 }
559
560 #[test]
561 fn test_is_professional() {
562 assert!(VideoCodec::ProRes.is_professional());
563 assert!(!VideoCodec::H264.is_professional());
564 assert!(!VideoCodec::Unknown.is_professional());
565 }
566
567 #[test]
568 fn test_hardware_support() {
569 assert!(VideoCodec::H264.has_hardware_support());
570 assert!(VideoCodec::H265.has_hardware_support());
571 assert!(VideoCodec::Vp9.has_hardware_support());
572 assert!(VideoCodec::Av1.has_hardware_support());
573 assert!(!VideoCodec::ProRes.has_hardware_support());
574 assert!(!VideoCodec::Mjpeg.has_hardware_support());
575 }
576
577 #[test]
578 fn test_is_unknown() {
579 assert!(VideoCodec::Unknown.is_unknown());
580 assert!(!VideoCodec::H264.is_unknown());
581 }
582
583 #[test]
584 fn test_debug() {
585 assert_eq!(format!("{:?}", VideoCodec::H264), "H264");
586 assert_eq!(format!("{:?}", VideoCodec::H265), "H265");
587 }
588
589 #[test]
590 fn test_equality_and_hash() {
591 use std::collections::HashSet;
592
593 assert_eq!(VideoCodec::H264, VideoCodec::H264);
594 assert_ne!(VideoCodec::H264, VideoCodec::H265);
595
596 let mut set = HashSet::new();
597 set.insert(VideoCodec::H264);
598 set.insert(VideoCodec::H265);
599 assert!(set.contains(&VideoCodec::H264));
600 assert!(!set.contains(&VideoCodec::Vp9));
601 }
602
603 #[test]
604 fn test_copy() {
605 let codec = VideoCodec::H264;
606 let copied = codec;
607 assert_eq!(codec, copied);
608 }
609 }
610
611 mod subtitle_codec_tests {
612 use super::*;
613
614 #[test]
615 fn name_should_return_short_codec_name() {
616 assert_eq!(SubtitleCodec::Srt.name(), "srt");
617 assert_eq!(SubtitleCodec::Ass.name(), "ass");
618 assert_eq!(SubtitleCodec::Dvb.name(), "dvb_subtitle");
619 assert_eq!(SubtitleCodec::Hdmv.name(), "hdmv_pgs_subtitle");
620 assert_eq!(SubtitleCodec::Webvtt.name(), "webvtt");
621 assert_eq!(
622 SubtitleCodec::Other("dvd_subtitle".to_string()).name(),
623 "dvd_subtitle"
624 );
625 }
626
627 #[test]
628 fn display_name_should_return_human_readable_name() {
629 assert_eq!(SubtitleCodec::Srt.display_name(), "SubRip (SRT)");
630 assert_eq!(SubtitleCodec::Ass.display_name(), "ASS/SSA");
631 assert_eq!(SubtitleCodec::Dvb.display_name(), "DVB Subtitle");
632 assert_eq!(SubtitleCodec::Hdmv.display_name(), "HDMV/PGS");
633 assert_eq!(SubtitleCodec::Webvtt.display_name(), "WebVTT");
634 }
635
636 #[test]
637 fn display_should_use_display_name() {
638 assert_eq!(format!("{}", SubtitleCodec::Srt), "SubRip (SRT)");
639 assert_eq!(format!("{}", SubtitleCodec::Hdmv), "HDMV/PGS");
640 }
641
642 #[test]
643 fn is_text_based_should_return_true_for_text_codecs() {
644 assert!(SubtitleCodec::Srt.is_text_based());
645 assert!(SubtitleCodec::Ass.is_text_based());
646 assert!(SubtitleCodec::Webvtt.is_text_based());
647 assert!(!SubtitleCodec::Dvb.is_text_based());
648 assert!(!SubtitleCodec::Hdmv.is_text_based());
649 }
650
651 #[test]
652 fn is_bitmap_based_should_return_true_for_bitmap_codecs() {
653 assert!(SubtitleCodec::Dvb.is_bitmap_based());
654 assert!(SubtitleCodec::Hdmv.is_bitmap_based());
655 assert!(!SubtitleCodec::Srt.is_bitmap_based());
656 assert!(!SubtitleCodec::Ass.is_bitmap_based());
657 assert!(!SubtitleCodec::Webvtt.is_bitmap_based());
658 }
659
660 #[test]
661 fn is_unknown_should_return_true_only_for_other_variant() {
662 assert!(SubtitleCodec::Other("dvd_subtitle".to_string()).is_unknown());
663 assert!(!SubtitleCodec::Srt.is_unknown());
664 assert!(!SubtitleCodec::Dvb.is_unknown());
665 }
666
667 #[test]
668 fn equality_should_compare_by_value() {
669 assert_eq!(SubtitleCodec::Srt, SubtitleCodec::Srt);
670 assert_ne!(SubtitleCodec::Srt, SubtitleCodec::Ass);
671 assert_eq!(
672 SubtitleCodec::Other("foo".to_string()),
673 SubtitleCodec::Other("foo".to_string())
674 );
675 assert_ne!(
676 SubtitleCodec::Other("foo".to_string()),
677 SubtitleCodec::Other("bar".to_string())
678 );
679 }
680
681 #[test]
682 fn clone_should_produce_equal_value() {
683 let codec = SubtitleCodec::Other("test".to_string());
684 let cloned = codec.clone();
685 assert_eq!(codec, cloned);
686 }
687 }
688
689 mod audio_codec_tests {
690 use super::*;
691
692 #[test]
693 fn test_names() {
694 assert_eq!(AudioCodec::Aac.name(), "aac");
695 assert_eq!(AudioCodec::Mp3.name(), "mp3");
696 assert_eq!(AudioCodec::Opus.name(), "opus");
697 assert_eq!(AudioCodec::Flac.name(), "flac");
698 assert_eq!(AudioCodec::Pcm.name(), "pcm");
699 assert_eq!(AudioCodec::Vorbis.name(), "vorbis");
700 assert_eq!(AudioCodec::Ac3.name(), "ac3");
701 assert_eq!(AudioCodec::Eac3.name(), "eac3");
702 assert_eq!(AudioCodec::Dts.name(), "dts");
703 assert_eq!(AudioCodec::Alac.name(), "alac");
704 assert_eq!(AudioCodec::Unknown.name(), "unknown");
705 }
706
707 #[test]
708 fn test_display_names() {
709 assert_eq!(AudioCodec::Aac.display_name(), "AAC");
710 assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
711 assert_eq!(AudioCodec::Ac3.display_name(), "Dolby Digital (AC-3)");
712 }
713
714 #[test]
715 fn test_display() {
716 assert_eq!(format!("{}", AudioCodec::Aac), "AAC");
717 assert_eq!(format!("{}", AudioCodec::Opus), "Opus");
718 }
719
720 #[test]
721 fn test_default() {
722 assert_eq!(AudioCodec::default(), AudioCodec::Aac);
723 }
724
725 #[test]
726 fn test_lossy_lossless() {
727 assert!(AudioCodec::Aac.is_lossy());
729 assert!(AudioCodec::Mp3.is_lossy());
730 assert!(AudioCodec::Opus.is_lossy());
731 assert!(AudioCodec::Vorbis.is_lossy());
732 assert!(AudioCodec::Ac3.is_lossy());
733 assert!(AudioCodec::Eac3.is_lossy());
734 assert!(AudioCodec::Dts.is_lossy());
735
736 assert!(AudioCodec::Flac.is_lossless());
738 assert!(AudioCodec::Pcm.is_lossless());
739 assert!(AudioCodec::Alac.is_lossless());
740
741 assert!(!AudioCodec::Aac.is_lossless());
743 assert!(!AudioCodec::Flac.is_lossy());
744 }
745
746 #[test]
747 fn test_surround() {
748 assert!(AudioCodec::Ac3.is_surround());
749 assert!(AudioCodec::Eac3.is_surround());
750 assert!(AudioCodec::Dts.is_surround());
751 assert!(!AudioCodec::Aac.is_surround());
752 assert!(!AudioCodec::Flac.is_surround());
753 }
754
755 #[test]
756 fn test_is_unknown() {
757 assert!(AudioCodec::Unknown.is_unknown());
758 assert!(!AudioCodec::Aac.is_unknown());
759 }
760
761 #[test]
762 fn test_debug() {
763 assert_eq!(format!("{:?}", AudioCodec::Aac), "Aac");
764 assert_eq!(format!("{:?}", AudioCodec::Flac), "Flac");
765 }
766
767 #[test]
768 fn test_equality_and_hash() {
769 use std::collections::HashSet;
770
771 assert_eq!(AudioCodec::Aac, AudioCodec::Aac);
772 assert_ne!(AudioCodec::Aac, AudioCodec::Mp3);
773
774 let mut set = HashSet::new();
775 set.insert(AudioCodec::Aac);
776 set.insert(AudioCodec::Flac);
777 assert!(set.contains(&AudioCodec::Aac));
778 assert!(!set.contains(&AudioCodec::Opus));
779 }
780
781 #[test]
782 fn test_copy() {
783 let codec = AudioCodec::Aac;
784 let copied = codec;
785 assert_eq!(codec, copied);
786 }
787 }
788}