imferno-core 3.0.0

SMPTE ST 2067 IMF parser and validator
Documentation
//! Typed validation-code catalogue for SMPTE ST 2067-202 (ISXD Plug-in).

use crate::diagnostics::codes::ValidationCode;
use crate::diagnostics::{Category, Severity};

// ─────────────────────────────────────────────────────────────────────────────
// Spec-agnostic reason codes
// ─────────────────────────────────────────────────────────────────────────────

/// Spec-agnostic reason codes for ISXD Plug-in validation.
///
/// Passed to each edition's `for_code` dispatch function to get the full
/// `&'static str` code without any runtime string building.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsxdCode {
    /// ISXDDataEssenceDescriptor: ContainerConstraintsSubDescriptor shall be present (§5).
    SubDescriptorMissing,
    /// ISXDDataEssenceDescriptor: NamespaceURI is absent (§5).
    NamespaceUriMissing,
    /// ISXDSequence shall contain at least one Resource (§6).
    ISXDSequenceNoResources,
    /// ISXDSequence Resource.SourceEncoding does not reference an ISXDDataEssenceDescriptor (§6).
    ISXDSequenceSourceEncodingInvalid,
    /// Resources in the same ISXDSequence reference descriptors with inconsistent NamespaceURI values (§6).
    NamespaceUriMismatch,
}

// ─────────────────────────────────────────────────────────────────────────────
// Edition-specific enum (generated by macro)
// ─────────────────────────────────────────────────────────────────────────────

macro_rules! define_isxd_enum {
    ($name:ident, $prefix:literal) => {
        /// ISXD Plug-in validation codes, edition
        #[doc = $prefix]
        #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
        pub enum $name {
            /// ISXDDataEssenceDescriptor: ContainerConstraintsSubDescriptor shall be present (§5).
            SubDescriptorMissing,
            /// ISXDDataEssenceDescriptor: NamespaceURI is absent (§5).
            NamespaceUriMissing,
            /// ISXDSequence shall contain at least one Resource (§6).
            ISXDSequenceNoResources,
            /// ISXDSequence Resource.SourceEncoding does not reference an ISXDDataEssenceDescriptor (§6).
            ISXDSequenceSourceEncodingInvalid,
            /// Resources in the same ISXDSequence reference descriptors with inconsistent NamespaceURI values (§6).
            NamespaceUriMismatch,
        }

        impl ValidationCode for $name {
            fn code(&self) -> &'static str {
                match self {
                    Self::SubDescriptorMissing              => concat!($prefix, ":5/SubDescriptorMissing"),
                    Self::NamespaceUriMissing               => concat!($prefix, ":5/NamespaceUriMissing"),
                    Self::ISXDSequenceNoResources           => concat!($prefix, ":6/ISXDSequenceNoResources"),
                    Self::ISXDSequenceSourceEncodingInvalid => concat!($prefix, ":6/ISXDSequenceSourceEncodingInvalid"),
                    Self::NamespaceUriMismatch              => concat!($prefix, ":6/NamespaceUriMismatch"),
                }
            }
            fn description(&self) -> &'static str {
                match self {
                    Self::SubDescriptorMissing =>
                        "ISXDDataEssenceDescriptor: ContainerConstraintsSubDescriptor shall be present.",
                    Self::NamespaceUriMissing =>
                        "ISXDDataEssenceDescriptor: NamespaceURI is absent.",
                    Self::ISXDSequenceNoResources =>
                        "ISXDSequence shall contain at least one Resource.",
                    Self::ISXDSequenceSourceEncodingInvalid =>
                        "ISXDSequence Resource.SourceEncoding does not reference an ISXDDataEssenceDescriptor.",
                    Self::NamespaceUriMismatch =>
                        "Resources in the same ISXDSequence reference descriptors with inconsistent NamespaceURI values.",
                }
            }
            fn default_severity(&self) -> Severity {
                match self {
                    Self::NamespaceUriMissing => Severity::Warning,
                    _ => Severity::Error,
                }
            }
            fn category(&self) -> Category {
                // ISXD carries dynamic data essence (sidecar metadata
                // streams). Not audio — the previous tagging was wrong.
                Category::Data
            }
            fn example(&self) -> Option<&'static str> {
                Some(match self {
                    Self::SubDescriptorMissing =>
                        "<ISXDDataEssenceDescriptor>…</ISXDDataEssenceDescriptor>  <!-- no ContainerConstraintsSubDescriptor inside -->",
                    Self::NamespaceUriMissing =>
                        "<ISXDDataEssenceDescriptor>  <!-- missing required <NamespaceURI>… --> </ISXDDataEssenceDescriptor>",
                    Self::ISXDSequenceNoResources =>
                        "<ISXDSequence><ResourceList/></ISXDSequence>",
                    Self::ISXDSequenceSourceEncodingInvalid =>
                        "<ISXDSequence>…<SourceEncoding>urn:uuid:…</SourceEncoding></ISXDSequence>  <!-- UUID doesn't resolve to an ISXDDataEssenceDescriptor -->",
                    Self::NamespaceUriMismatch =>
                        "Two Resources in the same ISXDSequence point at descriptors whose <NamespaceURI> differs.",
                })
            }
        }

        impl $name {
            pub const ALL: &'static [Self] = &[
                Self::SubDescriptorMissing,
                Self::NamespaceUriMissing,
                Self::ISXDSequenceNoResources,
                Self::ISXDSequenceSourceEncodingInvalid,
                Self::NamespaceUriMismatch,
            ];

            /// Dispatch from the spec-agnostic [`IsxdCode`] to this
            /// edition's static code string. Used by the shared validator helpers.
            pub fn for_code(r: IsxdCode) -> &'static str {
                match r {
                    IsxdCode::SubDescriptorMissing              => concat!($prefix, ":5/SubDescriptorMissing"),
                    IsxdCode::NamespaceUriMissing               => concat!($prefix, ":5/NamespaceUriMissing"),
                    IsxdCode::ISXDSequenceNoResources           => concat!($prefix, ":6/ISXDSequenceNoResources"),
                    IsxdCode::ISXDSequenceSourceEncodingInvalid => concat!($prefix, ":6/ISXDSequenceSourceEncodingInvalid"),
                    IsxdCode::NamespaceUriMismatch              => concat!($prefix, ":6/NamespaceUriMismatch"),
                }
            }
        }

        impl From<$name> for String {
            fn from(c: $name) -> String {
                c.code().to_string()
            }
        }
    };
}

define_isxd_enum!(St2067_202_2022, "ST2067-202:2022");