Skip to main content

imferno_core/assetmap/
codes.rs

1//! Typed validation-code catalogue for SMPTE ST 2067-2.
2//!
3//! Two groups of codes live here:
4//!
5//! 1. **Package-level codes** (`St2067_2_2020`) — AssetMap / PKL / checksum checks
6//!    emitted by the package validator.
7//!
8//! 2. **Core Constraints CPL codes** (`St2067_2_2013_Core` / `St2067_2_2016_Core` /
9//!    `St2067_2_2020_Core`) — CPL structure rules (XSD, §6.1, §6.9, §6.10, §8, §10,
10//!    ST 377-4) shared across all three spec editions, emitted by the CoreConstraints
11//!    validators.  A `macro_rules!` generates the three enums from a
12//!    single source-of-truth variant list.  Each enum also exposes `for_code` so that
13//!    shared helper functions can produce the right `&'static str` without any runtime
14//!    string building.
15//!
16//! Also re-exports [`St429_9_2014`] from the volindex module.
17
18// Re-export St429_9_2014 from the volindex module for convenience.
19pub use crate::assetmap::volindex_codes::St429_9_2014;
20
21use crate::diagnostics::codes::ValidationCode;
22use crate::diagnostics::{Category, Severity};
23
24// ─────────────────────────────────────────────────────────────────────────────
25// ST 2067-2:2020 — Package-level codes
26// ─────────────────────────────────────────────────────────────────────────────
27
28/// Validation codes defined by SMPTE ST 2067-2:2020 for package-level checks
29/// (AssetMap, PKL, file integrity).
30#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
31pub enum St2067_2_2020 {
32    /// AssetMap document is invalid or cannot be parsed.
33    AssetMap,
34    /// The ASSETMAP.xml document is not well-formed XML (ST 2067-2:2020 §7).
35    AssetMapMalformedXml,
36    /// A Packing List document is not well-formed XML (ST 2067-2:2020 §9).
37    PklMalformedXml,
38    /// No CPL assets found in the AssetMap.
39    NoCpls,
40    /// Declared file size does not match the on-disk size.
41    SizeMismatch,
42    /// A referenced asset file is not present at the declared path.
43    FileNotFound,
44    /// File hash does not match the declared SHA-1/SHA-256 checksum.
45    ChecksumMismatch,
46    /// UUID referenced in the CPL does not resolve to a known asset.
47    UnresolvedUuid,
48    /// Two or more assets within the package share the same UUID.
49    DuplicateUuid,
50    /// An I/O error prevented the asset from being read.
51    IoError,
52    /// EssenceDescriptorList element is required per ST 2067-2:2020 §6.4.2.
53    EssenceDescriptorList,
54}
55
56impl ValidationCode for St2067_2_2020 {
57    fn code(&self) -> &'static str {
58        match self {
59            Self::AssetMap => "ST2067-2:2020:7/AssetMap",
60            Self::AssetMapMalformedXml => "ST2067-2:2020:7/MalformedXml",
61            Self::PklMalformedXml => "ST2067-2:2020:9/MalformedXml",
62            Self::NoCpls => "ST2067-2:2020:7/NoCpls",
63            Self::SizeMismatch => "ST2067-2:2020:8.3/SizeMismatch",
64            Self::FileNotFound => "ST2067-2:2020:8.3/FileNotFound",
65            Self::ChecksumMismatch => "ST2067-2:2020:8.3/ChecksumMismatch",
66            Self::UnresolvedUuid => "ST2067-2:2020:7/UnresolvedUuid",
67            Self::DuplicateUuid => "ST2067-2:2020:7/DuplicateUuid",
68            Self::IoError => "IMF:General/IoError",
69            Self::EssenceDescriptorList => "ST2067-2:2020:6.4.2/EssenceDescriptorList",
70        }
71    }
72    fn description(&self) -> &'static str {
73        match self {
74            Self::AssetMap => "AssetMap document is invalid or cannot be parsed.",
75            Self::AssetMapMalformedXml => "The ASSETMAP.xml document is not well-formed XML.",
76            Self::PklMalformedXml => "A Packing List document is not well-formed XML.",
77            Self::NoCpls => "No CPL assets found in the AssetMap.",
78            Self::SizeMismatch => "Declared file size does not match the on-disk size.",
79            Self::FileNotFound => "A referenced asset file is not present at the declared path.",
80            Self::ChecksumMismatch => {
81                "File hash does not match the declared SHA-1/SHA-256 checksum."
82            }
83            Self::UnresolvedUuid => "UUID referenced in the CPL does not resolve to a known asset.",
84            Self::DuplicateUuid => "Two or more assets within the package share the same UUID.",
85            Self::IoError => "An I/O error prevented the asset from being read.",
86            Self::EssenceDescriptorList => {
87                "EssenceDescriptorList element is required per ST 2067-2:2020 §6.4.2."
88            }
89        }
90    }
91    fn default_severity(&self) -> Severity {
92        match self {
93            Self::AssetMap | Self::NoCpls => Severity::Critical,
94            _ => Severity::Error,
95        }
96    }
97    fn category(&self) -> Category {
98        match self {
99            Self::AssetMap
100            | Self::AssetMapMalformedXml
101            | Self::PklMalformedXml
102            | Self::NoCpls
103            | Self::EssenceDescriptorList => Category::Structure,
104            Self::SizeMismatch | Self::FileNotFound | Self::IoError | Self::ChecksumMismatch => {
105                Category::Asset
106            }
107            Self::UnresolvedUuid | Self::DuplicateUuid => Category::Reference,
108        }
109    }
110}
111
112impl St2067_2_2020 {
113    pub const ALL: &'static [Self] = &[
114        Self::AssetMap,
115        Self::AssetMapMalformedXml,
116        Self::PklMalformedXml,
117        Self::NoCpls,
118        Self::SizeMismatch,
119        Self::FileNotFound,
120        Self::ChecksumMismatch,
121        Self::UnresolvedUuid,
122        Self::DuplicateUuid,
123        Self::IoError,
124        Self::EssenceDescriptorList,
125    ];
126}
127
128impl From<St2067_2_2020> for String {
129    fn from(c: St2067_2_2020) -> String {
130        c.code().to_string()
131    }
132}
133
134// ─────────────────────────────────────────────────────────────────────────────
135// Core Constraints CPL codes  (emitted by CoreConstraints validators in st2067-21)
136// ─────────────────────────────────────────────────────────────────────────────
137
138/// Spec-agnostic reason codes for Core Constraints CPL validation.
139///
140/// Passed to each edition's `for_code` dispatch function to get the full
141/// `&'static str` code without any runtime string building.
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum CoreConstraintsCode {
144    ResourceListEmpty,
145    ContentTitle,
146    TotalRunningTimeFormat,
147    SegmentList,
148    Segment,
149    EditRate,
150    IssueDate,
151    IssueDateFormat,
152    CompositionTimecodeDropFrame,
153    CompositionTimecodeRate,
154    CompositionTimecodeStartAddress,
155    CompositionTimecodeRateZero,
156    CompositionTimecodeStartAddressFormat,
157    CompositionTimecodeRateMismatch,
158    LocaleListNonEmpty,
159    UniqueSegmentId,
160    UniqueEssenceDescriptorId,
161    UniqueResourceId,
162    IntrinsicDuration,
163    EntryPoint,
164    SourceDuration,
165    ResourceDuration,
166    RepeatCount,
167    TrackFileId,
168    VirtualTrackContinuity,
169    VirtualTrackEditRate,
170    TimedTextSampleRate,
171    TimedTextEmptyLanguageTag,
172    TimedTextMalformedLanguageTag,
173    AudioSampleRate,
174    ChannelCount,
175    MCASubDescriptors,
176    SoundfieldGroup,
177    MCATagSymbol,
178    SoundfieldChannelCount,
179    DigitalSignature,
180    DanglingEssenceDescriptor,
181    EssenceDescriptorList,
182}
183
184macro_rules! define_core_constraints_enum {
185    ($name:ident, $prefix:literal) => {
186        /// Validation codes for Core Constraints CPL checks, edition
187        #[doc = $prefix]
188        #[allow(non_camel_case_types)]
189        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
190        pub enum $name {
191            /// A Sequence has an empty ResourceList.
192            ResourceListEmpty,
193            /// ContentTitle shall not be empty.
194            ContentTitle,
195            /// TotalRunningTime does not match format HH:MM:SS.
196            TotalRunningTimeFormat,
197            /// SegmentList shall contain at least one Segment.
198            SegmentList,
199            /// A Segment contains no sequences.
200            Segment,
201            /// CPL EditRate is required (XSD schema §88).
202            EditRate,
203            /// IssueDate shall not be empty.
204            IssueDate,
205            /// IssueDate is not a valid xs:dateTime format.
206            IssueDateFormat,
207            /// CompositionTimecode.TimecodeDropFrame is required when CompositionTimecode is present.
208            CompositionTimecodeDropFrame,
209            /// CompositionTimecode.TimecodeRate is required when CompositionTimecode is present.
210            CompositionTimecodeRate,
211            /// CompositionTimecode.TimecodeStartAddress is required when CompositionTimecode is present.
212            CompositionTimecodeStartAddress,
213            /// CompositionTimecode.TimecodeRate shall be a positive integer.
214            CompositionTimecodeRateZero,
215            /// TimecodeStartAddress does not match SMPTE timecode format HH:MM:SS:FF.
216            CompositionTimecodeStartAddressFormat,
217            /// CompositionTimecode.TimecodeRate does not match the CPL EditRate.
218            CompositionTimecodeRateMismatch,
219            /// LocaleList shall contain at least one Locale.
220            LocaleListNonEmpty,
221            /// Duplicate Segment Id within the CPL.
222            UniqueSegmentId,
223            /// Duplicate EssenceDescriptor Id within the CPL.
224            UniqueEssenceDescriptorId,
225            /// Duplicate Resource Id within the CPL.
226            UniqueResourceId,
227            /// IntrinsicDuration shall be greater than 0.
228            IntrinsicDuration,
229            /// EntryPoint shall be less than IntrinsicDuration.
230            EntryPoint,
231            /// EntryPoint + SourceDuration exceeds IntrinsicDuration.
232            SourceDuration,
233            /// SourceDuration shall be a positive integer.
234            ResourceDuration,
235            /// RepeatCount shall be a positive integer.
236            RepeatCount,
237            /// A non-marker resource is missing a TrackFileId.
238            TrackFileId,
239            /// A virtual track is missing from one or more segments.
240            VirtualTrackContinuity,
241            /// All resources in a virtual track shall have the same edit rate.
242            VirtualTrackEditRate,
243            /// DCTimedTextDescriptor SampleRate is missing.
244            TimedTextSampleRate,
245            /// Empty language tag in RFC5646LanguageTagList.
246            TimedTextEmptyLanguageTag,
247            /// Language tag does not start with an ASCII letter (RFC 5646 primary subtag).
248            TimedTextMalformedLanguageTag,
249            /// WAVEPCMDescriptor has no AudioSampleRate or SampleRate.
250            AudioSampleRate,
251            /// WAVEPCMDescriptor ChannelCount is zero or missing.
252            ChannelCount,
253            /// WAVEPCMDescriptor has no MCA SubDescriptors.
254            MCASubDescriptors,
255            /// WAVEPCMDescriptor SubDescriptors missing SoundfieldGroupLabelSubDescriptor.
256            SoundfieldGroup,
257            /// SoundfieldGroupLabelSubDescriptor is missing MCATagSymbol.
258            MCATagSymbol,
259            /// Soundfield group channel count is inconsistent with WAVEPCMDescriptor.ChannelCount.
260            SoundfieldChannelCount,
261            /// Digital signature validation (ST 2067-2 §8) is not currently performed.
262            DigitalSignature,
263            /// EssenceDescriptor present in EssenceDescriptorList but not referenced by any Resource.
264            DanglingEssenceDescriptor,
265            /// EssenceDescriptorList is required per ST 2067-2 §6.4.2.
266            EssenceDescriptorList,
267        }
268
269        impl ValidationCode for $name {
270            fn code(&self) -> &'static str {
271                match self {
272                    Self::ResourceListEmpty                    => concat!($prefix, ":XSD/ResourceList-Empty"),
273                    Self::ContentTitle                         => concat!($prefix, ":XSD/ContentTitle"),
274                    Self::TotalRunningTimeFormat               => concat!($prefix, ":XSD/TotalRunningTime-Format"),
275                    Self::SegmentList                          => concat!($prefix, ":XSD/SegmentList"),
276                    Self::Segment                              => concat!($prefix, ":XSD/Segment"),
277                    Self::EditRate                             => concat!($prefix, ":XSD-88/EditRate"),
278                    Self::IssueDate                            => concat!($prefix, ":XSD-66/IssueDate"),
279                    Self::IssueDateFormat                      => concat!($prefix, ":XSD-66/IssueDate-Format"),
280                    Self::CompositionTimecodeDropFrame         => concat!($prefix, ":XSD-121-127/CompositionTimecode-DropFrame"),
281                    Self::CompositionTimecodeRate              => concat!($prefix, ":XSD-121-127/CompositionTimecode-Rate"),
282                    Self::CompositionTimecodeStartAddress      => concat!($prefix, ":XSD-121-127/CompositionTimecode-StartAddress"),
283                    Self::CompositionTimecodeRateZero          => concat!($prefix, ":XSD-121-127/CompositionTimecode-Rate-Zero"),
284                    Self::CompositionTimecodeStartAddressFormat => concat!($prefix, ":XSD-121-127/CompositionTimecode-StartAddress-Format"),
285                    Self::CompositionTimecodeRateMismatch      => concat!($prefix, ":XSD-121-127/CompositionTimecode-RateMismatch"),
286                    Self::LocaleListNonEmpty                   => concat!($prefix, ":XSD/LocaleList-NonEmpty"),
287                    Self::UniqueSegmentId                      => concat!($prefix, ":6.1/UniqueSegmentId"),
288                    Self::UniqueEssenceDescriptorId            => concat!($prefix, ":6.1/UniqueEssenceDescriptorId"),
289                    Self::UniqueResourceId                     => concat!($prefix, ":6.1/UniqueResourceId"),
290                    Self::IntrinsicDuration                    => concat!($prefix, ":6.10/IntrinsicDuration"),
291                    Self::EntryPoint                           => concat!($prefix, ":6.10/EntryPoint"),
292                    Self::SourceDuration                       => concat!($prefix, ":6.10/SourceDuration"),
293                    Self::ResourceDuration                     => concat!($prefix, ":6.10/ResourceDuration"),
294                    Self::RepeatCount                          => concat!($prefix, ":6.10/RepeatCount"),
295                    Self::TrackFileId                          => concat!($prefix, ":6.10/TrackFileId"),
296                    Self::VirtualTrackContinuity               => concat!($prefix, ":6.9/VirtualTrackContinuity"),
297                    Self::VirtualTrackEditRate                 => concat!($prefix, ":6.9.3/VirtualTrackEditRate"),
298                    Self::TimedTextSampleRate                  => concat!($prefix, ":10/TimedText-SampleRate"),
299                    Self::TimedTextEmptyLanguageTag            => concat!($prefix, ":10/TimedText-EmptyLanguageTag"),
300                    Self::TimedTextMalformedLanguageTag        => concat!($prefix, ":10/TimedText-MalformedLanguageTag"),
301                    Self::AudioSampleRate                      => concat!($prefix, ":ST377-4/AudioSampleRate"),
302                    Self::ChannelCount                         => concat!($prefix, ":ST377-4/ChannelCount"),
303                    Self::MCASubDescriptors                    => concat!($prefix, ":ST377-4/MCASubDescriptors"),
304                    Self::SoundfieldGroup                      => concat!($prefix, ":ST377-4/SoundfieldGroup"),
305                    Self::MCATagSymbol                         => concat!($prefix, ":ST377-4/MCATagSymbol"),
306                    Self::SoundfieldChannelCount               => concat!($prefix, ":ST377-4/SoundfieldChannelCount"),
307                    Self::DigitalSignature                     => concat!($prefix, ":8/DigitalSignature"),
308                    Self::DanglingEssenceDescriptor            => concat!($prefix, ":6.4.2/DanglingEssenceDescriptor"),
309                    Self::EssenceDescriptorList                => concat!($prefix, ":6.4.2/EssenceDescriptorList"),
310                }
311            }
312            fn description(&self) -> &'static str {
313                match self {
314                    Self::ResourceListEmpty                    => "A Sequence has an empty ResourceList.",
315                    Self::ContentTitle                         => "ContentTitle shall not be empty.",
316                    Self::TotalRunningTimeFormat               => "TotalRunningTime does not match required format HH:MM:SS.",
317                    Self::SegmentList                          => "SegmentList shall contain at least one Segment.",
318                    Self::Segment                              => "A Segment contains no sequences.",
319                    Self::EditRate                             => "CPL EditRate is required (XSD schema §88).",
320                    Self::IssueDate                            => "IssueDate shall not be empty.",
321                    Self::IssueDateFormat                      => "IssueDate is not a valid xs:dateTime format.",
322                    Self::CompositionTimecodeDropFrame         => "CompositionTimecode.TimecodeDropFrame is required when CompositionTimecode is present.",
323                    Self::CompositionTimecodeRate              => "CompositionTimecode.TimecodeRate is required when CompositionTimecode is present.",
324                    Self::CompositionTimecodeStartAddress      => "CompositionTimecode.TimecodeStartAddress is required when CompositionTimecode is present.",
325                    Self::CompositionTimecodeRateZero          => "CompositionTimecode.TimecodeRate shall be a positive integer.",
326                    Self::CompositionTimecodeStartAddressFormat => "TimecodeStartAddress does not match SMPTE timecode format HH:MM:SS:FF.",
327                    Self::CompositionTimecodeRateMismatch      => "CompositionTimecode.TimecodeRate does not match the CPL EditRate.",
328                    Self::LocaleListNonEmpty                   => "LocaleList shall contain at least one Locale.",
329                    Self::UniqueSegmentId                      => "Duplicate Segment Id within the CPL.",
330                    Self::UniqueEssenceDescriptorId            => "Duplicate EssenceDescriptor Id within the CPL.",
331                    Self::UniqueResourceId                     => "Duplicate Resource Id within the CPL.",
332                    Self::IntrinsicDuration                    => "IntrinsicDuration shall be greater than 0.",
333                    Self::EntryPoint                           => "EntryPoint shall be less than IntrinsicDuration.",
334                    Self::SourceDuration                       => "EntryPoint + SourceDuration exceeds IntrinsicDuration.",
335                    Self::ResourceDuration                     => "SourceDuration shall be a positive integer.",
336                    Self::RepeatCount                          => "RepeatCount shall be a positive integer.",
337                    Self::TrackFileId                          => "A non-marker resource is missing a TrackFileId.",
338                    Self::VirtualTrackContinuity               => "A virtual track is missing from one or more segments.",
339                    Self::VirtualTrackEditRate                 => "All resources in a virtual track shall have the same edit rate.",
340                    Self::TimedTextSampleRate                  => "DCTimedTextDescriptor SampleRate is missing.",
341                    Self::TimedTextEmptyLanguageTag            => "Empty language tag in RFC5646LanguageTagList.",
342                    Self::TimedTextMalformedLanguageTag        => "Language tag does not start with an ASCII letter (RFC 5646 primary subtag).",
343                    Self::AudioSampleRate                      => "WAVEPCMDescriptor has no AudioSampleRate or SampleRate.",
344                    Self::ChannelCount                         => "WAVEPCMDescriptor ChannelCount is zero or missing.",
345                    Self::MCASubDescriptors                    => "WAVEPCMDescriptor has no MCA SubDescriptors.",
346                    Self::SoundfieldGroup                      => "WAVEPCMDescriptor SubDescriptors missing SoundfieldGroupLabelSubDescriptor.",
347                    Self::MCATagSymbol                         => "SoundfieldGroupLabelSubDescriptor is missing MCATagSymbol.",
348                    Self::SoundfieldChannelCount               => "Soundfield group channel count is inconsistent with WAVEPCMDescriptor.ChannelCount.",
349                    Self::DigitalSignature                     => "Digital signature validation (ST 2067-2 §8) is not currently performed.",
350                    Self::DanglingEssenceDescriptor            => "EssenceDescriptor present in EssenceDescriptorList but not referenced by any Resource.",
351                    Self::EssenceDescriptorList                => "EssenceDescriptorList is required per ST 2067-2 §6.4.2.",
352                }
353            }
354            fn default_severity(&self) -> Severity {
355                match self {
356                    Self::SegmentList => Severity::Critical,
357                    Self::IssueDateFormat
358                    | Self::CompositionTimecodeRateMismatch
359                    | Self::TimedTextSampleRate
360                    | Self::TimedTextEmptyLanguageTag
361                    | Self::TimedTextMalformedLanguageTag
362                    | Self::AudioSampleRate
363                    | Self::ChannelCount
364                    | Self::MCASubDescriptors
365                    | Self::SoundfieldGroup
366                    | Self::MCATagSymbol => Severity::Warning,
367                    Self::DigitalSignature => Severity::Info,
368                    _ => Severity::Error,
369                }
370            }
371            fn category(&self) -> Category {
372                match self {
373                    Self::ContentTitle
374                    | Self::IssueDate
375                    | Self::IssueDateFormat
376                    | Self::CompositionTimecodeRateMismatch => Category::Metadata,
377
378                    Self::CompositionTimecodeDropFrame
379                    | Self::CompositionTimecodeRate
380                    | Self::CompositionTimecodeStartAddress
381                    | Self::CompositionTimecodeRateZero
382                    | Self::CompositionTimecodeStartAddressFormat
383                    | Self::IntrinsicDuration
384                    | Self::EntryPoint
385                    | Self::SourceDuration
386                    | Self::ResourceDuration
387                    | Self::RepeatCount
388                    | Self::VirtualTrackEditRate => Category::Timing,
389
390                    Self::TimedTextSampleRate
391                    | Self::TimedTextEmptyLanguageTag
392                    | Self::TimedTextMalformedLanguageTag => Category::Subtitle,
393
394                    Self::AudioSampleRate
395                    | Self::ChannelCount
396                    | Self::MCASubDescriptors
397                    | Self::SoundfieldGroup
398                    | Self::MCATagSymbol
399                    | Self::SoundfieldChannelCount => Category::Audio,
400
401                    Self::DanglingEssenceDescriptor | Self::TrackFileId => Category::Reference,
402
403                    Self::DigitalSignature => Category::Security,
404
405                    _ => Category::Structure,
406                }
407            }
408        }
409
410        impl $name {
411            pub const ALL: &'static [Self] = &[
412                Self::ResourceListEmpty,
413                Self::ContentTitle,
414                Self::TotalRunningTimeFormat,
415                Self::SegmentList,
416                Self::Segment,
417                Self::EditRate,
418                Self::IssueDate,
419                Self::IssueDateFormat,
420                Self::CompositionTimecodeDropFrame,
421                Self::CompositionTimecodeRate,
422                Self::CompositionTimecodeStartAddress,
423                Self::CompositionTimecodeRateZero,
424                Self::CompositionTimecodeStartAddressFormat,
425                Self::CompositionTimecodeRateMismatch,
426                Self::LocaleListNonEmpty,
427                Self::UniqueSegmentId,
428                Self::UniqueEssenceDescriptorId,
429                Self::UniqueResourceId,
430                Self::IntrinsicDuration,
431                Self::EntryPoint,
432                Self::SourceDuration,
433                Self::ResourceDuration,
434                Self::RepeatCount,
435                Self::TrackFileId,
436                Self::VirtualTrackContinuity,
437                Self::VirtualTrackEditRate,
438                Self::TimedTextSampleRate,
439                Self::TimedTextEmptyLanguageTag,
440                Self::TimedTextMalformedLanguageTag,
441                Self::AudioSampleRate,
442                Self::ChannelCount,
443                Self::MCASubDescriptors,
444                Self::SoundfieldGroup,
445                Self::MCATagSymbol,
446                Self::SoundfieldChannelCount,
447                Self::DigitalSignature,
448                Self::DanglingEssenceDescriptor,
449                Self::EssenceDescriptorList,
450            ];
451
452            /// Dispatch from the spec-agnostic [`CoreConstraintsCode`] to this
453            /// edition's static code string.  Used by the shared validator helpers.
454            pub fn for_code(r: CoreConstraintsCode) -> &'static str {
455                match r {
456                    CoreConstraintsCode::ResourceListEmpty                    => concat!($prefix, ":XSD/ResourceList-Empty"),
457                    CoreConstraintsCode::ContentTitle                         => concat!($prefix, ":XSD/ContentTitle"),
458                    CoreConstraintsCode::TotalRunningTimeFormat               => concat!($prefix, ":XSD/TotalRunningTime-Format"),
459                    CoreConstraintsCode::SegmentList                          => concat!($prefix, ":XSD/SegmentList"),
460                    CoreConstraintsCode::Segment                              => concat!($prefix, ":XSD/Segment"),
461                    CoreConstraintsCode::EditRate                             => concat!($prefix, ":XSD-88/EditRate"),
462                    CoreConstraintsCode::IssueDate                            => concat!($prefix, ":XSD-66/IssueDate"),
463                    CoreConstraintsCode::IssueDateFormat                      => concat!($prefix, ":XSD-66/IssueDate-Format"),
464                    CoreConstraintsCode::CompositionTimecodeDropFrame         => concat!($prefix, ":XSD-121-127/CompositionTimecode-DropFrame"),
465                    CoreConstraintsCode::CompositionTimecodeRate              => concat!($prefix, ":XSD-121-127/CompositionTimecode-Rate"),
466                    CoreConstraintsCode::CompositionTimecodeStartAddress      => concat!($prefix, ":XSD-121-127/CompositionTimecode-StartAddress"),
467                    CoreConstraintsCode::CompositionTimecodeRateZero          => concat!($prefix, ":XSD-121-127/CompositionTimecode-Rate-Zero"),
468                    CoreConstraintsCode::CompositionTimecodeStartAddressFormat => concat!($prefix, ":XSD-121-127/CompositionTimecode-StartAddress-Format"),
469                    CoreConstraintsCode::CompositionTimecodeRateMismatch      => concat!($prefix, ":XSD-121-127/CompositionTimecode-RateMismatch"),
470                    CoreConstraintsCode::LocaleListNonEmpty                   => concat!($prefix, ":XSD/LocaleList-NonEmpty"),
471                    CoreConstraintsCode::UniqueSegmentId                      => concat!($prefix, ":6.1/UniqueSegmentId"),
472                    CoreConstraintsCode::UniqueEssenceDescriptorId            => concat!($prefix, ":6.1/UniqueEssenceDescriptorId"),
473                    CoreConstraintsCode::UniqueResourceId                     => concat!($prefix, ":6.1/UniqueResourceId"),
474                    CoreConstraintsCode::IntrinsicDuration                    => concat!($prefix, ":6.10/IntrinsicDuration"),
475                    CoreConstraintsCode::EntryPoint                           => concat!($prefix, ":6.10/EntryPoint"),
476                    CoreConstraintsCode::SourceDuration                       => concat!($prefix, ":6.10/SourceDuration"),
477                    CoreConstraintsCode::ResourceDuration                     => concat!($prefix, ":6.10/ResourceDuration"),
478                    CoreConstraintsCode::RepeatCount                          => concat!($prefix, ":6.10/RepeatCount"),
479                    CoreConstraintsCode::TrackFileId                          => concat!($prefix, ":6.10/TrackFileId"),
480                    CoreConstraintsCode::VirtualTrackContinuity               => concat!($prefix, ":6.9/VirtualTrackContinuity"),
481                    CoreConstraintsCode::VirtualTrackEditRate                 => concat!($prefix, ":6.9.3/VirtualTrackEditRate"),
482                    CoreConstraintsCode::TimedTextSampleRate                  => concat!($prefix, ":10/TimedText-SampleRate"),
483                    CoreConstraintsCode::TimedTextEmptyLanguageTag            => concat!($prefix, ":10/TimedText-EmptyLanguageTag"),
484                    CoreConstraintsCode::TimedTextMalformedLanguageTag        => concat!($prefix, ":10/TimedText-MalformedLanguageTag"),
485                    CoreConstraintsCode::AudioSampleRate                      => concat!($prefix, ":ST377-4/AudioSampleRate"),
486                    CoreConstraintsCode::ChannelCount                         => concat!($prefix, ":ST377-4/ChannelCount"),
487                    CoreConstraintsCode::MCASubDescriptors                    => concat!($prefix, ":ST377-4/MCASubDescriptors"),
488                    CoreConstraintsCode::SoundfieldGroup                      => concat!($prefix, ":ST377-4/SoundfieldGroup"),
489                    CoreConstraintsCode::MCATagSymbol                         => concat!($prefix, ":ST377-4/MCATagSymbol"),
490                    CoreConstraintsCode::SoundfieldChannelCount               => concat!($prefix, ":ST377-4/SoundfieldChannelCount"),
491                    CoreConstraintsCode::DigitalSignature                     => concat!($prefix, ":8/DigitalSignature"),
492                    CoreConstraintsCode::DanglingEssenceDescriptor            => concat!($prefix, ":6.4.2/DanglingEssenceDescriptor"),
493                    CoreConstraintsCode::EssenceDescriptorList                => concat!($prefix, ":6.4.2/EssenceDescriptorList"),
494                }
495            }
496        }
497
498        impl From<$name> for String {
499            fn from(c: $name) -> String {
500                c.code().to_string()
501            }
502        }
503    };
504}
505
506define_core_constraints_enum!(St2067_2_2013_Core, "ST2067-2:2013");
507define_core_constraints_enum!(St2067_2_2016_Core, "ST2067-2:2016");
508define_core_constraints_enum!(St2067_2_2020_Core, "ST2067-2:2020");