ddex_builder/canonical/
rules.rs

1//! # Canonical XML Rules for DB-C14N/1.0
2//! 
3//! This module defines the canonical XML transformation rules for DDEX Builder,
4//! including comprehensive namespace prefix locking, element ordering, and
5//! support for all DDEX standards (ERN, AVS, MEAD, PIE, etc.).
6
7use indexmap::IndexMap;
8use ddex_core::namespace::NamespaceRegistry;
9use std::collections::HashSet;
10
11/// Fixed XML declaration for all canonical XML
12pub const XML_DECLARATION: &str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
13
14/// Comprehensive namespace prefix lock tables for all ERN versions
15pub struct NamespacePrefixLock {
16    /// Registry for namespace management
17    registry: NamespaceRegistry,
18    /// Locked prefixes for specific versions
19    version_locks: IndexMap<String, IndexMap<String, String>>, // version -> (uri -> prefix)
20    /// Reserved prefixes that cannot be used
21    reserved_prefixes: HashSet<String>,
22}
23
24impl NamespacePrefixLock {
25    /// Create new prefix lock manager
26    pub fn new() -> Self {
27        let mut lock = Self {
28            registry: NamespaceRegistry::new(),
29            version_locks: IndexMap::new(),
30            reserved_prefixes: HashSet::new(),
31        };
32        
33        lock.initialize_version_locks();
34        lock
35    }
36    
37    /// Initialize prefix locks for all ERN versions
38    fn initialize_version_locks(&mut self) {
39        // ERN 3.8.2 prefix locks
40        let mut ern_382_prefixes = IndexMap::new();
41        ern_382_prefixes.insert("http://ddex.net/xml/ern/382".to_string(), "ern".to_string());
42        ern_382_prefixes.insert("http://ddex.net/xml/avs".to_string(), "avs".to_string());
43        ern_382_prefixes.insert("http://ddex.net/xml/avs/avs".to_string(), "avs".to_string());
44        ern_382_prefixes.insert("http://www.w3.org/2001/XMLSchema-instance".to_string(), "xsi".to_string());
45        ern_382_prefixes.insert("http://www.w3.org/2001/XMLSchema".to_string(), "xs".to_string());
46        ern_382_prefixes.insert("http://ddex.net/xml/gc".to_string(), "gc".to_string());
47        self.version_locks.insert("3.8.2".to_string(), ern_382_prefixes.clone());
48        self.version_locks.insert("382".to_string(), ern_382_prefixes);
49
50        // ERN 4.2 prefix locks
51        let mut ern_42_prefixes = IndexMap::new();
52        ern_42_prefixes.insert("http://ddex.net/xml/ern/42".to_string(), "ern".to_string());
53        ern_42_prefixes.insert("http://ddex.net/xml/avs".to_string(), "avs".to_string());
54        ern_42_prefixes.insert("http://ddex.net/xml/avs/avs".to_string(), "avs".to_string());
55        ern_42_prefixes.insert("http://www.w3.org/2001/XMLSchema-instance".to_string(), "xsi".to_string());
56        ern_42_prefixes.insert("http://www.w3.org/2001/XMLSchema".to_string(), "xs".to_string());
57        ern_42_prefixes.insert("http://ddex.net/xml/gc".to_string(), "gc".to_string());
58        // Additional 4.2 namespaces
59        ern_42_prefixes.insert("http://ddex.net/xml/mead/mead".to_string(), "mead".to_string());
60        ern_42_prefixes.insert("http://ddex.net/xml/pie/pie".to_string(), "pie".to_string());
61        self.version_locks.insert("4.2".to_string(), ern_42_prefixes.clone());
62        self.version_locks.insert("42".to_string(), ern_42_prefixes);
63
64        // ERN 4.3 prefix locks
65        let mut ern_43_prefixes = IndexMap::new();
66        ern_43_prefixes.insert("http://ddex.net/xml/ern/43".to_string(), "ern".to_string());
67        ern_43_prefixes.insert("http://ddex.net/xml/avs".to_string(), "avs".to_string());
68        ern_43_prefixes.insert("http://ddex.net/xml/avs/avs".to_string(), "avs".to_string());
69        ern_43_prefixes.insert("http://www.w3.org/2001/XMLSchema-instance".to_string(), "xsi".to_string());
70        ern_43_prefixes.insert("http://www.w3.org/2001/XMLSchema".to_string(), "xs".to_string());
71        ern_43_prefixes.insert("http://ddex.net/xml/gc".to_string(), "gc".to_string());
72        // Additional 4.3 namespaces
73        ern_43_prefixes.insert("http://ddex.net/xml/mead/mead".to_string(), "mead".to_string());
74        ern_43_prefixes.insert("http://ddex.net/xml/pie/pie".to_string(), "pie".to_string());
75        ern_43_prefixes.insert("http://ddex.net/xml/rin/rin".to_string(), "rin".to_string());
76        ern_43_prefixes.insert("http://ddex.net/xml/dsrf".to_string(), "dsrf".to_string());
77        self.version_locks.insert("4.3".to_string(), ern_43_prefixes.clone());
78        self.version_locks.insert("43".to_string(), ern_43_prefixes);
79        
80        // Mark all locked prefixes as reserved
81        for prefixes in self.version_locks.values() {
82            for prefix in prefixes.values() {
83                self.reserved_prefixes.insert(prefix.clone());
84            }
85        }
86    }
87    
88    /// Get locked prefix for a namespace URI in a specific version
89    pub fn get_locked_prefix(&self, uri: &str, version: &str) -> Option<&str> {
90        self.version_locks
91            .get(version)
92            .and_then(|prefixes| prefixes.get(uri))
93            .map(|prefix| prefix.as_str())
94    }
95    
96    /// Get all locked prefixes for a version
97    pub fn get_version_prefixes(&self, version: &str) -> Option<&IndexMap<String, String>> {
98        self.version_locks.get(version)
99    }
100    
101    /// Apply prefix deduplication algorithm
102    pub fn deduplicate_prefixes(&self, declarations: &IndexMap<String, String>, version: &str) -> IndexMap<String, String> {
103        let mut deduplicated = IndexMap::new();
104        let locked_prefixes = self.get_version_prefixes(version).cloned().unwrap_or_default();
105        
106        // First pass: apply locked prefixes
107        for (original_prefix, uri) in declarations {
108            if let Some(locked_prefix) = locked_prefixes.get(uri) {
109                deduplicated.insert(locked_prefix.clone(), uri.clone());
110            } else {
111                // Use original prefix if not locked
112                deduplicated.insert(original_prefix.clone(), uri.clone());
113            }
114        }
115        
116        // Second pass: resolve conflicts
117        let mut final_declarations = IndexMap::new();
118        let mut used_prefixes = HashSet::new();
119        
120        for (prefix, uri) in deduplicated {
121            if used_prefixes.contains(&prefix) {
122                // Generate unique prefix
123                let unique_prefix = self.generate_unique_prefix(&prefix, &used_prefixes);
124                used_prefixes.insert(unique_prefix.clone());
125                final_declarations.insert(unique_prefix, uri);
126            } else {
127                used_prefixes.insert(prefix.clone());
128                final_declarations.insert(prefix, uri);
129            }
130        }
131        
132        final_declarations
133    }
134    
135    /// Generate a unique prefix to avoid conflicts
136    fn generate_unique_prefix(&self, base_prefix: &str, used_prefixes: &HashSet<String>) -> String {
137        let mut counter = 1;
138        loop {
139            let candidate = format!("{}{}", base_prefix, counter);
140            if !used_prefixes.contains(&candidate) && !self.reserved_prefixes.contains(&candidate) {
141                return candidate;
142            }
143            counter += 1;
144        }
145    }
146}
147
148/// Comprehensive element ordering for all ERN versions
149pub struct ElementOrder {
150    /// Element ordering rules by version
151    version_orders: IndexMap<String, IndexMap<String, Vec<String>>>, // version -> (parent -> children)
152}
153
154impl ElementOrder {
155    /// Create new element order manager
156    pub fn new() -> Self {
157        let mut order = Self {
158            version_orders: IndexMap::new(),
159        };
160        
161        order.initialize_element_orders();
162        order
163    }
164    
165    /// Initialize element orders for all ERN versions
166    fn initialize_element_orders(&mut self) {
167        // ERN 3.8.2 element orders
168        let mut ern_382_order = IndexMap::new();
169        self.add_common_orders(&mut ern_382_order);
170        self.add_ern_382_specific_orders(&mut ern_382_order);
171        self.version_orders.insert("3.8.2".to_string(), ern_382_order.clone());
172        self.version_orders.insert("382".to_string(), ern_382_order);
173
174        // ERN 4.2 element orders
175        let mut ern_42_order = IndexMap::new();
176        self.add_common_orders(&mut ern_42_order);
177        self.add_ern_42_specific_orders(&mut ern_42_order);
178        self.version_orders.insert("4.2".to_string(), ern_42_order.clone());
179        self.version_orders.insert("42".to_string(), ern_42_order);
180
181        // ERN 4.3 element orders
182        let mut ern_43_order = IndexMap::new();
183        self.add_common_orders(&mut ern_43_order);
184        self.add_ern_43_specific_orders(&mut ern_43_order);
185        self.version_orders.insert("4.3".to_string(), ern_43_order.clone());
186        self.version_orders.insert("43".to_string(), ern_43_order);
187    }
188    
189    /// Add common element orders across all versions
190    fn add_common_orders(&self, order: &mut IndexMap<String, Vec<String>>) {
191        // Message header - common to all versions
192        order.insert("MessageHeader".to_string(), vec![
193            "MessageId".to_string(),
194            "MessageType".to_string(),
195            "MessageCreatedDateTime".to_string(),
196            "MessageSender".to_string(),
197            "MessageRecipient".to_string(),
198            "MessageControlType".to_string(),
199        ]);
200        
201        // Party - common structure
202        order.insert("Party".to_string(), vec![
203            "PartyReference".to_string(),
204            "PartyId".to_string(),
205            "PartyName".to_string(),
206            "PartyType".to_string(),
207        ]);
208        
209        // Release - base structure
210        order.insert("Release".to_string(), vec![
211            "ReleaseReference".to_string(),
212            "ReleaseId".to_string(),
213            "ReferenceTitle".to_string(),
214            "ReleaseType".to_string(),
215            "ReleaseResourceReferenceList".to_string(),
216            "ReleaseDetailsByTerritory".to_string(),
217        ]);
218        
219        // Deal - base structure
220        order.insert("Deal".to_string(), vec![
221            "DealReference".to_string(),
222            "DealTerms".to_string(),
223            "DealReleaseReference".to_string(),
224        ]);
225    }
226    
227    /// Add ERN 3.8.2 specific orders
228    fn add_ern_382_specific_orders(&self, order: &mut IndexMap<String, Vec<String>>) {
229        // SoundRecording for 3.8.2
230        order.insert("SoundRecording".to_string(), vec![
231            "SoundRecordingType".to_string(),
232            "SoundRecordingId".to_string(),
233            "ReferenceTitle".to_string(),
234            "Duration".to_string(),
235            "CreationDate".to_string(),
236            "SoundRecordingDetailsByTerritory".to_string(),
237        ]);
238    }
239    
240    /// Add ERN 4.2 specific orders
241    fn add_ern_42_specific_orders(&self, order: &mut IndexMap<String, Vec<String>>) {
242        // MessageHeader with audit trail for 4.2
243        order.insert("MessageHeader".to_string(), vec![
244            "MessageId".to_string(),
245            "MessageType".to_string(),
246            "MessageCreatedDateTime".to_string(),
247            "MessageSender".to_string(),
248            "MessageRecipient".to_string(),
249            "MessageControlType".to_string(),
250            "MessageAuditTrail".to_string(),
251        ]);
252        
253        // Enhanced SoundRecording for 4.2
254        order.insert("SoundRecording".to_string(), vec![
255            "SoundRecordingType".to_string(),
256            "SoundRecordingId".to_string(),
257            "ReferenceTitle".to_string(),
258            "DisplayTitle".to_string(),
259            "Duration".to_string(),
260            "CreationDate".to_string(),
261            "MasteredDate".to_string(),
262            "SoundRecordingDetailsByTerritory".to_string(),
263        ]);
264    }
265    
266    /// Add ERN 4.3 specific orders
267    fn add_ern_43_specific_orders(&self, order: &mut IndexMap<String, Vec<String>>) {
268        // MessageHeader with audit trail for 4.3
269        order.insert("MessageHeader".to_string(), vec![
270            "MessageId".to_string(),
271            "MessageType".to_string(),
272            "MessageCreatedDateTime".to_string(),
273            "MessageSender".to_string(),
274            "MessageRecipient".to_string(),
275            "MessageControlType".to_string(),
276            "MessageAuditTrail".to_string(),
277        ]);
278        
279        // Enhanced SoundRecording for 4.3
280        order.insert("SoundRecording".to_string(), vec![
281            "SoundRecordingType".to_string(),
282            "SoundRecordingId".to_string(),
283            "ReferenceTitle".to_string(),
284            "DisplayTitle".to_string(),
285            "DisplayTitleText".to_string(),
286            "Duration".to_string(),
287            "CreationDate".to_string(),
288            "MasteredDate".to_string(),
289            "OriginalResourceReleaseDate".to_string(),
290            "SoundRecordingDetailsByTerritory".to_string(),
291        ]);
292        
293        // Video for 4.3
294        order.insert("Video".to_string(), vec![
295            "VideoType".to_string(),
296            "VideoId".to_string(),
297            "ReferenceTitle".to_string(),
298            "DisplayTitle".to_string(),
299            "Duration".to_string(),
300            "CreationDate".to_string(),
301            "VideoDetailsByTerritory".to_string(),
302        ]);
303        
304        // Image for 4.3
305        order.insert("Image".to_string(), vec![
306            "ImageType".to_string(),
307            "ImageId".to_string(),
308            "ReferenceTitle".to_string(),
309            "DisplayTitle".to_string(),
310            "CreationDate".to_string(),
311            "ImageDetailsByTerritory".to_string(),
312        ]);
313    }
314    
315    /// Get element order for a parent element in a specific version
316    pub fn get_element_order(&self, parent_element: &str, version: &str) -> Option<&Vec<String>> {
317        self.version_orders
318            .get(version)
319            .and_then(|orders| orders.get(parent_element))
320    }
321    
322    /// Get all element orders for a version
323    pub fn get_version_orders(&self, version: &str) -> Option<&IndexMap<String, Vec<String>>> {
324        self.version_orders.get(version)
325    }
326}
327
328/// AVS namespace handling
329pub struct AVSNamespaceHandler {
330    /// AVS-specific namespaces
331    avs_namespaces: IndexMap<String, String>,
332}
333
334impl AVSNamespaceHandler {
335    /// Create new AVS namespace handler
336    pub fn new() -> Self {
337        let mut handler = Self {
338            avs_namespaces: IndexMap::new(),
339        };
340        
341        // Initialize AVS namespaces
342        handler.avs_namespaces.insert("http://ddex.net/xml/avs".to_string(), "avs".to_string());
343        handler.avs_namespaces.insert("http://ddex.net/xml/avs/avs".to_string(), "avs".to_string());
344        
345        handler
346    }
347    
348    /// Check if a namespace is AVS-related
349    pub fn is_avs_namespace(&self, uri: &str) -> bool {
350        self.avs_namespaces.contains_key(uri)
351    }
352    
353    /// Get AVS prefix for a namespace
354    pub fn get_avs_prefix(&self, uri: &str) -> Option<&str> {
355        self.avs_namespaces.get(uri).map(|prefix| prefix.as_str())
356    }
357}
358
359/// Complete namespace management for canonical XML
360pub struct CanonicalNamespaceManager {
361    /// Namespace prefix lock
362    prefix_lock: NamespacePrefixLock,
363    /// Element order manager
364    element_order: ElementOrder,
365    /// AVS namespace handler
366    avs_handler: AVSNamespaceHandler,
367}
368
369impl CanonicalNamespaceManager {
370    /// Create new canonical namespace manager
371    pub fn new() -> Self {
372        Self {
373            prefix_lock: NamespacePrefixLock::new(),
374            element_order: ElementOrder::new(),
375            avs_handler: AVSNamespaceHandler::new(),
376        }
377    }
378    
379    /// Apply complete canonical transformation
380    pub fn canonicalize_namespaces(
381        &self, 
382        declarations: &IndexMap<String, String>, 
383        version: &str
384    ) -> IndexMap<String, String> {
385        // Apply prefix locking and deduplication
386        let locked_declarations = self.prefix_lock.deduplicate_prefixes(declarations, version);
387        
388        // Sort declarations alphabetically by prefix
389        let mut sorted_declarations: Vec<_> = locked_declarations.into_iter().collect();
390        sorted_declarations.sort_by(|a, b| a.0.cmp(&b.0));
391        
392        sorted_declarations.into_iter().collect()
393    }
394    
395    /// Get element order for canonicalization
396    pub fn get_canonical_element_order(&self, parent: &str, version: &str) -> Option<&Vec<String>> {
397        self.element_order.get_element_order(parent, version)
398    }
399    
400    /// Check if namespace requires special AVS handling
401    pub fn requires_avs_handling(&self, uri: &str) -> bool {
402        self.avs_handler.is_avs_namespace(uri)
403    }
404}
405
406impl Default for NamespacePrefixLock {
407    fn default() -> Self {
408        Self::new()
409    }
410}
411
412impl Default for ElementOrder {
413    fn default() -> Self {
414        Self::new()
415    }
416}
417
418impl Default for AVSNamespaceHandler {
419    fn default() -> Self {
420        Self::new()
421    }
422}
423
424impl Default for CanonicalNamespaceManager {
425    fn default() -> Self {
426        Self::new()
427    }
428}
429
430/// Convenience functions for backward compatibility
431
432/// Get namespace prefix table for a specific ERN version
433pub fn get_namespace_prefixes(version: &str) -> IndexMap<String, String> {
434    let lock = NamespacePrefixLock::new();
435    lock.get_version_prefixes(version).cloned().unwrap_or_default()
436}
437
438/// Get element order for a specific ERN version
439pub fn get_element_order(version: &str) -> IndexMap<String, Vec<String>> {
440    let order = ElementOrder::new();
441    order.get_version_orders(version).cloned().unwrap_or_default()
442}
443
444#[cfg(test)]
445mod tests {
446    use super::*;
447
448    #[test]
449    fn test_namespace_prefix_lock() {
450        let lock = NamespacePrefixLock::new();
451        
452        assert_eq!(
453            lock.get_locked_prefix("http://ddex.net/xml/ern/43", "4.3"),
454            Some("ern")
455        );
456        assert_eq!(
457            lock.get_locked_prefix("http://ddex.net/xml/avs", "4.3"),
458            Some("avs")
459        );
460    }
461
462    #[test]
463    fn test_prefix_deduplication() {
464        let lock = NamespacePrefixLock::new();
465        
466        let mut declarations = IndexMap::new();
467        declarations.insert("custom_ern".to_string(), "http://ddex.net/xml/ern/43".to_string());
468        declarations.insert("avs".to_string(), "http://ddex.net/xml/avs".to_string());
469        
470        let deduplicated = lock.deduplicate_prefixes(&declarations, "4.3");
471        
472        // Should use locked prefix for ERN
473        assert_eq!(deduplicated.get("ern"), Some(&"http://ddex.net/xml/ern/43".to_string()));
474        assert_eq!(deduplicated.get("avs"), Some(&"http://ddex.net/xml/avs".to_string()));
475    }
476
477    #[test]
478    fn test_element_order() {
479        let order = ElementOrder::new();
480        
481        let message_order = order.get_element_order("MessageHeader", "4.3");
482        assert!(message_order.is_some());
483        
484        let order_vec = message_order.unwrap();
485        assert_eq!(order_vec[0], "MessageId");
486        assert_eq!(order_vec[1], "MessageType");
487    }
488
489    #[test]
490    fn test_avs_namespace_handler() {
491        let handler = AVSNamespaceHandler::new();
492        
493        assert!(handler.is_avs_namespace("http://ddex.net/xml/avs"));
494        assert_eq!(handler.get_avs_prefix("http://ddex.net/xml/avs"), Some("avs"));
495        assert!(!handler.is_avs_namespace("http://ddex.net/xml/ern/43"));
496    }
497
498    #[test]
499    fn test_canonical_namespace_manager() {
500        let manager = CanonicalNamespaceManager::new();
501        
502        let mut declarations = IndexMap::new();
503        declarations.insert("z_ern".to_string(), "http://ddex.net/xml/ern/43".to_string());
504        declarations.insert("a_avs".to_string(), "http://ddex.net/xml/avs".to_string());
505        
506        let canonical = manager.canonicalize_namespaces(&declarations, "4.3");
507        
508        // Should be sorted alphabetically and use locked prefixes
509        let keys: Vec<_> = canonical.keys().collect();
510        assert!(keys.len() >= 2);
511        // Should contain locked prefixes
512        assert!(canonical.contains_key("ern"));
513        assert!(canonical.contains_key("avs"));
514    }
515}