ddex_builder/linker/
reference_generator.rs

1//! Deterministic reference generation with configurable styles
2
3use super::types::{EntityType, ReferenceStyle};
4use indexmap::IndexMap;
5
6/// Generates deterministic references for DDEX entities
7#[derive(Debug, Clone)]
8pub struct ReferenceGenerator {
9    /// Style configuration for references
10    style: ReferenceStyle,
11
12    /// Counters for each entity type (deterministic ordering with IndexMap)
13    counters: IndexMap<EntityType, u32>,
14
15    /// Custom prefixes for entity types
16    prefixes: IndexMap<EntityType, String>,
17}
18
19impl ReferenceGenerator {
20    /// Create a new reference generator
21    pub fn new(style: ReferenceStyle) -> Self {
22        let mut prefixes = IndexMap::new();
23
24        // Default DDEX prefixes
25        prefixes.insert(EntityType::Release, "R".to_string());
26        prefixes.insert(EntityType::Resource, "A".to_string());
27        prefixes.insert(EntityType::Party, "P".to_string());
28        prefixes.insert(EntityType::Deal, "D".to_string());
29        prefixes.insert(EntityType::TechnicalDetails, "T".to_string());
30        prefixes.insert(EntityType::RightsController, "RC".to_string());
31
32        Self {
33            style,
34            counters: IndexMap::new(),
35            prefixes,
36        }
37    }
38
39    /// Generate a new reference for an entity type
40    pub fn generate(&mut self, entity_type: EntityType) -> String {
41        match self.style.clone() {
42            // Clone the style to avoid borrow issues
43            ReferenceStyle::Sequential => self.generate_sequential(entity_type),
44            ReferenceStyle::Custom(formatter) => {
45                formatter(entity_type, self.next_counter(entity_type))
46            }
47            ReferenceStyle::Prefixed { separator } => {
48                self.generate_prefixed(entity_type, &separator)
49            }
50        }
51    }
52
53    /// Generate sequential reference (e.g., "A1", "A2", "R1", "R2")
54    fn generate_sequential(&mut self, entity_type: EntityType) -> String {
55        let prefix = self
56            .prefixes
57            .get(&entity_type)
58            .expect("Unknown entity type")
59            .clone(); // Clone to avoid borrow issues
60        let counter = self.next_counter(entity_type);
61        format!("{}{}", prefix, counter)
62    }
63
64    /// Generate prefixed reference with custom separator
65    fn generate_prefixed(&mut self, entity_type: EntityType, separator: &str) -> String {
66        let prefix = self
67            .prefixes
68            .get(&entity_type)
69            .expect("Unknown entity type")
70            .clone(); // Clone to avoid borrow issues
71        let counter = self.next_counter(entity_type);
72        format!("{}{}{}", prefix, separator, counter)
73    }
74
75    /// Get next counter value for entity type
76    fn next_counter(&mut self, entity_type: EntityType) -> u32 {
77        let counter = self.counters.entry(entity_type).or_insert(0);
78        *counter += 1;
79        *counter
80    }
81
82    /// Set custom prefix for an entity type
83    pub fn set_prefix(&mut self, entity_type: EntityType, prefix: String) {
84        self.prefixes.insert(entity_type, prefix);
85    }
86
87    /// Reset counter for an entity type
88    pub fn reset_counter(&mut self, entity_type: EntityType) {
89        self.counters.insert(entity_type, 0);
90    }
91
92    /// Reset all counters
93    pub fn reset_all_counters(&mut self) {
94        self.counters.clear();
95    }
96
97    /// Get current counter value for entity type
98    pub fn get_counter(&self, entity_type: EntityType) -> u32 {
99        self.counters.get(&entity_type).copied().unwrap_or(0)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_sequential_generation() {
109        let mut gen = ReferenceGenerator::new(ReferenceStyle::Sequential);
110
111        assert_eq!(gen.generate(EntityType::Resource), "A1");
112        assert_eq!(gen.generate(EntityType::Resource), "A2");
113        assert_eq!(gen.generate(EntityType::Release), "R1");
114        assert_eq!(gen.generate(EntityType::Resource), "A3");
115        assert_eq!(gen.generate(EntityType::Release), "R2");
116    }
117
118    #[test]
119    fn test_deterministic_ordering() {
120        let mut gen1 = ReferenceGenerator::new(ReferenceStyle::Sequential);
121        let mut gen2 = ReferenceGenerator::new(ReferenceStyle::Sequential);
122
123        // Same sequence should produce same results
124        for _ in 0..5 {
125            assert_eq!(
126                gen1.generate(EntityType::Resource),
127                gen2.generate(EntityType::Resource)
128            );
129        }
130    }
131}