ddex_core/
namespace.rs

1//! # DDEX Namespace Management
2//! 
3//! This module provides comprehensive namespace management for the DDEX Suite,
4//! handling multiple DDEX versions and custom extensions with proper prefix
5//! management and collision detection.
6
7use indexmap::{IndexMap, IndexSet};
8use crate::models::versions::ERNVersion;
9
10/// DDEX namespace registry for comprehensive namespace management
11#[derive(Debug, Clone)]
12pub struct NamespaceRegistry {
13    /// Known DDEX namespace URIs mapped to their information
14    namespaces: IndexMap<String, NamespaceInfo>,
15    /// Default prefixes for known namespaces
16    default_prefixes: IndexMap<String, String>,
17    /// Custom namespace registrations
18    custom_namespaces: IndexMap<String, NamespaceInfo>,
19    /// Prefix collision detection
20    reserved_prefixes: IndexSet<String>,
21}
22
23/// Information about a namespace
24#[derive(Debug, Clone, PartialEq)]
25pub struct NamespaceInfo {
26    /// The namespace URI
27    pub uri: String,
28    /// Preferred prefix
29    pub preferred_prefix: String,
30    /// Alternative acceptable prefixes
31    pub alternative_prefixes: Vec<String>,
32    /// DDEX standard this namespace belongs to
33    pub standard: DDEXStandard,
34    /// Version information if applicable
35    pub version: Option<String>,
36    /// Whether this namespace is required for the standard
37    pub required: bool,
38}
39
40/// DDEX standards supported
41#[derive(Debug, Clone, PartialEq)]
42pub enum DDEXStandard {
43    /// Electronic Release Notification
44    ERN,
45    /// Audio Video Vocabulary
46    AVS,
47    /// Musical Work Electronic Delivery
48    MEAD,
49    /// Performer Information Exchange
50    PIE,
51    /// Recording Information Notification
52    RIN,
53    /// Rights and Remuneration Information
54    RRI,
55    /// Sales and Usage Reporting
56    DSRF,
57    /// XML Schema Instance
58    XMLSchema,
59    /// Custom/Unknown standard
60    Custom(String),
61}
62
63/// Namespace scope tracking for inheritance
64#[derive(Debug, Clone)]
65pub struct NamespaceScope {
66    /// Active namespace declarations at this scope
67    pub declarations: IndexMap<String, String>, // prefix -> uri
68    /// Parent scope for inheritance
69    pub parent: Option<Box<NamespaceScope>>,
70    /// Element depth for debugging
71    pub depth: usize,
72}
73
74/// Namespace conflict resolution strategy
75#[derive(Debug, Clone, PartialEq)]
76pub enum ConflictResolution {
77    /// Use the first declared prefix
78    PreferFirst,
79    /// Use the most recently declared prefix
80    PreferLatest,
81    /// Generate a unique prefix
82    GenerateUnique,
83    /// Throw an error
84    Error,
85}
86
87impl NamespaceRegistry {
88    /// Create a new namespace registry with all known DDEX namespaces
89    pub fn new() -> Self {
90        let mut registry = Self {
91            namespaces: IndexMap::new(),
92            default_prefixes: IndexMap::new(),
93            custom_namespaces: IndexMap::new(),
94            reserved_prefixes: IndexSet::new(),
95        };
96        
97        registry.initialize_ddex_namespaces();
98        registry
99    }
100
101    /// Initialize all known DDEX namespaces
102    fn initialize_ddex_namespaces(&mut self) {
103        // ERN namespaces
104        self.register_namespace(NamespaceInfo {
105            uri: "http://ddex.net/xml/ern/382".to_string(),
106            preferred_prefix: "ern".to_string(),
107            alternative_prefixes: vec!["ern382".to_string()],
108            standard: DDEXStandard::ERN,
109            version: Some("3.8.2".to_string()),
110            required: true,
111        });
112
113        self.register_namespace(NamespaceInfo {
114            uri: "http://ddex.net/xml/ern/42".to_string(),
115            preferred_prefix: "ern".to_string(),
116            alternative_prefixes: vec!["ern42".to_string()],
117            standard: DDEXStandard::ERN,
118            version: Some("4.2".to_string()),
119            required: true,
120        });
121
122        self.register_namespace(NamespaceInfo {
123            uri: "http://ddex.net/xml/ern/43".to_string(),
124            preferred_prefix: "ern".to_string(),
125            alternative_prefixes: vec!["ern43".to_string()],
126            standard: DDEXStandard::ERN,
127            version: Some("4.3".to_string()),
128            required: true,
129        });
130
131        // AVS namespaces
132        self.register_namespace(NamespaceInfo {
133            uri: "http://ddex.net/xml/avs".to_string(),
134            preferred_prefix: "avs".to_string(),
135            alternative_prefixes: vec!["ddexavs".to_string()],
136            standard: DDEXStandard::AVS,
137            version: None,
138            required: false,
139        });
140
141        self.register_namespace(NamespaceInfo {
142            uri: "http://ddex.net/xml/avs/avs".to_string(),
143            preferred_prefix: "avs".to_string(),
144            alternative_prefixes: vec!["ddexavs".to_string()],
145            standard: DDEXStandard::AVS,
146            version: None,
147            required: false,
148        });
149
150        // MEAD namespaces
151        self.register_namespace(NamespaceInfo {
152            uri: "http://ddex.net/xml/mead/mead".to_string(),
153            preferred_prefix: "mead".to_string(),
154            alternative_prefixes: vec!["ddexmead".to_string()],
155            standard: DDEXStandard::MEAD,
156            version: None,
157            required: false,
158        });
159
160        // PIE namespaces
161        self.register_namespace(NamespaceInfo {
162            uri: "http://ddex.net/xml/pie/pie".to_string(),
163            preferred_prefix: "pie".to_string(),
164            alternative_prefixes: vec!["ddexpie".to_string()],
165            standard: DDEXStandard::PIE,
166            version: None,
167            required: false,
168        });
169
170        // RIN namespaces
171        self.register_namespace(NamespaceInfo {
172            uri: "http://ddex.net/xml/rin/rin".to_string(),
173            preferred_prefix: "rin".to_string(),
174            alternative_prefixes: vec!["ddexrin".to_string()],
175            standard: DDEXStandard::RIN,
176            version: None,
177            required: false,
178        });
179
180        // XML Schema Instance
181        self.register_namespace(NamespaceInfo {
182            uri: "http://www.w3.org/2001/XMLSchema-instance".to_string(),
183            preferred_prefix: "xsi".to_string(),
184            alternative_prefixes: vec!["xmlschema".to_string()],
185            standard: DDEXStandard::XMLSchema,
186            version: None,
187            required: false,
188        });
189
190        // XML Schema
191        self.register_namespace(NamespaceInfo {
192            uri: "http://www.w3.org/2001/XMLSchema".to_string(),
193            preferred_prefix: "xs".to_string(),
194            alternative_prefixes: vec!["xsd".to_string(), "schema".to_string()],
195            standard: DDEXStandard::XMLSchema,
196            version: None,
197            required: false,
198        });
199
200        // Common extension namespaces
201        self.register_namespace(NamespaceInfo {
202            uri: "http://ddex.net/xml/gc".to_string(),
203            preferred_prefix: "gc".to_string(),
204            alternative_prefixes: vec!["ddexgc".to_string()],
205            standard: DDEXStandard::Custom("GC".to_string()),
206            version: None,
207            required: false,
208        });
209    }
210
211    /// Register a new namespace
212    pub fn register_namespace(&mut self, info: NamespaceInfo) {
213        self.default_prefixes.insert(info.uri.clone(), info.preferred_prefix.clone());
214        self.reserved_prefixes.insert(info.preferred_prefix.clone());
215        
216        for alt_prefix in &info.alternative_prefixes {
217            self.reserved_prefixes.insert(alt_prefix.clone());
218        }
219        
220        self.namespaces.insert(info.uri.clone(), info);
221    }
222
223    /// Register a custom namespace
224    pub fn register_custom_namespace(&mut self, info: NamespaceInfo) -> Result<(), NamespaceError> {
225        // Check for URI conflicts
226        if self.namespaces.contains_key(&info.uri) || self.custom_namespaces.contains_key(&info.uri) {
227            return Err(NamespaceError::UriConflict(info.uri));
228        }
229
230        // Check for prefix conflicts
231        if self.is_prefix_reserved(&info.preferred_prefix) {
232            return Err(NamespaceError::PrefixConflict(info.preferred_prefix));
233        }
234
235        self.reserved_prefixes.insert(info.preferred_prefix.clone());
236        self.custom_namespaces.insert(info.uri.clone(), info);
237        Ok(())
238    }
239
240    /// Detect DDEX version from namespace URI
241    pub fn detect_version(&self, namespace_uri: &str) -> Option<ERNVersion> {
242        match namespace_uri {
243            "http://ddex.net/xml/ern/382" => Some(ERNVersion::V3_8_2),
244            "http://ddex.net/xml/ern/42" => Some(ERNVersion::V4_2),
245            "http://ddex.net/xml/ern/43" => Some(ERNVersion::V4_3),
246            _ => None,
247        }
248    }
249
250    /// Get all namespace URIs for a specific ERN version
251    pub fn get_version_namespaces(&self, version: &ERNVersion) -> Vec<String> {
252        let mut namespaces = vec![];
253        
254        // Add the main ERN namespace
255        match version {
256            ERNVersion::V3_8_2 => namespaces.push("http://ddex.net/xml/ern/382".to_string()),
257            ERNVersion::V4_2 => namespaces.push("http://ddex.net/xml/ern/42".to_string()),
258            ERNVersion::V4_3 => namespaces.push("http://ddex.net/xml/ern/43".to_string()),
259        }
260        
261        // Add common supporting namespaces
262        namespaces.push("http://ddex.net/xml/avs".to_string());
263        namespaces.push("http://www.w3.org/2001/XMLSchema-instance".to_string());
264        
265        namespaces
266    }
267
268    /// Get preferred prefix for a namespace URI
269    pub fn get_preferred_prefix(&self, uri: &str) -> Option<&str> {
270        self.default_prefixes.get(uri).map(|s| s.as_str())
271    }
272
273    /// Get namespace info by URI
274    pub fn get_namespace_info(&self, uri: &str) -> Option<&NamespaceInfo> {
275        self.namespaces.get(uri).or_else(|| self.custom_namespaces.get(uri))
276    }
277
278    /// Check if a prefix is reserved
279    pub fn is_prefix_reserved(&self, prefix: &str) -> bool {
280        self.reserved_prefixes.contains(prefix)
281    }
282
283    /// Generate a unique prefix for a namespace
284    pub fn generate_unique_prefix(&self, base_prefix: &str) -> String {
285        if !self.is_prefix_reserved(base_prefix) {
286            return base_prefix.to_string();
287        }
288        
289        let mut counter = 1;
290        loop {
291            let candidate = format!("{}{}", base_prefix, counter);
292            if !self.is_prefix_reserved(&candidate) {
293                return candidate;
294            }
295            counter += 1;
296        }
297    }
298
299    /// Resolve namespace conflicts using the specified strategy
300    pub fn resolve_prefix_conflict(
301        &self,
302        _uri: &str,
303        existing_prefix: &str,
304        new_prefix: &str,
305        strategy: ConflictResolution,
306    ) -> Result<String, NamespaceError> {
307        match strategy {
308            ConflictResolution::PreferFirst => Ok(existing_prefix.to_string()),
309            ConflictResolution::PreferLatest => Ok(new_prefix.to_string()),
310            ConflictResolution::GenerateUnique => Ok(self.generate_unique_prefix(new_prefix)),
311            ConflictResolution::Error => Err(NamespaceError::PrefixConflict(new_prefix.to_string())),
312        }
313    }
314
315    /// Create minimal namespace declarations for root element
316    pub fn create_minimal_declarations(&self, used_namespaces: &[String]) -> IndexMap<String, String> {
317        let mut declarations = IndexMap::new();
318        
319        for uri in used_namespaces {
320            if let Some(prefix) = self.get_preferred_prefix(uri) {
321                declarations.insert(prefix.to_string(), uri.clone());
322            }
323        }
324        
325        declarations
326    }
327
328    /// Validate namespace declarations against known namespaces
329    pub fn validate_declarations(&self, declarations: &IndexMap<String, String>) -> Vec<NamespaceWarning> {
330        let mut warnings = Vec::new();
331        
332        for (prefix, uri) in declarations {
333            if let Some(info) = self.get_namespace_info(uri) {
334                // Check if using non-preferred prefix
335                if prefix != &info.preferred_prefix && !info.alternative_prefixes.contains(prefix) {
336                    warnings.push(NamespaceWarning::NonStandardPrefix {
337                        uri: uri.clone(),
338                        used_prefix: prefix.clone(),
339                        preferred_prefix: info.preferred_prefix.clone(),
340                    });
341                }
342            } else {
343                // Unknown namespace
344                warnings.push(NamespaceWarning::UnknownNamespace {
345                    uri: uri.clone(),
346                    prefix: prefix.clone(),
347                });
348            }
349        }
350        
351        warnings
352    }
353
354    /// Get all registered namespaces for a standard
355    pub fn get_namespaces_by_standard(&self, standard: &DDEXStandard) -> Vec<&NamespaceInfo> {
356        self.namespaces
357            .values()
358            .chain(self.custom_namespaces.values())
359            .filter(|info| &info.standard == standard)
360            .collect()
361    }
362}
363
364impl NamespaceScope {
365    /// Create a new root scope
366    pub fn new() -> Self {
367        Self {
368            declarations: IndexMap::new(),
369            parent: None,
370            depth: 0,
371        }
372    }
373
374    /// Create a child scope
375    pub fn new_child(&self) -> Self {
376        Self {
377            declarations: IndexMap::new(),
378            parent: Some(Box::new(self.clone())),
379            depth: self.depth + 1,
380        }
381    }
382
383    /// Add a namespace declaration to this scope
384    pub fn declare_namespace(&mut self, prefix: String, uri: String) {
385        self.declarations.insert(prefix, uri);
386    }
387
388    /// Resolve a prefix to its URI, checking parent scopes
389    pub fn resolve_prefix(&self, prefix: &str) -> Option<String> {
390        if let Some(uri) = self.declarations.get(prefix) {
391            Some(uri.clone())
392        } else if let Some(parent) = &self.parent {
393            parent.resolve_prefix(prefix)
394        } else {
395            None
396        }
397    }
398
399    /// Get all active namespace declarations (including inherited)
400    pub fn get_all_declarations(&self) -> IndexMap<String, String> {
401        let mut all_declarations = IndexMap::new();
402        
403        // Start with parent declarations
404        if let Some(parent) = &self.parent {
405            all_declarations = parent.get_all_declarations();
406        }
407        
408        // Override with current scope declarations
409        for (prefix, uri) in &self.declarations {
410            all_declarations.insert(prefix.clone(), uri.clone());
411        }
412        
413        all_declarations
414    }
415
416    /// Check if a namespace is declared in this scope or parents
417    pub fn is_namespace_declared(&self, uri: &str) -> bool {
418        self.declarations.values().any(|declared_uri| declared_uri == uri) ||
419        self.parent.as_ref().map_or(false, |parent| parent.is_namespace_declared(uri))
420    }
421
422    /// Find the prefix for a namespace URI
423    pub fn find_prefix_for_uri(&self, uri: &str) -> Option<String> {
424        for (prefix, declared_uri) in &self.declarations {
425            if declared_uri == uri {
426                return Some(prefix.clone());
427            }
428        }
429        
430        if let Some(parent) = &self.parent {
431            parent.find_prefix_for_uri(uri)
432        } else {
433            None
434        }
435    }
436}
437
438/// Namespace-related errors
439#[derive(Debug, Clone, PartialEq)]
440pub enum NamespaceError {
441    /// URI conflict with existing namespace
442    UriConflict(String),
443    /// Prefix conflict with reserved prefix
444    PrefixConflict(String),
445    /// Invalid namespace URI format
446    InvalidUri(String),
447    /// Circular namespace dependency
448    CircularDependency(Vec<String>),
449}
450
451impl std::fmt::Display for NamespaceError {
452    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
453        match self {
454            NamespaceError::UriConflict(uri) => write!(f, "Namespace URI conflict: {}", uri),
455            NamespaceError::PrefixConflict(prefix) => write!(f, "Prefix conflict: {}", prefix),
456            NamespaceError::InvalidUri(uri) => write!(f, "Invalid namespace URI: {}", uri),
457            NamespaceError::CircularDependency(chain) => {
458                write!(f, "Circular namespace dependency: {}", chain.join(" -> "))
459            }
460        }
461    }
462}
463
464impl std::error::Error for NamespaceError {}
465
466/// Namespace-related warnings
467#[derive(Debug, Clone, PartialEq)]
468pub enum NamespaceWarning {
469    /// Using non-standard prefix for known namespace
470    NonStandardPrefix {
471        uri: String,
472        used_prefix: String,
473        preferred_prefix: String,
474    },
475    /// Unknown/unregistered namespace
476    UnknownNamespace {
477        uri: String,
478        prefix: String,
479    },
480    /// Redundant namespace declaration
481    RedundantDeclaration {
482        uri: String,
483        prefix: String,
484    },
485}
486
487impl std::fmt::Display for NamespaceWarning {
488    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489        match self {
490            NamespaceWarning::NonStandardPrefix { uri, used_prefix, preferred_prefix } => {
491                write!(f, "Non-standard prefix '{}' for namespace '{}', prefer '{}'", 
492                       used_prefix, uri, preferred_prefix)
493            },
494            NamespaceWarning::UnknownNamespace { uri, prefix } => {
495                write!(f, "Unknown namespace '{}' with prefix '{}'", uri, prefix)
496            },
497            NamespaceWarning::RedundantDeclaration { uri, prefix } => {
498                write!(f, "Redundant declaration of namespace '{}' with prefix '{}'", uri, prefix)
499            },
500        }
501    }
502}
503
504impl Default for NamespaceRegistry {
505    fn default() -> Self {
506        Self::new()
507    }
508}
509
510impl Default for NamespaceScope {
511    fn default() -> Self {
512        Self::new()
513    }
514}
515
516#[cfg(test)]
517mod tests {
518    use super::*;
519
520    #[test]
521    fn test_namespace_registry_creation() {
522        let registry = NamespaceRegistry::new();
523        assert!(registry.get_preferred_prefix("http://ddex.net/xml/ern/43").is_some());
524        assert_eq!(registry.get_preferred_prefix("http://ddex.net/xml/ern/43"), Some("ern"));
525    }
526
527    #[test]
528    fn test_version_detection() {
529        let registry = NamespaceRegistry::new();
530        assert_eq!(registry.detect_version("http://ddex.net/xml/ern/382"), Some(ERNVersion::V3_8_2));
531        assert_eq!(registry.detect_version("http://ddex.net/xml/ern/42"), Some(ERNVersion::V4_2));
532        assert_eq!(registry.detect_version("http://ddex.net/xml/ern/43"), Some(ERNVersion::V4_3));
533        assert_eq!(registry.detect_version("http://unknown.com/namespace"), None);
534    }
535
536    #[test]
537    fn test_custom_namespace_registration() {
538        let mut registry = NamespaceRegistry::new();
539        
540        let custom_ns = NamespaceInfo {
541            uri: "http://example.com/custom".to_string(),
542            preferred_prefix: "ex".to_string(),
543            alternative_prefixes: vec!["example".to_string()],
544            standard: DDEXStandard::Custom("Example".to_string()),
545            version: None,
546            required: false,
547        };
548
549        assert!(registry.register_custom_namespace(custom_ns).is_ok());
550        assert_eq!(registry.get_preferred_prefix("http://example.com/custom"), Some("ex"));
551    }
552
553    #[test]
554    fn test_prefix_conflict_detection() {
555        let mut registry = NamespaceRegistry::new();
556        
557        let conflicting_ns = NamespaceInfo {
558            uri: "http://example.com/conflict".to_string(),
559            preferred_prefix: "ern".to_string(), // Conflicts with existing ERN prefix
560            alternative_prefixes: vec![],
561            standard: DDEXStandard::Custom("Conflict".to_string()),
562            version: None,
563            required: false,
564        };
565
566        assert!(matches!(registry.register_custom_namespace(conflicting_ns), Err(NamespaceError::PrefixConflict(_))));
567    }
568
569    #[test]
570    fn test_namespace_scope() {
571        let mut root_scope = NamespaceScope::new();
572        root_scope.declare_namespace("ern".to_string(), "http://ddex.net/xml/ern/43".to_string());
573        
574        let mut child_scope = root_scope.new_child();
575        child_scope.declare_namespace("avs".to_string(), "http://ddex.net/xml/avs".to_string());
576        
577        // Child should resolve both its own and parent declarations
578        assert_eq!(child_scope.resolve_prefix("ern"), Some("http://ddex.net/xml/ern/43".to_string()));
579        assert_eq!(child_scope.resolve_prefix("avs"), Some("http://ddex.net/xml/avs".to_string()));
580        
581        // Parent should not see child declarations
582        assert_eq!(root_scope.resolve_prefix("avs"), None);
583    }
584
585    #[test]
586    fn test_unique_prefix_generation() {
587        let mut registry = NamespaceRegistry::new();
588        
589        // Reserve some test prefixes
590        registry.reserved_prefixes.insert("test".to_string());
591        registry.reserved_prefixes.insert("test1".to_string());
592        
593        let unique = registry.generate_unique_prefix("test");
594        assert_eq!(unique, "test2");
595    }
596
597    #[test]
598    fn test_minimal_declarations() {
599        let registry = NamespaceRegistry::new();
600        let used_namespaces = vec![
601            "http://ddex.net/xml/ern/43".to_string(),
602            "http://ddex.net/xml/avs".to_string(),
603        ];
604        
605        let declarations = registry.create_minimal_declarations(&used_namespaces);
606        assert_eq!(declarations.get("ern"), Some(&"http://ddex.net/xml/ern/43".to_string()));
607        assert_eq!(declarations.get("avs"), Some(&"http://ddex.net/xml/avs".to_string()));
608    }
609}