Skip to main content

imferno_core/validation/
codes.rs

1//! Typed validation-code catalogue for SMPTE ST 2067-21 (Application Profile #2E).
2
3use crate::diagnostics::codes::ValidationCode;
4use crate::diagnostics::{Category, Severity};
5
6macro_rules! impl_into_string {
7    ($t:ty) => {
8        impl From<$t> for String {
9            fn from(c: $t) -> String {
10                <$t as ValidationCode>::code(&c).to_string()
11            }
12        }
13    };
14}
15
16// ─────────────────────────────────────────────────────────────────────────────
17// ST 2067-21:2020
18// ─────────────────────────────────────────────────────────────────────────────
19
20/// Validation codes defined by SMPTE ST 2067-21:2020 (Application Profile #2E).
21#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
22pub enum St2067_21_2020 {
23    /// Application identifier in CPL ExtensionProperties does not match the expected App2E URI.
24    AppIdMismatch,
25}
26
27impl ValidationCode for St2067_21_2020 {
28    fn code(&self) -> &'static str {
29        match self {
30            Self::AppIdMismatch => "ST2067-21:2020:7.1/AppIdMismatch",
31        }
32    }
33    fn description(&self) -> &'static str {
34        match self {
35            Self::AppIdMismatch =>
36                "Application identifier in CPL ExtensionProperties does not match the expected App2E URI.",
37        }
38    }
39    fn default_severity(&self) -> Severity {
40        Severity::Warning
41    }
42    fn category(&self) -> Category {
43        Category::Metadata
44    }
45}
46
47impl St2067_21_2020 {
48    pub const ALL: &'static [Self] = &[Self::AppIdMismatch];
49}
50
51impl_into_string!(St2067_21_2020);
52
53// ─────────────────────────────────────────────────────────────────────────────
54// ST 2067-21:2023
55// ─────────────────────────────────────────────────────────────────────────────
56
57/// Validation codes defined by SMPTE ST 2067-21:2023 (Application Profile #2E, UHD/HDR).
58///
59/// Variant naming notes:
60/// - Variants ending in `Unknown` check for unrecognized UL values (wrong value).
61/// - Variants starting with `Required` check for missing mandatory fields.
62/// - Plain variants (e.g. `ColorPrimaries`) check field presence at §6.2.1.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
64pub enum St2067_21_2023 {
65    // ── §5.2 Frame rates and resolutions ─────────────────────────────────────
66    /// Frame rate is not in the permitted set for App2E.
67    FrameRate,
68    /// Image resolution is not in the permitted set for App2E.
69    Resolution,
70
71    // ── §5.3 Language tags ────────────────────────────────────────────────────
72    /// Locale language tag is empty.
73    EmptyLanguageTag,
74    /// Locale language tag is not a valid BCP-47 subtag.
75    MalformedLanguageTag,
76    /// Region subtag in a language tag is not valid.
77    RegionCode,
78
79    // ── §6.2 Color system ────────────────────────────────────────────────────
80    /// Color system designator is not in the permitted set.
81    ColorSystem,
82
83    // ── §6.2 Required picture-descriptor fields ───────────────────────────────
84    /// RGBA/CDCI descriptor is missing the required `StoredWidth` field.
85    RequiredStoredWidth,
86    /// RGBA/CDCI descriptor is missing the required `StoredHeight` field.
87    RequiredStoredHeight,
88    /// RGBA/CDCI descriptor is missing the required `SampleRate` field.
89    RequiredSampleRate,
90    /// RGBA/CDCI descriptor is missing the required `FrameLayout` field.
91    RequiredFrameLayout,
92    /// RGBA/CDCI descriptor is missing the required `ColorPrimaries` field.
93    RequiredColorPrimaries,
94    /// RGBA/CDCI descriptor is missing the required `TransferCharacteristic` field.
95    RequiredTransferCharacteristic,
96    /// RGBA/CDCI descriptor is missing the required `PictureCompression` field.
97    RequiredPictureCompression,
98    /// CDCI descriptor is missing the required `ComponentDepth` field.
99    RequiredComponentDepth,
100
101    // ── §6.5 Required audio-descriptor fields ─────────────────────────────────
102    /// WavePCM descriptor is missing the required `ChannelCount` field.
103    RequiredChannelCount,
104    /// WavePCM descriptor is missing the required `QuantizationBits` field.
105    RequiredQuantizationBits,
106
107    // ── §6.2.1 Picture descriptor constraints ────────────────────────────────
108    /// Alpha transparency mode is not permitted in App2E.
109    AlphaTransparency,
110    /// `CodingEquations` field is absent from the picture descriptor (§6.2.1 Table 8).
111    CodingEquations,
112    /// `ColorPrimaries` field is absent from the picture descriptor (§6.2.1 Table 8).
113    ColorPrimaries,
114    /// `FieldDominance` value is not permitted for the declared `FrameLayout`.
115    FieldDominance,
116    /// `FrameLayout` value is not in the permitted set for App2E.
117    FrameLayout,
118    /// `FrameLayout` declares interlaced content, which is not permitted in App2E.
119    FrameLayoutInterlaced,
120    /// `ImageAlignmentOffset` value is not zero as required.
121    ImageAlignmentOffset,
122    /// `ImageEndOffset` value is not zero as required.
123    ImageEndOffset,
124    /// `ImageStartOffset` value is not zero as required.
125    ImageStartOffset,
126    /// `SampledHeight` does not equal `StoredHeight` as required.
127    SampledHeight,
128    /// `SampledWidth` does not equal `StoredWidth` as required.
129    SampledWidth,
130    /// `SampledXOffset` is not zero as required.
131    SampledXOffset,
132    /// `SampledYOffset` is not zero as required.
133    SampledYOffset,
134    /// `StoredF2Offset` is not zero as required for interlaced content.
135    StoredF2Offset,
136    /// `TransferCharacteristic` field is absent from the picture descriptor (§6.2.1 Table 8).
137    TransferCharacteristic,
138
139    // ── §6.2.2 Transfer characteristic value ────────────────────────────────
140    /// `TransferCharacteristic` UL is present but not a recognized value (§6.2.2).
141    TransferCharacteristicUnknown,
142
143    // ── §6.2.3 Coding equations value ────────────────────────────────────────
144    /// `CodingEquations` UL is present but not a recognized value (§6.2.3).
145    CodingEquationsUnknown,
146
147    // ── §6.2.4 Color primaries value ─────────────────────────────────────────
148    /// `ColorPrimaries` UL is present but not a recognized value (§6.2.4).
149    ColorPrimariesUnknown,
150
151    // ── §6.2.5 JPEG 2000 requirement ─────────────────────────────────────────
152    /// Video essence is not JPEG 2000 encoded as required by App2E.
153    J2KRequired,
154
155    // ── §6.3 RGBA descriptor ─────────────────────────────────────────────────
156    /// `AlphaMaxRef` value is not permitted.
157    AlphaMaxRef,
158    /// `AlphaMinRef` value is not permitted.
159    AlphaMinRef,
160    /// `ComponentMaxRef` value is not in the permitted range.
161    ComponentMaxRef,
162    /// `ComponentMinRef` value is not in the permitted range.
163    ComponentMinRef,
164    /// `Palette` is present; palette images are not permitted in App2E.
165    Palette,
166    /// `PaletteLayout` is present; palette layout is not permitted in App2E.
167    PaletteLayout,
168    /// `ScanningDirection` value is not in the permitted set.
169    ScanningDirection,
170
171    // ── §6.3.2 Component reference values ────────────────────────────────────
172    /// Component max/min reference values are inconsistent with bit depth.
173    ComponentRefValues,
174
175    // ── §6.4 Bit depth and chroma ────────────────────────────────────────────
176    /// `AlphaSampleDepth` value is not permitted.
177    AlphaSampleDepth,
178    /// `ColorSiting` value is not in the permitted set.
179    ColorSiting,
180    /// `ComponentDepth` value is not in the permitted set (8/10/12/16).
181    ComponentDepth,
182    /// `HorizontalSubsampling` value is not in the permitted set.
183    HorizontalSubsampling,
184    /// `PaddingBits` value is not zero as required.
185    PaddingBits,
186    /// `ReversedByteOrder` flag is set; byte reversal is not permitted.
187    ReversedByteOrder,
188    /// `VerticalSubsampling` value is not in the permitted set.
189    VerticalSubsampling,
190
191    // ── §6.4.3 Luma range ────────────────────────────────────────────────────
192    /// `BlackRefLevel` value is inconsistent with bit depth.
193    BlackRefLevel,
194    /// `ColorRange` value is not in the permitted set.
195    ColorRange,
196    /// `WhiteRefLevel` value is inconsistent with bit depth.
197    WhiteRefLevel,
198
199    // ── §6.5 Audio ───────────────────────────────────────────────────────────
200    /// Audio sample rate is not 48 kHz as required.
201    AudioSampleRate,
202    /// `QuantizationBits` value is not in the permitted set (16/24).
203    QuantizationBits,
204
205    // ── §6.5.2 JPEG 2000 sub-descriptor ─────────────────────────────────────
206    /// J2K codestream coding style is not compliant.
207    CodingStyle,
208    /// JPEG 2000 codestream layout (J2C) does not meet App2E requirements.
209    J2CLayout,
210    /// JPEG 2000 extended capabilities are declared but not permitted.
211    J2KExtendedCapabilities,
212    /// JPEG2000SubDescriptor is absent or incomplete.
213    Jpeg2000SubDescriptor,
214
215    // ── §6.2.5 JPEG 2000 resolution profiles ────────────────────────────────
216    /// JPEG 2000 HT (ISO 15444-15) is not permitted by the App2E 2020 edition.
217    J2KHtNotAllowed,
218    /// JPEG 2000 IMF 4K Profile: stored resolution is outside the permitted range.
219    J2K4KResolution,
220    /// JPEG 2000 IMF 2K Profile: stored resolution is outside the permitted range.
221    J2K2KResolution,
222    /// JPEG 2000 Broadcast Contribution Profile: stored resolution is outside the permitted range.
223    J2KBcpResolution,
224
225    // ── §7.1 Application identification ──────────────────────────────────────
226    /// ApplicationIdentification is required for App2E compositions.
227    ApplicationIdentification,
228    /// ContentMaturityRating agency is empty.
229    ContentMaturityRatingAgency,
230    /// ContentMaturityRating agency is not a valid xs:anyURI.
231    ContentMaturityRatingAgencyUri,
232
233    // ── §7.2 Homogeneous image essence ───────────────────────────────────────
234    /// All image essence in a composition shall use the same color system.
235    HomogeneousImageEssence,
236
237    // ── §7.1 Application identification ──────────────────────────────────────
238    /// Application identifier in CPL ExtensionProperties does not match the App2E URI.
239    AppIdMismatch,
240
241    // ── §7.4 Segment duration ─────────────────────────────────────────────────
242    /// Segment duration is not an integer multiple of 5 edit units as required by App2E.
243    SegmentDurationMultiple,
244
245    // ── §7.5 HDR metadata ────────────────────────────────────────────────────
246    /// `MaxCLL` / `MaxFALL` HDR metadata is absent; recommended for HDR content.
247    MaxCLLMaxFALL,
248}
249
250impl ValidationCode for St2067_21_2023 {
251    fn code(&self) -> &'static str {
252        match self {
253            Self::FrameRate => "ST2067-21:2023:5.2/FrameRate",
254            Self::Resolution => "ST2067-21:2023:5.2/Resolution",
255            Self::EmptyLanguageTag => "ST2067-21:2023:5.3/EmptyLanguageTag",
256            Self::MalformedLanguageTag => "ST2067-21:2023:5.3/MalformedLanguageTag",
257            Self::RegionCode => "ST2067-21:2023:5.3/RegionCode",
258            Self::ColorSystem => "ST2067-21:2023:6.2/ColorSystem",
259            Self::RequiredStoredWidth => "ST2067-21:2023:6.2/Required-StoredWidth",
260            Self::RequiredStoredHeight => "ST2067-21:2023:6.2/Required-StoredHeight",
261            Self::RequiredSampleRate => "ST2067-21:2023:6.2/Required-SampleRate",
262            Self::RequiredFrameLayout => "ST2067-21:2023:6.2/Required-FrameLayout",
263            Self::RequiredColorPrimaries => "ST2067-21:2023:6.2/Required-ColorPrimaries",
264            Self::RequiredTransferCharacteristic => {
265                "ST2067-21:2023:6.2/Required-TransferCharacteristic"
266            }
267            Self::RequiredPictureCompression => "ST2067-21:2023:6.2/Required-PictureCompression",
268            Self::RequiredComponentDepth => "ST2067-21:2023:6.2/Required-ComponentDepth",
269            Self::RequiredChannelCount => "ST2067-21:2023:6.5/Required-ChannelCount",
270            Self::RequiredQuantizationBits => "ST2067-21:2023:6.5/Required-QuantizationBits",
271            Self::AlphaTransparency => "ST2067-21:2023:6.2.1/AlphaTransparency",
272            Self::CodingEquations => "ST2067-21:2023:6.2.1/CodingEquations",
273            Self::ColorPrimaries => "ST2067-21:2023:6.2.1/ColorPrimaries",
274            Self::FieldDominance => "ST2067-21:2023:6.2.1/FieldDominance",
275            Self::FrameLayout => "ST2067-21:2023:6.2.1/FrameLayout",
276            Self::FrameLayoutInterlaced => "ST2067-21:2023:6.2.1/FrameLayoutInterlaced",
277            Self::ImageAlignmentOffset => "ST2067-21:2023:6.2.1/ImageAlignmentOffset",
278            Self::ImageEndOffset => "ST2067-21:2023:6.2.1/ImageEndOffset",
279            Self::ImageStartOffset => "ST2067-21:2023:6.2.1/ImageStartOffset",
280            Self::SampledHeight => "ST2067-21:2023:6.2.1/SampledHeight",
281            Self::SampledWidth => "ST2067-21:2023:6.2.1/SampledWidth",
282            Self::SampledXOffset => "ST2067-21:2023:6.2.1/SampledXOffset",
283            Self::SampledYOffset => "ST2067-21:2023:6.2.1/SampledYOffset",
284            Self::StoredF2Offset => "ST2067-21:2023:6.2.1/StoredF2Offset",
285            Self::TransferCharacteristic => "ST2067-21:2023:6.2.1/TransferCharacteristic",
286            Self::TransferCharacteristicUnknown => "ST2067-21:2023:6.2.2/TransferCharacteristic",
287            Self::CodingEquationsUnknown => "ST2067-21:2023:6.2.3/CodingEquations",
288            Self::ColorPrimariesUnknown => "ST2067-21:2023:6.2.4/ColorPrimaries",
289            Self::J2KRequired => "ST2067-21:2023:6.2.5/J2KRequired",
290            Self::AlphaMaxRef => "ST2067-21:2023:6.3/AlphaMaxRef",
291            Self::AlphaMinRef => "ST2067-21:2023:6.3/AlphaMinRef",
292            Self::ComponentMaxRef => "ST2067-21:2023:6.3/ComponentMaxRef",
293            Self::ComponentMinRef => "ST2067-21:2023:6.3/ComponentMinRef",
294            Self::Palette => "ST2067-21:2023:6.3/Palette",
295            Self::PaletteLayout => "ST2067-21:2023:6.3/PaletteLayout",
296            Self::ScanningDirection => "ST2067-21:2023:6.3/ScanningDirection",
297            Self::ComponentRefValues => "ST2067-21:2023:6.3.2/ComponentRefValues",
298            Self::AlphaSampleDepth => "ST2067-21:2023:6.4/AlphaSampleDepth",
299            Self::ColorSiting => "ST2067-21:2023:6.4/ColorSiting",
300            Self::ComponentDepth => "ST2067-21:2023:6.4/ComponentDepth",
301            Self::HorizontalSubsampling => "ST2067-21:2023:6.4/HorizontalSubsampling",
302            Self::PaddingBits => "ST2067-21:2023:6.4/PaddingBits",
303            Self::ReversedByteOrder => "ST2067-21:2023:6.4/ReversedByteOrder",
304            Self::VerticalSubsampling => "ST2067-21:2023:6.4/VerticalSubsampling",
305            Self::BlackRefLevel => "ST2067-21:2023:6.4.3/BlackRefLevel",
306            Self::ColorRange => "ST2067-21:2023:6.4.3/ColorRange",
307            Self::WhiteRefLevel => "ST2067-21:2023:6.4.3/WhiteRefLevel",
308            Self::AudioSampleRate => "ST2067-21:2023:6.5/AudioSampleRate",
309            Self::QuantizationBits => "ST2067-21:2023:6.5/QuantizationBits",
310            Self::CodingStyle => "ST2067-21:2023:6.5.2/CodingStyle",
311            Self::J2CLayout => "ST2067-21:2023:6.5.2/J2CLayout",
312            Self::J2KExtendedCapabilities => "ST2067-21:2023:6.5.2/J2KExtendedCapabilities",
313            Self::Jpeg2000SubDescriptor => "ST2067-21:2023:6.5.2/JPEG2000SubDescriptor",
314            Self::J2KHtNotAllowed => "ST2067-21:2023:6.2.5/J2K-HT-Not-Allowed",
315            Self::J2K4KResolution => "ST2067-21:2023:6.2.5/J2K-4K-Resolution",
316            Self::J2K2KResolution => "ST2067-21:2023:6.2.5/J2K-2K-Resolution",
317            Self::J2KBcpResolution => "ST2067-21:2023:6.2.5/J2K-BCP-Resolution",
318            Self::ApplicationIdentification => "ST2067-21:2023:7.1/ApplicationIdentification",
319            Self::ContentMaturityRatingAgency => "ST2067-21:2023:7.1/ContentMaturityRating-Agency",
320            Self::ContentMaturityRatingAgencyUri => {
321                "ST2067-21:2023:7.1/ContentMaturityRating-Agency-URI"
322            }
323            Self::HomogeneousImageEssence => "ST2067-21:2023:7.2/HomogeneousImageEssence",
324            Self::AppIdMismatch => "ST2067-21:2023:7.1/AppIdMismatch",
325            Self::SegmentDurationMultiple => "ST2067-21:2023:7.4/SegmentDurationMultiple",
326            Self::MaxCLLMaxFALL => "ST2067-21:2023:7.5/MaxCLLMaxFALL",
327        }
328    }
329
330    fn description(&self) -> &'static str {
331        match self {
332            Self::FrameRate => "Frame rate is not in the permitted set for App2E.",
333            Self::Resolution => "Image resolution is not in the permitted set for App2E.",
334            Self::EmptyLanguageTag => "Locale language tag is empty.",
335            Self::MalformedLanguageTag => "Locale language tag is not a valid BCP-47 subtag.",
336            Self::RegionCode => "Region subtag in a language tag is not valid.",
337            Self::ColorSystem => "Color system designator is not in the permitted set.",
338            Self::RequiredStoredWidth => "RGBA/CDCI descriptor is missing the required StoredWidth field.",
339            Self::RequiredStoredHeight => "RGBA/CDCI descriptor is missing the required StoredHeight field.",
340            Self::RequiredSampleRate => "RGBA/CDCI descriptor is missing the required SampleRate field.",
341            Self::RequiredFrameLayout => "RGBA/CDCI descriptor is missing the required FrameLayout field.",
342            Self::RequiredColorPrimaries => "RGBA/CDCI descriptor is missing the required ColorPrimaries field.",
343            Self::RequiredTransferCharacteristic => "RGBA/CDCI descriptor is missing the required TransferCharacteristic field.",
344            Self::RequiredPictureCompression => "RGBA/CDCI descriptor is missing the required PictureCompression field.",
345            Self::RequiredComponentDepth => "CDCI descriptor is missing the required ComponentDepth field.",
346            Self::RequiredChannelCount => "WavePCM descriptor is missing the required ChannelCount field.",
347            Self::RequiredQuantizationBits => "WavePCM descriptor is missing the required QuantizationBits field.",
348            Self::AlphaTransparency => "Alpha transparency mode is not permitted in App2E.",
349            Self::CodingEquations => "CodingEquations field is absent from the picture descriptor (Table 8).",
350            Self::ColorPrimaries => "ColorPrimaries field is absent from the picture descriptor (Table 8).",
351            Self::FieldDominance => "FieldDominance value is not permitted for the declared FrameLayout.",
352            Self::FrameLayout => "FrameLayout value is not in the permitted set for App2E.",
353            Self::FrameLayoutInterlaced => "FrameLayout declares interlaced content, which is not permitted in App2E.",
354            Self::ImageAlignmentOffset => "ImageAlignmentOffset must be zero.",
355            Self::ImageEndOffset => "ImageEndOffset must be zero.",
356            Self::ImageStartOffset => "ImageStartOffset must be zero.",
357            Self::SampledHeight => "SampledHeight must equal StoredHeight.",
358            Self::SampledWidth => "SampledWidth must equal StoredWidth.",
359            Self::SampledXOffset => "SampledXOffset must be zero.",
360            Self::SampledYOffset => "SampledYOffset must be zero.",
361            Self::StoredF2Offset => "StoredF2Offset must be zero.",
362            Self::TransferCharacteristic => "TransferCharacteristic field is absent from the picture descriptor (Table 8).",
363            Self::TransferCharacteristicUnknown => "TransferCharacteristic UL is present but not a recognized value.",
364            Self::CodingEquationsUnknown => "CodingEquations UL is present but not a recognized value.",
365            Self::ColorPrimariesUnknown => "ColorPrimaries UL is present but not a recognized value.",
366            Self::J2KRequired => "Video essence is not JPEG 2000 encoded as required by App2E.",
367            Self::AlphaMaxRef => "AlphaMaxRef value is not permitted.",
368            Self::AlphaMinRef => "AlphaMinRef value is not permitted.",
369            Self::ComponentMaxRef => "ComponentMaxRef value is not in the permitted range.",
370            Self::ComponentMinRef => "ComponentMinRef value is not in the permitted range.",
371            Self::Palette => "Palette is present; palette images are not permitted in App2E.",
372            Self::PaletteLayout => "PaletteLayout is present; palette layout is not permitted in App2E.",
373            Self::ScanningDirection => "ScanningDirection value is not in the permitted set.",
374            Self::ComponentRefValues => "Component max/min reference values are inconsistent with bit depth.",
375            Self::AlphaSampleDepth => "AlphaSampleDepth value is not permitted.",
376            Self::ColorSiting => "ColorSiting value is not in the permitted set.",
377            Self::ComponentDepth => "ComponentDepth value is not in the permitted set (8 / 10 / 12 / 16).",
378            Self::HorizontalSubsampling => "HorizontalSubsampling value is not in the permitted set.",
379            Self::PaddingBits => "PaddingBits must be zero.",
380            Self::ReversedByteOrder => "ReversedByteOrder flag is set; byte reversal is not permitted.",
381            Self::VerticalSubsampling => "VerticalSubsampling value is not in the permitted set.",
382            Self::BlackRefLevel => "BlackRefLevel value is inconsistent with ComponentDepth.",
383            Self::ColorRange => "ColorRange value is not in the permitted set.",
384            Self::WhiteRefLevel => "WhiteRefLevel value is inconsistent with ComponentDepth.",
385            Self::AudioSampleRate => "Audio sample rate must be 48 000 Hz.",
386            Self::QuantizationBits => "QuantizationBits must be 16 or 24.",
387            Self::CodingStyle => "JPEG 2000 codestream coding style is not compliant.",
388            Self::J2CLayout => "JPEG 2000 codestream layout does not meet App2E requirements.",
389            Self::J2KExtendedCapabilities => "JPEG 2000 extended capabilities are declared but not permitted.",
390            Self::Jpeg2000SubDescriptor => "JPEG2000SubDescriptor is absent or incomplete.",
391            Self::J2KHtNotAllowed                => "JPEG 2000 HT (ISO 15444-15) is not permitted by App2E 2020.",
392            Self::J2K4KResolution                => "JPEG 2000 IMF 4K Profile: stored resolution is outside the permitted range.",
393            Self::J2K2KResolution                => "JPEG 2000 IMF 2K Profile: stored resolution is outside the permitted range.",
394            Self::J2KBcpResolution               => "JPEG 2000 Broadcast Contribution Profile: stored resolution is outside the permitted range.",
395            Self::ApplicationIdentification      => "ApplicationIdentification is required for App2E compositions.",
396            Self::ContentMaturityRatingAgency    => "ContentMaturityRating Agency is empty.",
397            Self::ContentMaturityRatingAgencyUri => "ContentMaturityRating Agency is not a valid xs:anyURI.",
398            Self::HomogeneousImageEssence        => "All image essence in a composition shall use the same color system.",
399            Self::AppIdMismatch => "Application identifier in CPL ExtensionProperties does not match the expected App2E URI.",
400            Self::SegmentDurationMultiple => "Segment duration must be an integer multiple of 5 edit units.",
401            Self::MaxCLLMaxFALL => "MaxCLL / MaxFALL HDR metadata is absent; recommended for HDR content.",
402        }
403    }
404
405    fn default_severity(&self) -> Severity {
406        match self {
407            Self::MaxCLLMaxFALL => Severity::Info,
408            Self::AppIdMismatch | Self::Jpeg2000SubDescriptor => Severity::Warning,
409            Self::ContentMaturityRatingAgency | Self::ContentMaturityRatingAgencyUri => {
410                Severity::Error
411            }
412            _ => Severity::Error,
413        }
414    }
415
416    fn category(&self) -> Category {
417        match self {
418            Self::EmptyLanguageTag
419            | Self::MalformedLanguageTag
420            | Self::RegionCode
421            | Self::AppIdMismatch
422            | Self::ApplicationIdentification
423            | Self::ContentMaturityRatingAgency
424            | Self::ContentMaturityRatingAgencyUri => Category::Metadata,
425
426            Self::RequiredChannelCount
427            | Self::RequiredQuantizationBits
428            | Self::AudioSampleRate
429            | Self::QuantizationBits => Category::Audio,
430
431            Self::J2KRequired
432            | Self::CodingStyle
433            | Self::J2CLayout
434            | Self::J2KExtendedCapabilities
435            | Self::Jpeg2000SubDescriptor
436            | Self::J2KHtNotAllowed
437            | Self::J2K4KResolution
438            | Self::J2K2KResolution
439            | Self::J2KBcpResolution
440            | Self::RequiredStoredWidth
441            | Self::RequiredStoredHeight
442            | Self::RequiredSampleRate
443            | Self::RequiredFrameLayout
444            | Self::RequiredColorPrimaries
445            | Self::RequiredTransferCharacteristic
446            | Self::RequiredPictureCompression
447            | Self::RequiredComponentDepth => Category::Encoding,
448
449            Self::SegmentDurationMultiple => Category::Timing,
450
451            _ => Category::Video,
452        }
453    }
454}
455
456impl St2067_21_2023 {
457    pub const ALL: &'static [Self] = &[
458        Self::FrameRate,
459        Self::Resolution,
460        Self::EmptyLanguageTag,
461        Self::MalformedLanguageTag,
462        Self::RegionCode,
463        Self::ColorSystem,
464        Self::RequiredStoredWidth,
465        Self::RequiredStoredHeight,
466        Self::RequiredSampleRate,
467        Self::RequiredFrameLayout,
468        Self::RequiredColorPrimaries,
469        Self::RequiredTransferCharacteristic,
470        Self::RequiredPictureCompression,
471        Self::RequiredComponentDepth,
472        Self::RequiredChannelCount,
473        Self::RequiredQuantizationBits,
474        Self::AlphaTransparency,
475        Self::CodingEquations,
476        Self::ColorPrimaries,
477        Self::FieldDominance,
478        Self::FrameLayout,
479        Self::FrameLayoutInterlaced,
480        Self::ImageAlignmentOffset,
481        Self::ImageEndOffset,
482        Self::ImageStartOffset,
483        Self::SampledHeight,
484        Self::SampledWidth,
485        Self::SampledXOffset,
486        Self::SampledYOffset,
487        Self::StoredF2Offset,
488        Self::TransferCharacteristic,
489        Self::TransferCharacteristicUnknown,
490        Self::CodingEquationsUnknown,
491        Self::ColorPrimariesUnknown,
492        Self::J2KRequired,
493        Self::AlphaMaxRef,
494        Self::AlphaMinRef,
495        Self::ComponentMaxRef,
496        Self::ComponentMinRef,
497        Self::Palette,
498        Self::PaletteLayout,
499        Self::ScanningDirection,
500        Self::ComponentRefValues,
501        Self::AlphaSampleDepth,
502        Self::ColorSiting,
503        Self::ComponentDepth,
504        Self::HorizontalSubsampling,
505        Self::PaddingBits,
506        Self::ReversedByteOrder,
507        Self::VerticalSubsampling,
508        Self::BlackRefLevel,
509        Self::ColorRange,
510        Self::WhiteRefLevel,
511        Self::AudioSampleRate,
512        Self::QuantizationBits,
513        Self::CodingStyle,
514        Self::J2CLayout,
515        Self::J2KExtendedCapabilities,
516        Self::Jpeg2000SubDescriptor,
517        Self::J2KHtNotAllowed,
518        Self::J2K4KResolution,
519        Self::J2K2KResolution,
520        Self::J2KBcpResolution,
521        Self::ApplicationIdentification,
522        Self::ContentMaturityRatingAgency,
523        Self::ContentMaturityRatingAgencyUri,
524        Self::HomogeneousImageEssence,
525        Self::AppIdMismatch,
526        Self::SegmentDurationMultiple,
527        Self::MaxCLLMaxFALL,
528    ];
529}
530
531impl_into_string!(St2067_21_2023);
532
533// ─────────────────────────────────────────────────────────────────────────────
534// ST 2067-21:2025
535// ─────────────────────────────────────────────────────────────────────────────
536
537/// Validation codes defined by SMPTE ST 2067-21:2025 (App2E, timed-text additions).
538#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
539pub enum St2067_21_2025 {
540    /// Timed text track designated as Forced Narrative (FN) does not comply with §5.6.
541    FNTimedText,
542    /// Timed text track designated as HI-Caption (HIC) does not comply with §5.6.
543    HICTimedText,
544}
545
546impl ValidationCode for St2067_21_2025 {
547    fn code(&self) -> &'static str {
548        match self {
549            Self::FNTimedText => "ST2067-21:2025:5.6/FNTimedText",
550            Self::HICTimedText => "ST2067-21:2025:5.6/HICTimedText",
551        }
552    }
553    fn description(&self) -> &'static str {
554        match self {
555            Self::FNTimedText => {
556                "Timed text track designated as Forced Narrative (FN) does not comply with §5.6."
557            }
558            Self::HICTimedText => {
559                "Timed text track designated as HI-Caption (HIC) does not comply with §5.6."
560            }
561        }
562    }
563    fn default_severity(&self) -> Severity {
564        Severity::Error
565    }
566    fn category(&self) -> Category {
567        Category::Subtitle
568    }
569}
570
571impl St2067_21_2025 {
572    pub const ALL: &'static [Self] = &[Self::FNTimedText, Self::HICTimedText];
573}
574
575impl_into_string!(St2067_21_2025);