ddex_builder/versions/
ern_43.rs

1//! ERN 4.3 (Current) version specification and handling
2//!
3//! ERN 4.3 is the current recommended DDEX version with the most complete
4//! feature set and modern capabilities.
5
6use super::*;
7use crate::presets::DdexVersion;
8
9/// Get ERN 4.3 version specification
10pub fn get_version_spec() -> VersionSpec {
11    let mut element_mappings = IndexMap::new();
12    let mut namespace_prefixes = IndexMap::new();
13
14    // Current namespace mappings
15    namespace_prefixes.insert("ern".to_string(), "http://ddex.net/xml/ern/43".to_string());
16    namespace_prefixes.insert("avs".to_string(), "http://ddex.net/xml/avs/avs".to_string());
17    namespace_prefixes.insert(
18        "mead".to_string(),
19        "http://ddex.net/xml/mead/mead".to_string(),
20    );
21
22    // Complete element mappings for ERN 4.3
23    element_mappings.insert(
24        "NewReleaseMessage".to_string(),
25        "NewReleaseMessage".to_string(),
26    );
27    element_mappings.insert(
28        "UpdateReleaseMessage".to_string(),
29        "UpdateReleaseMessage".to_string(),
30    );
31    element_mappings.insert("MessageHeader".to_string(), "MessageHeader".to_string());
32    element_mappings.insert("MessageId".to_string(), "MessageId".to_string());
33    element_mappings.insert("MessageSender".to_string(), "MessageSender".to_string());
34    element_mappings.insert(
35        "MessageRecipient".to_string(),
36        "MessageRecipient".to_string(),
37    );
38    element_mappings.insert(
39        "MessageCreatedDateTime".to_string(),
40        "MessageCreatedDateTime".to_string(),
41    );
42    element_mappings.insert(
43        "MessageControlType".to_string(),
44        "MessageControlType".to_string(),
45    );
46
47    // Advanced resource mappings
48    element_mappings.insert("ResourceList".to_string(), "ResourceList".to_string());
49    element_mappings.insert("SoundRecording".to_string(), "SoundRecording".to_string());
50    element_mappings.insert("Video".to_string(), "Video".to_string());
51    element_mappings.insert("Image".to_string(), "Image".to_string());
52    element_mappings.insert(
53        "ResourceReference".to_string(),
54        "ResourceReference".to_string(),
55    );
56    element_mappings.insert("ResourceId".to_string(), "ResourceId".to_string());
57    element_mappings.insert("ReferenceTitle".to_string(), "ReferenceTitle".to_string());
58    element_mappings.insert("DisplayArtist".to_string(), "DisplayArtist".to_string());
59    element_mappings.insert("ISRC".to_string(), "ISRC".to_string());
60    element_mappings.insert("Duration".to_string(), "Duration".to_string());
61    element_mappings.insert(
62        "TechnicalResourceDetails".to_string(),
63        "TechnicalResourceDetails".to_string(),
64    );
65    element_mappings.insert("AudioCodecType".to_string(), "AudioCodecType".to_string());
66    element_mappings.insert("BitRate".to_string(), "BitRate".to_string());
67    element_mappings.insert("FileName".to_string(), "FileName".to_string());
68    element_mappings.insert("FileSize".to_string(), "FileSize".to_string());
69    element_mappings.insert("HashSum".to_string(), "HashSum".to_string());
70
71    // Advanced release mappings
72    element_mappings.insert("ReleaseList".to_string(), "ReleaseList".to_string());
73    element_mappings.insert("Release".to_string(), "Release".to_string());
74    element_mappings.insert(
75        "ReleaseReference".to_string(),
76        "ReleaseReference".to_string(),
77    );
78    element_mappings.insert("ReleaseId".to_string(), "ReleaseId".to_string());
79    element_mappings.insert("ReleaseType".to_string(), "ReleaseType".to_string());
80    element_mappings.insert("Title".to_string(), "Title".to_string());
81    element_mappings.insert("DisplayArtist".to_string(), "DisplayArtist".to_string());
82    element_mappings.insert("LabelName".to_string(), "LabelName".to_string());
83    element_mappings.insert("UPC".to_string(), "UPC".to_string());
84    element_mappings.insert("ReleaseDate".to_string(), "ReleaseDate".to_string());
85    element_mappings.insert(
86        "OriginalReleaseDate".to_string(),
87        "OriginalReleaseDate".to_string(),
88    );
89    element_mappings.insert("Genre".to_string(), "Genre".to_string());
90    element_mappings.insert("SubGenre".to_string(), "SubGenre".to_string());
91    element_mappings.insert("ResourceGroup".to_string(), "ResourceGroup".to_string());
92    element_mappings.insert(
93        "ReleaseResourceReference".to_string(),
94        "ReleaseResourceReference".to_string(),
95    );
96
97    // Advanced deal mappings
98    element_mappings.insert("DealList".to_string(), "DealList".to_string());
99    element_mappings.insert("ReleaseDeal".to_string(), "ReleaseDeal".to_string());
100    element_mappings.insert("DealReference".to_string(), "DealReference".to_string());
101    element_mappings.insert("DealTerms".to_string(), "DealTerms".to_string());
102    element_mappings.insert(
103        "CommercialModelType".to_string(),
104        "CommercialModelType".to_string(),
105    );
106    element_mappings.insert("Territory".to_string(), "Territory".to_string());
107    element_mappings.insert("TerritoryCode".to_string(), "TerritoryCode".to_string());
108    element_mappings.insert(
109        "ExcludedTerritoryCode".to_string(),
110        "ExcludedTerritoryCode".to_string(),
111    );
112    element_mappings.insert("ValidityPeriod".to_string(), "ValidityPeriod".to_string());
113    element_mappings.insert("StartDate".to_string(), "StartDate".to_string());
114    element_mappings.insert("EndDate".to_string(), "EndDate".to_string());
115    element_mappings.insert("Price".to_string(), "Price".to_string());
116    element_mappings.insert("PriceAmount".to_string(), "PriceAmount".to_string());
117    element_mappings.insert(
118        "PriceCurrencyCode".to_string(),
119        "PriceCurrencyCode".to_string(),
120    );
121    element_mappings.insert("PriceType".to_string(), "PriceType".to_string());
122
123    // New elements in ERN 4.3
124    element_mappings.insert("PartyId".to_string(), "PartyId".to_string());
125    element_mappings.insert("PartyReference".to_string(), "PartyReference".to_string());
126    element_mappings.insert("DetailedHashSum".to_string(), "DetailedHashSum".to_string());
127    element_mappings.insert("PreviewDetails".to_string(), "PreviewDetails".to_string());
128    element_mappings.insert("UsageType".to_string(), "UsageType".to_string());
129    element_mappings.insert("UseType".to_string(), "UseType".to_string());
130    element_mappings.insert(
131        "DistributionChannel".to_string(),
132        "DistributionChannel".to_string(),
133    );
134    element_mappings.insert(
135        "RightsController".to_string(),
136        "RightsController".to_string(),
137    );
138    element_mappings.insert("RemixType".to_string(), "RemixType".to_string());
139    element_mappings.insert("PLine".to_string(), "PLine".to_string());
140    element_mappings.insert("CLine".to_string(), "CLine".to_string());
141
142    VersionSpec {
143        version: DdexVersion::Ern43,
144        namespace: "http://ddex.net/xml/ern/43".to_string(),
145        schema_location: Some(
146            "http://ddex.net/xml/ern/43 http://ddex.net/xml/ern/43/release-notification.xsd"
147                .to_string(),
148        ),
149        message_schema_version_id: "ern/43".to_string(),
150        supported_message_types: vec![
151            "NewReleaseMessage".to_string(),
152            "UpdateReleaseMessage".to_string(),
153            "CatalogTransferMessage".to_string(),
154            "PurgeReleaseMessage".to_string(),
155            "ReleaseAvailabilityMessage".to_string(),
156            "SalesReportMessage".to_string(),
157        ],
158        element_mappings,
159        required_elements: vec![
160            "MessageId".to_string(),
161            "MessageSender".to_string(),
162            "MessageRecipient".to_string(),
163            "MessageCreatedDateTime".to_string(),
164            "ResourceList".to_string(),
165            "ReleaseList".to_string(),
166        ],
167        deprecated_elements: vec![
168            // No deprecated elements in current version
169        ],
170        new_elements: vec![
171            // Elements new in 4.3 compared to 4.2
172            "Video".to_string(),
173            "Image".to_string(),
174            "FileSize".to_string(),
175            "HashSum".to_string(),
176            "DetailedHashSum".to_string(),
177            "OriginalReleaseDate".to_string(),
178            "SubGenre".to_string(),
179            "ReleaseResourceReference".to_string(),
180            "ExcludedTerritoryCode".to_string(),
181            "PriceType".to_string(),
182            "UseType".to_string(),
183            "DistributionChannel".to_string(),
184            "RightsController".to_string(),
185            "RemixType".to_string(),
186            "PLine".to_string(),
187            "CLine".to_string(),
188        ],
189        namespace_prefixes,
190    }
191}
192
193/// ERN 4.3 specific constraints and validation rules
194pub struct Ern43Constraints {
195    /// Maximum allowed resources per release
196    pub max_resources_per_release: usize,
197    /// Supported image formats
198    pub supported_image_formats: Vec<String>,
199    /// Supported audio formats
200    pub supported_audio_formats: Vec<String>,
201    /// Supported video formats
202    pub supported_video_formats: Vec<String>,
203    /// Maximum deal complexity
204    pub max_deal_terms: usize,
205    /// Enhanced validation features
206    pub enhanced_validation: bool,
207    /// Support for complex rights management
208    pub rights_management: bool,
209}
210
211impl Default for Ern43Constraints {
212    fn default() -> Self {
213        Self {
214            max_resources_per_release: 1000,
215            supported_image_formats: vec![
216                "JPEG".to_string(),
217                "PNG".to_string(),
218                "GIF".to_string(),
219                "TIFF".to_string(),
220                "BMP".to_string(),
221                "WEBP".to_string(),
222            ],
223            supported_audio_formats: vec![
224                "MP3".to_string(),
225                "WAV".to_string(),
226                "FLAC".to_string(),
227                "AAC".to_string(),
228                "OGG".to_string(),
229                "M4A".to_string(),
230                "AIFF".to_string(),
231                "WMA".to_string(),
232            ],
233            supported_video_formats: vec![
234                "MP4".to_string(),
235                "MOV".to_string(),
236                "AVI".to_string(),
237                "WMV".to_string(),
238                "MKV".to_string(),
239                "WEBM".to_string(),
240            ],
241            max_deal_terms: 100,
242            enhanced_validation: true,
243            rights_management: true,
244        }
245    }
246}
247
248/// Get ERN 4.3 namespace mappings
249pub fn get_namespace_mappings() -> IndexMap<String, String> {
250    let mut mappings = IndexMap::new();
251
252    mappings.insert("ern".to_string(), "http://ddex.net/xml/ern/43".to_string());
253    mappings.insert("avs".to_string(), "http://ddex.net/xml/avs/avs".to_string());
254    mappings.insert("drm".to_string(), "http://ddex.net/xml/drm/drm".to_string());
255    mappings.insert("mv".to_string(), "http://ddex.net/xml/mv/mv".to_string());
256    mappings.insert(
257        "mead".to_string(),
258        "http://ddex.net/xml/mead/mead".to_string(),
259    );
260
261    mappings
262}
263
264/// Get ERN 4.3 specific XML template
265pub fn get_xml_template() -> &'static str {
266    r#"<?xml version="1.0" encoding="UTF-8"?>
267<NewReleaseMessage xmlns="http://ddex.net/xml/ern/43" 
268                  xmlns:avs="http://ddex.net/xml/avs/avs"
269                  xmlns:mead="http://ddex.net/xml/mead/mead"
270                  MessageSchemaVersionId="ern/43">
271    <MessageHeader>
272        <MessageId>{message_id}</MessageId>
273        <MessageSender>
274            <PartyName>{sender_name}</PartyName>
275            <PartyId>{sender_id}</PartyId>
276            <PartyReference>{sender_ref}</PartyReference>
277        </MessageSender>
278        <MessageRecipient>
279            <PartyName>{recipient_name}</PartyName>
280            <PartyId>{recipient_id}</PartyId>
281            <PartyReference>{recipient_ref}</PartyReference>
282        </MessageRecipient>
283        <MessageControlType>{control_type}</MessageControlType>
284        <MessageCreatedDateTime>{created_datetime}</MessageCreatedDateTime>
285    </MessageHeader>
286    
287    <ResourceList>
288        <!-- Advanced resources will be populated here -->
289    </ResourceList>
290    
291    <ReleaseList>
292        <!-- Advanced releases will be populated here -->
293    </ReleaseList>
294    
295    <DealList>
296        <!-- Advanced deals will be populated here -->
297    </DealList>
298</NewReleaseMessage>"#
299}
300
301/// ERN 4.3 specific element builders
302pub mod builders {
303
304    use crate::ast::Element;
305
306    /// Build ERN 4.3 advanced message header
307    pub fn build_advanced_message_header(
308        message_id: &str,
309        sender_name: &str,
310        sender_id: Option<&str>,
311        sender_ref: Option<&str>,
312        recipient_name: &str,
313        recipient_id: Option<&str>,
314        recipient_ref: Option<&str>,
315        control_type: Option<&str>,
316        created_datetime: &str,
317    ) -> Element {
318        let mut header = Element::new("MessageHeader");
319
320        // Message ID
321        let mut msg_id = Element::new("MessageId");
322        msg_id.add_text(message_id);
323        header.add_child(msg_id);
324
325        // Advanced Message Sender
326        let mut sender = Element::new("MessageSender");
327        let mut sender_party = Element::new("PartyName");
328        sender_party.add_text(sender_name);
329        sender.add_child(sender_party);
330
331        if let Some(sid) = sender_id {
332            let mut sender_id_elem = Element::new("PartyId");
333            sender_id_elem.add_text(sid);
334            sender.add_child(sender_id_elem);
335        }
336
337        if let Some(sref) = sender_ref {
338            let mut sender_ref_elem = Element::new("PartyReference");
339            sender_ref_elem.add_text(sref);
340            sender.add_child(sender_ref_elem);
341        }
342        header.add_child(sender);
343
344        // Advanced Message Recipient
345        let mut recipient = Element::new("MessageRecipient");
346        let mut recipient_party = Element::new("PartyName");
347        recipient_party.add_text(recipient_name);
348        recipient.add_child(recipient_party);
349
350        if let Some(rid) = recipient_id {
351            let mut recipient_id_elem = Element::new("PartyId");
352            recipient_id_elem.add_text(rid);
353            recipient.add_child(recipient_id_elem);
354        }
355
356        if let Some(rref) = recipient_ref {
357            let mut recipient_ref_elem = Element::new("PartyReference");
358            recipient_ref_elem.add_text(rref);
359            recipient.add_child(recipient_ref_elem);
360        }
361        header.add_child(recipient);
362
363        // Message Control Type
364        if let Some(control) = control_type {
365            let mut control_elem = Element::new("MessageControlType");
366            control_elem.add_text(control);
367            header.add_child(control_elem);
368        }
369
370        // Created DateTime
371        let mut created = Element::new("MessageCreatedDateTime");
372        created.add_text(created_datetime);
373        header.add_child(created);
374
375        header
376    }
377
378    /// Build ERN 4.3 advanced sound recording resource
379    pub fn build_advanced_sound_recording(
380        resource_ref: &str,
381        resource_id: &str,
382        title: &str,
383        artist: &str,
384        isrc: &str,
385        duration: &str,
386        file_name: Option<&str>,
387        file_size: Option<u64>,
388        codec: Option<&str>,
389        bit_rate: Option<u32>,
390        hash_sum: Option<&str>,
391        hash_algorithm: Option<&str>,
392    ) -> Element {
393        let mut sound_recording = Element::new("SoundRecording");
394
395        // Resource Reference
396        let mut res_ref = Element::new("ResourceReference");
397        res_ref.add_text(resource_ref);
398        sound_recording.add_child(res_ref);
399
400        // Type
401        let mut res_type = Element::new("Type");
402        res_type.add_text("SoundRecording");
403        sound_recording.add_child(res_type);
404
405        // Resource ID
406        let mut res_id = Element::new("ResourceId");
407        res_id.add_text(resource_id);
408        sound_recording.add_child(res_id);
409
410        // Reference Title
411        let mut ref_title = Element::new("ReferenceTitle");
412        ref_title.add_text(title);
413        sound_recording.add_child(ref_title);
414
415        // Display Artist
416        let mut display_artist = Element::new("DisplayArtist");
417        display_artist.add_text(artist);
418        sound_recording.add_child(display_artist);
419
420        // ISRC
421        let mut isrc_elem = Element::new("ISRC");
422        isrc_elem.add_text(isrc);
423        sound_recording.add_child(isrc_elem);
424
425        // Duration
426        let mut duration_elem = Element::new("Duration");
427        duration_elem.add_text(duration);
428        sound_recording.add_child(duration_elem);
429
430        // Advanced Technical Details
431        if file_name.is_some()
432            || file_size.is_some()
433            || codec.is_some()
434            || bit_rate.is_some()
435            || hash_sum.is_some()
436        {
437            let mut tech_details = Element::new("TechnicalResourceDetails");
438
439            if let Some(fname) = file_name {
440                let mut file_elem = Element::new("FileName");
441                file_elem.add_text(fname);
442                tech_details.add_child(file_elem);
443            }
444
445            if let Some(fsize) = file_size {
446                let mut size_elem = Element::new("FileSize");
447                size_elem.add_text(&fsize.to_string());
448                tech_details.add_child(size_elem);
449            }
450
451            if let Some(codec_type) = codec {
452                let mut codec_elem = Element::new("AudioCodecType");
453                codec_elem.add_text(codec_type);
454                tech_details.add_child(codec_elem);
455            }
456
457            if let Some(bitrate) = bit_rate {
458                let mut bitrate_elem = Element::new("BitRate");
459                bitrate_elem.add_text(&bitrate.to_string());
460                tech_details.add_child(bitrate_elem);
461            }
462
463            // Advanced hash sum support
464            if let Some(hash) = hash_sum {
465                let mut hash_elem = Element::new("HashSum");
466
467                if let Some(algorithm) = hash_algorithm {
468                    hash_elem
469                        .attributes
470                        .insert("Algorithm".to_string(), algorithm.to_string());
471                }
472
473                hash_elem.add_text(hash);
474                tech_details.add_child(hash_elem);
475            }
476
477            sound_recording.add_child(tech_details);
478        }
479
480        sound_recording
481    }
482
483    /// Build ERN 4.3 video resource
484    pub fn build_video_resource(
485        resource_ref: &str,
486        resource_id: &str,
487        title: &str,
488        artist: &str,
489        duration: &str,
490        file_name: Option<&str>,
491        file_size: Option<u64>,
492        codec: Option<&str>,
493        resolution: Option<&str>,
494        frame_rate: Option<f32>,
495    ) -> Element {
496        let mut video = Element::new("Video");
497
498        // Resource Reference
499        let mut res_ref = Element::new("ResourceReference");
500        res_ref.add_text(resource_ref);
501        video.add_child(res_ref);
502
503        // Type
504        let mut res_type = Element::new("Type");
505        res_type.add_text("Video");
506        video.add_child(res_type);
507
508        // Resource ID
509        let mut res_id = Element::new("ResourceId");
510        res_id.add_text(resource_id);
511        video.add_child(res_id);
512
513        // Reference Title
514        let mut ref_title = Element::new("ReferenceTitle");
515        ref_title.add_text(title);
516        video.add_child(ref_title);
517
518        // Display Artist
519        let mut display_artist = Element::new("DisplayArtist");
520        display_artist.add_text(artist);
521        video.add_child(display_artist);
522
523        // Duration
524        let mut duration_elem = Element::new("Duration");
525        duration_elem.add_text(duration);
526        video.add_child(duration_elem);
527
528        // Technical Details for Video
529        if file_name.is_some()
530            || file_size.is_some()
531            || codec.is_some()
532            || resolution.is_some()
533            || frame_rate.is_some()
534        {
535            let mut tech_details = Element::new("TechnicalResourceDetails");
536
537            if let Some(fname) = file_name {
538                let mut file_elem = Element::new("FileName");
539                file_elem.add_text(fname);
540                tech_details.add_child(file_elem);
541            }
542
543            if let Some(fsize) = file_size {
544                let mut size_elem = Element::new("FileSize");
545                size_elem.add_text(&fsize.to_string());
546                tech_details.add_child(size_elem);
547            }
548
549            if let Some(codec_type) = codec {
550                let mut codec_elem = Element::new("VideoCodecType");
551                codec_elem.add_text(codec_type);
552                tech_details.add_child(codec_elem);
553            }
554
555            if let Some(res) = resolution {
556                let mut res_elem = Element::new("VideoResolution");
557                res_elem.add_text(res);
558                tech_details.add_child(res_elem);
559            }
560
561            if let Some(fps) = frame_rate {
562                let mut fps_elem = Element::new("FrameRate");
563                fps_elem.add_text(&fps.to_string());
564                tech_details.add_child(fps_elem);
565            }
566
567            video.add_child(tech_details);
568        }
569
570        video
571    }
572}
573
574/// ERN 4.3 validation functions
575pub mod validation {
576
577    use once_cell::sync::Lazy;
578    use regex::Regex;
579
580    // Most advanced regex patterns for ERN 4.3
581    static ISRC_PATTERN_43: Lazy<Regex> =
582        Lazy::new(|| Regex::new(r"^[A-Z]{2}[A-Z0-9]{3}\d{7}$").unwrap());
583
584    static UPC_PATTERN_43: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\d{12}$").unwrap());
585
586    static EAN_PATTERN_43: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\d{13}$").unwrap());
587
588    static DURATION_PATTERN_43: Lazy<Regex> =
589        Lazy::new(|| Regex::new(r"^PT(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?$").unwrap());
590
591    static DATE_PATTERN_43: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap());
592
593    static DATETIME_PATTERN_43: Lazy<Regex> = Lazy::new(|| {
594        Regex::new(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$").unwrap()
595    });
596
597    static HASH_PATTERN_43: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[A-Fa-f0-9]+$").unwrap());
598
599    /// Most comprehensive validation for ERN 4.3
600    pub fn validate_isrc(isrc: &str) -> bool {
601        ISRC_PATTERN_43.is_match(isrc)
602    }
603
604    /// Enhanced UPC validation for ERN 4.3
605    pub fn validate_upc(upc: &str) -> bool {
606        UPC_PATTERN_43.is_match(upc)
607    }
608
609    /// EAN validation for ERN 4.3
610    pub fn validate_ean(ean: &str) -> bool {
611        EAN_PATTERN_43.is_match(ean)
612    }
613
614    /// Most flexible duration validation for ERN 4.3
615    pub fn validate_duration(duration: &str) -> bool {
616        DURATION_PATTERN_43.is_match(duration)
617    }
618
619    /// Enhanced date validation for ERN 4.3
620    pub fn validate_date(date: &str) -> bool {
621        DATE_PATTERN_43.is_match(date)
622    }
623
624    /// Enhanced datetime validation for ERN 4.3
625    pub fn validate_datetime(datetime: &str) -> bool {
626        DATETIME_PATTERN_43.is_match(datetime)
627    }
628
629    /// Hash sum validation for ERN 4.3
630    pub fn validate_hash_sum(hash: &str) -> bool {
631        HASH_PATTERN_43.is_match(hash) && hash.len() >= 8
632    }
633
634    /// Most comprehensive territory code validation for ERN 4.3
635    pub fn validate_territory_code(territory: &str) -> bool {
636        // Full ISO 3166 support + DDEX extensions
637        matches!(
638            territory,
639            // Major markets
640            "US" | "GB" | "DE" | "FR" | "JP" | "CA" | "AU" | "IT" | "ES" | "NL" |
641            "SE" | "NO" | "DK" | "FI" | "BR" | "MX" | "AR" | "IN" | "CN" | "KR" |
642            "RU" | "PL" | "CZ" | "AT" | "CH" | "BE" | "PT" | "GR" | "TR" | "ZA" |
643            // Extended regions
644            "Worldwide" | "WorldwideExceptUS" | "Europe" | "NorthAmerica" | "SouthAmerica" |
645            "Asia" | "Africa" | "Oceania"
646        )
647    }
648
649    /// Most comprehensive commercial model validation for ERN 4.3
650    pub fn validate_commercial_model(model: &str) -> bool {
651        matches!(
652            model,
653            "SubscriptionModel"
654                | "PurchaseModel"
655                | "AdSupportedModel"
656                | "FreeOfChargeModel"
657                | "StreamingModel"
658                | "DownloadModel"
659                | "ConditionalDownloadModel"
660                | "LimitedDownloadModel"
661                | "PayAsYouGoModel"
662                | "FreemiumModel"
663        )
664    }
665
666    /// Enhanced audio codec validation for ERN 4.3
667    pub fn validate_audio_codec(codec: &str) -> bool {
668        matches!(
669            codec,
670            "MP3"
671                | "AAC"
672                | "FLAC"
673                | "WAV"
674                | "OGG"
675                | "WMA"
676                | "MP4"
677                | "M4A"
678                | "AIFF"
679                | "DSD"
680                | "ALAC"
681                | "Opus"
682                | "AMR"
683                | "AC3"
684                | "DTS"
685        )
686    }
687
688    /// Video codec validation for ERN 4.3
689    pub fn validate_video_codec(codec: &str) -> bool {
690        matches!(
691            codec,
692            "H.264"
693                | "H.265"
694                | "VP8"
695                | "VP9"
696                | "AV1"
697                | "MPEG-2"
698                | "MPEG-4"
699                | "DivX"
700                | "XviD"
701                | "WMV"
702                | "QuickTime"
703                | "ProRes"
704        )
705    }
706
707    /// Enhanced usage type validation for ERN 4.3
708    pub fn validate_usage_type(usage: &str) -> bool {
709        matches!(
710            usage,
711            "Stream"
712                | "Download"
713                | "Preview"
714                | "ConditionalDownload"
715                | "DigitalPhonogramDelivery"
716                | "UserMadeClip"
717                | "Podcast"
718                | "RadioPlay"
719                | "BackgroundMusic"
720                | "Ringtone"
721                | "Synchronization"
722        )
723    }
724
725    /// Hash algorithm validation for ERN 4.3
726    pub fn validate_hash_algorithm(algorithm: &str) -> bool {
727        matches!(algorithm, "MD5" | "SHA-1" | "SHA-256" | "SHA-512" | "CRC32")
728    }
729
730    /// Get all validation errors for an ERN 4.3 message
731    pub fn validate_ern_43_message(xml_content: &str) -> Vec<String> {
732        let mut errors = Vec::new();
733
734        // Check required namespace
735        if !xml_content.contains("http://ddex.net/xml/ern/43") {
736            errors.push("Missing ERN 4.3 namespace".to_string());
737        }
738
739        // Check message schema version ID
740        if !xml_content.contains("ern/43") {
741            errors.push("Missing or incorrect MessageSchemaVersionId".to_string());
742        }
743
744        // Check for required elements
745        let required_elements = [
746            "MessageId",
747            "MessageSender",
748            "MessageRecipient",
749            "MessageCreatedDateTime",
750            "ResourceList",
751            "ReleaseList",
752        ];
753
754        for element in &required_elements {
755            if !xml_content.contains(&format!("<{}", element)) {
756                errors.push(format!("Missing required element: {}", element));
757            }
758        }
759
760        // Check for advanced features that should be used properly
761        if xml_content.contains("HashSum") {
762            if !xml_content.contains("Algorithm=") {
763                errors.push("HashSum should specify Algorithm attribute".to_string());
764            }
765        }
766
767        if xml_content.contains("TechnicalResourceDetails") {
768            if !xml_content.contains("FileName") && !xml_content.contains("FileSize") {
769                errors.push(
770                    "TechnicalResourceDetails should contain FileName or FileSize".to_string(),
771                );
772            }
773        }
774
775        errors
776    }
777}