Skip to main content

imferno_core/validation/
iab_codes.rs

1//! Typed validation-code catalogue for SMPTE ST 2067-201 (IAB Level 0 Plug-in).
2//!
3//! The same 21 reason codes apply to both the 2019 and 2021 editions; a
4//! `macro_rules!` generates `St2067_201_2019` and `St2067_201_2021` from a
5//! single source-of-truth variant list.  Each edition enum also exposes
6//! `for_code` so that shared helper functions can produce the right
7//! `&'static str` without any runtime string building.
8
9use crate::diagnostics::codes::ValidationCode;
10use crate::diagnostics::{Category, Severity};
11
12// ─────────────────────────────────────────────────────────────────────────────
13// Spec-agnostic reason codes
14// ─────────────────────────────────────────────────────────────────────────────
15
16/// Spec-agnostic reason codes for IAB Level 0 Plug-in validation.
17///
18/// Passed to each edition's `for_code` dispatch function to get the full
19/// `&'static str` code without any runtime string building.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum IabCode {
22    /// IABEssenceDescriptor: Codec item shall not be present (§5.9).
23    CodecForbidden,
24    /// IABEssenceDescriptor: ElectrospatialFormulation shall not be present (§5.9).
25    ElectrospatialFormulationForbidden,
26    /// IABEssenceDescriptor: QuantizationBits is missing; shall be 24 (§5.9).
27    QuantizationBitsMissing,
28    /// IABEssenceDescriptor: QuantizationBits is present but not 24 (§5.9).
29    QuantizationBitsInvalid,
30    /// IABEssenceDescriptor: ContainerFormat is missing (§5.3).
31    ContainerFormatMissing,
32    /// IABEssenceDescriptor: ContainerFormat is not the required IAB container UL (§5.3).
33    EssenceContainerInvalid,
34    /// IABEssenceDescriptor: AudioSampleRate is missing; shall be 48000/1 (§5.9).
35    AudioSamplingRateMissing,
36    /// IABEssenceDescriptor: AudioSampleRate is not 48000/1 (§5.9).
37    AudioSamplingRateInvalid,
38    /// IABEssenceDescriptor: SoundCompression is missing (§5.9).
39    SoundCompressionMissing,
40    /// IABEssenceDescriptor: SoundCompression is not the required IAB compression UL (§5.9).
41    SoundCompressionInvalid,
42    /// IABEssenceDescriptor: ChannelCount shall be the distinguished value 0 (§5.9, 2019 only).
43    ChannelCountNotZero,
44    /// IABEssenceDescriptor: IABSoundfieldLabelSubDescriptor shall be present (§5.9).
45    SubDescriptorMissing,
46    /// IABSoundfieldLabelSubDescriptor: MCATagSymbol shall be "IAB" — missing (§5.9).
47    MCATagSymbolMissing,
48    /// IABSoundfieldLabelSubDescriptor: MCATagSymbol shall be "IAB" — wrong value (§5.9).
49    MCATagSymbolInvalid,
50    /// IABSoundfieldLabelSubDescriptor: MCATagName shall be "IAB" — missing (§5.9).
51    MCATagNameMissing,
52    /// IABSoundfieldLabelSubDescriptor: MCATagName shall be "IAB" — wrong value (§5.9).
53    MCATagNameInvalid,
54    /// IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is missing (§5.9).
55    MCALabelDictionaryIDMissing,
56    /// IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is not the required IAB label UL (§5.9).
57    MCALabelDictionaryIDInvalid,
58    /// Segment has IABSequence but no MainAudioSequence (§6.2).
59    MainAudioMissing,
60    /// IABSequence shall contain at least one Resource (§6.2).
61    IABSequenceNoResources,
62    /// IABSequence Resource.SourceEncoding does not reference an IABEssenceDescriptor (§6.2).
63    IABSequenceSourceEncodingInvalid,
64}
65
66// ─────────────────────────────────────────────────────────────────────────────
67// Edition-specific enums (generated by macro)
68// ─────────────────────────────────────────────────────────────────────────────
69
70macro_rules! define_iab_enum {
71    ($name:ident, $prefix:literal) => {
72        /// IAB Level 0 Plug-in validation codes, edition
73        #[doc = $prefix]
74        #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
75        pub enum $name {
76            /// IABEssenceDescriptor: Codec item shall not be present (§5.9).
77            CodecForbidden,
78            /// IABEssenceDescriptor: ElectrospatialFormulation shall not be present (§5.9).
79            ElectrospatialFormulationForbidden,
80            /// IABEssenceDescriptor: QuantizationBits is missing; shall be 24 (§5.9).
81            QuantizationBitsMissing,
82            /// IABEssenceDescriptor: QuantizationBits is present but not 24 (§5.9).
83            QuantizationBitsInvalid,
84            /// IABEssenceDescriptor: ContainerFormat is missing (§5.3).
85            ContainerFormatMissing,
86            /// IABEssenceDescriptor: ContainerFormat is not the required IAB container UL (§5.3).
87            EssenceContainerInvalid,
88            /// IABEssenceDescriptor: AudioSampleRate is missing; shall be 48000/1 (§5.9).
89            AudioSamplingRateMissing,
90            /// IABEssenceDescriptor: AudioSampleRate is not 48000/1 (§5.9).
91            AudioSamplingRateInvalid,
92            /// IABEssenceDescriptor: SoundCompression is missing (§5.9).
93            SoundCompressionMissing,
94            /// IABEssenceDescriptor: SoundCompression is not the required IAB compression UL (§5.9).
95            SoundCompressionInvalid,
96            /// IABEssenceDescriptor: ChannelCount shall be the distinguished value 0 (§5.9, 2019 only).
97            ChannelCountNotZero,
98            /// IABEssenceDescriptor: IABSoundfieldLabelSubDescriptor shall be present (§5.9).
99            SubDescriptorMissing,
100            /// IABSoundfieldLabelSubDescriptor: MCATagSymbol shall be "IAB" — missing (§5.9).
101            MCATagSymbolMissing,
102            /// IABSoundfieldLabelSubDescriptor: MCATagSymbol shall be "IAB" — wrong value (§5.9).
103            MCATagSymbolInvalid,
104            /// IABSoundfieldLabelSubDescriptor: MCATagName shall be "IAB" — missing (§5.9).
105            MCATagNameMissing,
106            /// IABSoundfieldLabelSubDescriptor: MCATagName shall be "IAB" — wrong value (§5.9).
107            MCATagNameInvalid,
108            /// IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is missing (§5.9).
109            MCALabelDictionaryIDMissing,
110            /// IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is not the required IAB label UL (§5.9).
111            MCALabelDictionaryIDInvalid,
112            /// Segment has IABSequence but no MainAudioSequence (§6.2).
113            MainAudioMissing,
114            /// IABSequence shall contain at least one Resource (§6.2).
115            IABSequenceNoResources,
116            /// IABSequence Resource.SourceEncoding does not reference an IABEssenceDescriptor (§6.2).
117            IABSequenceSourceEncodingInvalid,
118        }
119
120        impl ValidationCode for $name {
121            fn code(&self) -> &'static str {
122                match self {
123                    Self::CodecForbidden                 => concat!($prefix, ":5.9/CodecForbidden"),
124                    Self::ElectrospatialFormulationForbidden => concat!($prefix, ":5.9/ElectrospatialFormulationForbidden"),
125                    Self::QuantizationBitsMissing        => concat!($prefix, ":5.9/QuantizationBitsMissing"),
126                    Self::QuantizationBitsInvalid        => concat!($prefix, ":5.9/QuantizationBitsInvalid"),
127                    Self::ContainerFormatMissing         => concat!($prefix, ":5.3/ContainerFormatMissing"),
128                    Self::EssenceContainerInvalid        => concat!($prefix, ":5.3/EssenceContainerInvalid"),
129                    Self::AudioSamplingRateMissing       => concat!($prefix, ":5.9/AudioSamplingRateMissing"),
130                    Self::AudioSamplingRateInvalid       => concat!($prefix, ":5.9/AudioSamplingRateInvalid"),
131                    Self::SoundCompressionMissing        => concat!($prefix, ":5.9/SoundCompressionMissing"),
132                    Self::SoundCompressionInvalid        => concat!($prefix, ":5.9/SoundCompressionInvalid"),
133                    Self::ChannelCountNotZero            => concat!($prefix, ":5.9/ChannelCountNotZero"),
134                    Self::SubDescriptorMissing           => concat!($prefix, ":5.9/SubDescriptorMissing"),
135                    Self::MCATagSymbolMissing            => concat!($prefix, ":5.9/MCATagSymbolMissing"),
136                    Self::MCATagSymbolInvalid            => concat!($prefix, ":5.9/MCATagSymbolInvalid"),
137                    Self::MCATagNameMissing              => concat!($prefix, ":5.9/MCATagNameMissing"),
138                    Self::MCATagNameInvalid              => concat!($prefix, ":5.9/MCATagNameInvalid"),
139                    Self::MCALabelDictionaryIDMissing    => concat!($prefix, ":5.9/MCALabelDictionaryIDMissing"),
140                    Self::MCALabelDictionaryIDInvalid    => concat!($prefix, ":5.9/MCALabelDictionaryIDInvalid"),
141                    Self::MainAudioMissing               => concat!($prefix, ":6.2/MainAudioMissing"),
142                    Self::IABSequenceNoResources         => concat!($prefix, ":6.2/IABSequenceNoResources"),
143                    Self::IABSequenceSourceEncodingInvalid => concat!($prefix, ":6.2/IABSequenceSourceEncodingInvalid"),
144                }
145            }
146            fn description(&self) -> &'static str {
147                match self {
148                    Self::CodecForbidden                 => "IABEssenceDescriptor: Codec item shall not be present (§5.9).",
149                    Self::ElectrospatialFormulationForbidden => "IABEssenceDescriptor: ElectrospatialFormulation shall not be present (§5.9).",
150                    Self::QuantizationBitsMissing        => "IABEssenceDescriptor: QuantizationBits is missing; shall be 24.",
151                    Self::QuantizationBitsInvalid        => "IABEssenceDescriptor: QuantizationBits shall be 24.",
152                    Self::ContainerFormatMissing         => "IABEssenceDescriptor: ContainerFormat is missing.",
153                    Self::EssenceContainerInvalid        => "IABEssenceDescriptor: ContainerFormat is not the required IAB container UL.",
154                    Self::AudioSamplingRateMissing       => "IABEssenceDescriptor: AudioSampleRate is missing; shall be 48000/1.",
155                    Self::AudioSamplingRateInvalid       => "IABEssenceDescriptor: AudioSampleRate shall be 48000/1.",
156                    Self::SoundCompressionMissing        => "IABEssenceDescriptor: SoundCompression is missing.",
157                    Self::SoundCompressionInvalid        => "IABEssenceDescriptor: SoundCompression is not the required IAB compression UL.",
158                    Self::ChannelCountNotZero            => "IABEssenceDescriptor: ChannelCount shall be the distinguished value 0 (2019 edition).",
159                    Self::SubDescriptorMissing           => "IABEssenceDescriptor: IABSoundfieldLabelSubDescriptor shall be present.",
160                    Self::MCATagSymbolMissing            => "IABSoundfieldLabelSubDescriptor: MCATagSymbol is missing; shall be \"IAB\".",
161                    Self::MCATagSymbolInvalid            => "IABSoundfieldLabelSubDescriptor: MCATagSymbol shall be \"IAB\".",
162                    Self::MCATagNameMissing              => "IABSoundfieldLabelSubDescriptor: MCATagName is missing; shall be \"IAB\".",
163                    Self::MCATagNameInvalid              => "IABSoundfieldLabelSubDescriptor: MCATagName shall be \"IAB\".",
164                    Self::MCALabelDictionaryIDMissing    => "IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is missing.",
165                    Self::MCALabelDictionaryIDInvalid    => "IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is not the required IAB label UL.",
166                    Self::MainAudioMissing               => "Segment has IABSequence but no MainAudioSequence (§6.2).",
167                    Self::IABSequenceNoResources         => "IABSequence shall contain at least one Resource (§6.2).",
168                    Self::IABSequenceSourceEncodingInvalid => "IABSequence Resource.SourceEncoding does not reference an IABEssenceDescriptor (§6.2).",
169                }
170            }
171            fn default_severity(&self) -> Severity {
172                match self {
173                    Self::QuantizationBitsMissing
174                    | Self::ContainerFormatMissing
175                    | Self::AudioSamplingRateMissing
176                    | Self::SoundCompressionMissing => Severity::Warning,
177                    _ => Severity::Error,
178                }
179            }
180            fn category(&self) -> Category {
181                Category::Audio
182            }
183        }
184
185        impl $name {
186            pub const ALL: &'static [Self] = &[
187                Self::CodecForbidden,
188                Self::ElectrospatialFormulationForbidden,
189                Self::QuantizationBitsMissing,
190                Self::QuantizationBitsInvalid,
191                Self::ContainerFormatMissing,
192                Self::EssenceContainerInvalid,
193                Self::AudioSamplingRateMissing,
194                Self::AudioSamplingRateInvalid,
195                Self::SoundCompressionMissing,
196                Self::SoundCompressionInvalid,
197                Self::ChannelCountNotZero,
198                Self::SubDescriptorMissing,
199                Self::MCATagSymbolMissing,
200                Self::MCATagSymbolInvalid,
201                Self::MCATagNameMissing,
202                Self::MCATagNameInvalid,
203                Self::MCALabelDictionaryIDMissing,
204                Self::MCALabelDictionaryIDInvalid,
205                Self::MainAudioMissing,
206                Self::IABSequenceNoResources,
207                Self::IABSequenceSourceEncodingInvalid,
208            ];
209
210            /// Dispatch from the spec-agnostic [`IabCode`] to this
211            /// edition's static code string.  Used by the shared validator helpers.
212            pub fn for_code(r: IabCode) -> &'static str {
213                match r {
214                    IabCode::CodecForbidden                    => concat!($prefix, ":5.9/CodecForbidden"),
215                    IabCode::ElectrospatialFormulationForbidden => concat!($prefix, ":5.9/ElectrospatialFormulationForbidden"),
216                    IabCode::QuantizationBitsMissing           => concat!($prefix, ":5.9/QuantizationBitsMissing"),
217                    IabCode::QuantizationBitsInvalid           => concat!($prefix, ":5.9/QuantizationBitsInvalid"),
218                    IabCode::ContainerFormatMissing            => concat!($prefix, ":5.3/ContainerFormatMissing"),
219                    IabCode::EssenceContainerInvalid           => concat!($prefix, ":5.3/EssenceContainerInvalid"),
220                    IabCode::AudioSamplingRateMissing          => concat!($prefix, ":5.9/AudioSamplingRateMissing"),
221                    IabCode::AudioSamplingRateInvalid          => concat!($prefix, ":5.9/AudioSamplingRateInvalid"),
222                    IabCode::SoundCompressionMissing           => concat!($prefix, ":5.9/SoundCompressionMissing"),
223                    IabCode::SoundCompressionInvalid           => concat!($prefix, ":5.9/SoundCompressionInvalid"),
224                    IabCode::ChannelCountNotZero               => concat!($prefix, ":5.9/ChannelCountNotZero"),
225                    IabCode::SubDescriptorMissing              => concat!($prefix, ":5.9/SubDescriptorMissing"),
226                    IabCode::MCATagSymbolMissing               => concat!($prefix, ":5.9/MCATagSymbolMissing"),
227                    IabCode::MCATagSymbolInvalid               => concat!($prefix, ":5.9/MCATagSymbolInvalid"),
228                    IabCode::MCATagNameMissing                 => concat!($prefix, ":5.9/MCATagNameMissing"),
229                    IabCode::MCATagNameInvalid                 => concat!($prefix, ":5.9/MCATagNameInvalid"),
230                    IabCode::MCALabelDictionaryIDMissing       => concat!($prefix, ":5.9/MCALabelDictionaryIDMissing"),
231                    IabCode::MCALabelDictionaryIDInvalid       => concat!($prefix, ":5.9/MCALabelDictionaryIDInvalid"),
232                    IabCode::MainAudioMissing                  => concat!($prefix, ":6.2/MainAudioMissing"),
233                    IabCode::IABSequenceNoResources            => concat!($prefix, ":6.2/IABSequenceNoResources"),
234                    IabCode::IABSequenceSourceEncodingInvalid  => concat!($prefix, ":6.2/IABSequenceSourceEncodingInvalid"),
235                }
236            }
237        }
238
239        impl From<$name> for String {
240            fn from(c: $name) -> String {
241                c.code().to_string()
242            }
243        }
244    };
245}
246
247define_iab_enum!(St2067_201_2019, "ST2067-201:2019");
248define_iab_enum!(St2067_201_2021, "ST2067-201:2021");