Skip to main content

ff_format/
codec.rs

1//! Video and audio codec definitions.
2//!
3//! This module provides enums for identifying video and audio codecs
4//! commonly used in media files.
5//!
6//! # Examples
7//!
8//! ```
9//! use ff_format::codec::{VideoCodec, AudioCodec};
10//!
11//! let video = VideoCodec::H264;
12//! assert!(video.is_h264_family());
13//! assert_eq!(video.name(), "h264");
14//!
15//! let audio = AudioCodec::Aac;
16//! assert!(audio.is_lossy());
17//! ```
18
19use std::fmt;
20
21/// Video codec identifier.
22///
23/// This enum represents common video codecs used in media files.
24/// It covers the most widely used codecs while remaining extensible
25/// via the `Unknown` variant.
26///
27/// # Common Usage
28///
29/// - **H.264/AVC**: Most common codec for HD video, excellent compatibility
30/// - **H.265/HEVC**: Better compression than H.264, used for 4K content
31/// - **VP9**: Google's open codec for web video streaming
32/// - **AV1**: Next-gen open codec, excellent compression
33/// - **Apple `ProRes`**: Apple's professional editing codec
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
35#[non_exhaustive]
36pub enum VideoCodec {
37    /// H.264/AVC - most common video codec
38    #[default]
39    H264,
40    /// H.265/HEVC - successor to H.264, better compression
41    H265,
42    /// VP8 - Google's older open codec
43    Vp8,
44    /// VP9 - Google's open codec for web video streaming
45    Vp9,
46    /// AV1 - Alliance for Open Media codec, next-gen compression
47    Av1,
48    /// Apple's professional editing codec
49    ProRes,
50    /// MPEG-4 Part 2 - older codec, legacy support
51    Mpeg4,
52    /// MPEG-2 Video - DVD and broadcast standard
53    Mpeg2,
54    /// MJPEG - Motion JPEG, used by some cameras
55    Mjpeg,
56    /// Unknown or unsupported codec
57    Unknown,
58}
59
60impl VideoCodec {
61    /// Returns the codec name as a human-readable string.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use ff_format::codec::VideoCodec;
67    ///
68    /// assert_eq!(VideoCodec::H264.name(), "h264");
69    /// assert_eq!(VideoCodec::H265.name(), "hevc");
70    /// ```
71    #[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    /// Returns the human-readable display name for the codec.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// use ff_format::codec::VideoCodec;
93    ///
94    /// assert_eq!(VideoCodec::H264.display_name(), "H.264/AVC");
95    /// assert_eq!(VideoCodec::H265.display_name(), "H.265/HEVC");
96    /// ```
97    #[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    /// Returns `true` if this is part of the H.264 family.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use ff_format::codec::VideoCodec;
119    ///
120    /// assert!(VideoCodec::H264.is_h264_family());
121    /// assert!(!VideoCodec::H265.is_h264_family());
122    /// ```
123    #[must_use]
124    pub const fn is_h264_family(&self) -> bool {
125        matches!(self, Self::H264)
126    }
127
128    /// Returns `true` if this is part of the H.265/HEVC family.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use ff_format::codec::VideoCodec;
134    ///
135    /// assert!(VideoCodec::H265.is_h265_family());
136    /// assert!(!VideoCodec::H264.is_h265_family());
137    /// ```
138    #[must_use]
139    pub const fn is_h265_family(&self) -> bool {
140        matches!(self, Self::H265)
141    }
142
143    /// Returns `true` if this is a Google/WebM codec (VP8, VP9).
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// use ff_format::codec::VideoCodec;
149    ///
150    /// assert!(VideoCodec::Vp8.is_vp_family());
151    /// assert!(VideoCodec::Vp9.is_vp_family());
152    /// assert!(!VideoCodec::H264.is_vp_family());
153    /// ```
154    #[must_use]
155    pub const fn is_vp_family(&self) -> bool {
156        matches!(self, Self::Vp8 | Self::Vp9)
157    }
158
159    /// Returns `true` if this is a professional/editing codec.
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use ff_format::codec::VideoCodec;
165    ///
166    /// assert!(VideoCodec::ProRes.is_professional());
167    /// assert!(!VideoCodec::H264.is_professional());
168    /// ```
169    #[must_use]
170    pub const fn is_professional(&self) -> bool {
171        matches!(self, Self::ProRes)
172    }
173
174    /// Returns `true` if this codec supports hardware acceleration on most platforms.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// use ff_format::codec::VideoCodec;
180    ///
181    /// assert!(VideoCodec::H264.has_hardware_support());
182    /// assert!(VideoCodec::H265.has_hardware_support());
183    /// assert!(!VideoCodec::ProRes.has_hardware_support());
184    /// ```
185    #[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    /// Returns `true` if the codec is unknown.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use ff_format::codec::VideoCodec;
196    ///
197    /// assert!(VideoCodec::Unknown.is_unknown());
198    /// assert!(!VideoCodec::H264.is_unknown());
199    /// ```
200    #[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/// Audio codec identifier.
213///
214/// This enum represents common audio codecs used in media files.
215/// It covers the most widely used codecs while remaining extensible
216/// via the `Unknown` variant.
217///
218/// # Common Usage
219///
220/// - **AAC**: Most common for streaming and mobile
221/// - **MP3**: Legacy but still widely supported
222/// - **Opus**: Excellent quality at low bitrates, used for voice communication
223/// - **FLAC**: Lossless compression
224/// - **PCM**: Uncompressed audio
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
226#[non_exhaustive]
227pub enum AudioCodec {
228    /// AAC (Advanced Audio Coding) - most common lossy codec
229    #[default]
230    Aac,
231    /// MP3 (MPEG-1 Audio Layer 3) - legacy lossy codec
232    Mp3,
233    /// Opus - modern lossy codec, excellent at low bitrates
234    Opus,
235    /// FLAC (Free Lossless Audio Codec) - lossless compression
236    Flac,
237    /// PCM (Pulse Code Modulation) - uncompressed audio
238    Pcm,
239    /// Vorbis - open lossy codec, used in Ogg containers
240    Vorbis,
241    /// AC3 (Dolby Digital) - surround sound codec
242    Ac3,
243    /// EAC3 (Dolby Digital Plus) - enhanced AC3
244    Eac3,
245    /// DTS (Digital Theater Systems) - surround sound codec
246    Dts,
247    /// ALAC (Apple Lossless Audio Codec)
248    Alac,
249    /// Unknown or unsupported codec
250    Unknown,
251}
252
253impl AudioCodec {
254    /// Returns the codec name as a human-readable string.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use ff_format::codec::AudioCodec;
260    ///
261    /// assert_eq!(AudioCodec::Aac.name(), "aac");
262    /// assert_eq!(AudioCodec::Flac.name(), "flac");
263    /// ```
264    #[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    /// Returns the human-readable display name for the codec.
282    ///
283    /// # Examples
284    ///
285    /// ```
286    /// use ff_format::codec::AudioCodec;
287    ///
288    /// assert_eq!(AudioCodec::Aac.display_name(), "AAC");
289    /// assert_eq!(AudioCodec::Flac.display_name(), "FLAC");
290    /// ```
291    #[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    /// Returns `true` if this is a lossy codec.
309    ///
310    /// Lossy codecs discard some audio data for smaller file sizes.
311    ///
312    /// # Examples
313    ///
314    /// ```
315    /// use ff_format::codec::AudioCodec;
316    ///
317    /// assert!(AudioCodec::Aac.is_lossy());
318    /// assert!(AudioCodec::Mp3.is_lossy());
319    /// assert!(!AudioCodec::Flac.is_lossy());
320    /// ```
321    #[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    /// Returns `true` if this is a lossless codec.
330    ///
331    /// Lossless codecs preserve all audio data.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use ff_format::codec::AudioCodec;
337    ///
338    /// assert!(AudioCodec::Flac.is_lossless());
339    /// assert!(AudioCodec::Pcm.is_lossless());
340    /// assert!(AudioCodec::Alac.is_lossless());
341    /// assert!(!AudioCodec::Aac.is_lossless());
342    /// ```
343    #[must_use]
344    pub const fn is_lossless(&self) -> bool {
345        matches!(self, Self::Flac | Self::Pcm | Self::Alac)
346    }
347
348    /// Returns `true` if this is a surround sound codec.
349    ///
350    /// # Examples
351    ///
352    /// ```
353    /// use ff_format::codec::AudioCodec;
354    ///
355    /// assert!(AudioCodec::Ac3.is_surround());
356    /// assert!(AudioCodec::Dts.is_surround());
357    /// assert!(!AudioCodec::Aac.is_surround());
358    /// ```
359    #[must_use]
360    pub const fn is_surround(&self) -> bool {
361        matches!(self, Self::Ac3 | Self::Eac3 | Self::Dts)
362    }
363
364    /// Returns `true` if the codec is unknown.
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use ff_format::codec::AudioCodec;
370    ///
371    /// assert!(AudioCodec::Unknown.is_unknown());
372    /// assert!(!AudioCodec::Aac.is_unknown());
373    /// ```
374    #[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/// Subtitle codec identifier.
387///
388/// This enum represents common subtitle codecs used in media files.
389/// It covers text-based and bitmap-based formats while remaining extensible
390/// via the `Other` variant.
391///
392/// Note: No `Copy` or `Default` derive because `Other(String)` is not `Copy`.
393#[derive(Debug, Clone, PartialEq, Eq, Hash)]
394pub enum SubtitleCodec {
395    /// `SubRip` / SRT — text-based timed subtitles
396    Srt,
397    /// ASS/SSA (Advanced `SubStation` Alpha) — styled text subtitles
398    Ass,
399    /// DVB bitmap subtitles (digital broadcast)
400    Dvb,
401    /// HDMV/PGS — Blu-ray bitmap subtitles
402    Hdmv,
403    /// `WebVTT` — web-standard text subtitles
404    Webvtt,
405    /// Unrecognized codec; raw codec name stored for transparency
406    Other(String),
407}
408
409impl SubtitleCodec {
410    /// Returns the codec name as a short string.
411    ///
412    /// # Examples
413    ///
414    /// ```
415    /// use ff_format::codec::SubtitleCodec;
416    ///
417    /// assert_eq!(SubtitleCodec::Srt.name(), "srt");
418    /// assert_eq!(SubtitleCodec::Ass.name(), "ass");
419    /// ```
420    #[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    /// Returns the human-readable display name for the codec.
433    ///
434    /// # Examples
435    ///
436    /// ```
437    /// use ff_format::codec::SubtitleCodec;
438    ///
439    /// assert_eq!(SubtitleCodec::Srt.display_name(), "SubRip (SRT)");
440    /// assert_eq!(SubtitleCodec::Hdmv.display_name(), "HDMV/PGS");
441    /// ```
442    #[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    /// Returns `true` if this is a text-based subtitle codec.
455    ///
456    /// # Examples
457    ///
458    /// ```
459    /// use ff_format::codec::SubtitleCodec;
460    ///
461    /// assert!(SubtitleCodec::Srt.is_text_based());
462    /// assert!(SubtitleCodec::Ass.is_text_based());
463    /// assert!(!SubtitleCodec::Dvb.is_text_based());
464    /// ```
465    #[must_use]
466    pub fn is_text_based(&self) -> bool {
467        matches!(self, Self::Srt | Self::Ass | Self::Webvtt)
468    }
469
470    /// Returns `true` if this is a bitmap-based subtitle codec.
471    ///
472    /// # Examples
473    ///
474    /// ```
475    /// use ff_format::codec::SubtitleCodec;
476    ///
477    /// assert!(SubtitleCodec::Dvb.is_bitmap_based());
478    /// assert!(SubtitleCodec::Hdmv.is_bitmap_based());
479    /// assert!(!SubtitleCodec::Srt.is_bitmap_based());
480    /// ```
481    #[must_use]
482    pub fn is_bitmap_based(&self) -> bool {
483        matches!(self, Self::Dvb | Self::Hdmv)
484    }
485
486    /// Returns `true` if the codec is unrecognized.
487    ///
488    /// # Examples
489    ///
490    /// ```
491    /// use ff_format::codec::SubtitleCodec;
492    ///
493    /// assert!(SubtitleCodec::Other("dvd_subtitle".to_string()).is_unknown());
494    /// assert!(!SubtitleCodec::Srt.is_unknown());
495    /// ```
496    #[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            // Lossy codecs
728            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            // Lossless codecs
737            assert!(AudioCodec::Flac.is_lossless());
738            assert!(AudioCodec::Pcm.is_lossless());
739            assert!(AudioCodec::Alac.is_lossless());
740
741            // Mutual exclusion
742            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}