ddex_builder/linker/
relationship_manager.rs1use super::types::{EntityType, LinkingError};
4use indexmap::{IndexMap, IndexSet};
5
6#[derive(Debug, Clone, Default)]
8pub struct RelationshipManager {
9 registry: IndexMap<EntityType, IndexMap<String, String>>,
12
13 reverse_lookup: IndexMap<String, (EntityType, String)>,
15
16 relationships: IndexMap<String, IndexSet<String>>,
18}
19
20impl RelationshipManager {
21 pub fn new() -> Self {
23 Self::default()
24 }
25
26 pub fn register(&mut self, entity_type: EntityType, id: String, reference: String) {
28 self.registry
30 .entry(entity_type)
31 .or_insert_with(IndexMap::new)
32 .insert(id.clone(), reference.clone());
33
34 self.reverse_lookup.insert(reference, (entity_type, id));
36 }
37
38 pub fn get_reference(&self, entity_type: EntityType, id: &str) -> Option<String> {
40 self.registry
41 .get(&entity_type)
42 .and_then(|refs| refs.get(id))
43 .cloned()
44 }
45
46 pub fn get_entity_by_reference(&self, reference: &str) -> Option<(EntityType, String)> {
48 self.reverse_lookup.get(reference).cloned()
49 }
50
51 pub fn reference_exists(&self, reference: &str) -> bool {
53 self.reverse_lookup.contains_key(reference)
54 }
55
56 pub fn add_relationship(&mut self, from_ref: String, to_ref: String) {
58 self.relationships
59 .entry(from_ref)
60 .or_insert_with(IndexSet::new)
61 .insert(to_ref);
62 }
63
64 pub fn get_relationships(&self, reference: &str) -> Option<&IndexSet<String>> {
66 self.relationships.get(reference)
67 }
68
69 pub fn get_all(&self) -> IndexMap<EntityType, IndexMap<String, String>> {
71 self.registry.clone()
72 }
73
74 pub fn validate(&self) -> Result<(), Vec<LinkingError>> {
76 let mut errors = Vec::new();
77
78 for (from_ref, to_refs) in &self.relationships {
80 if !self.reference_exists(from_ref) {
81 errors.push(LinkingError::OrphanedReference(from_ref.clone()));
82 }
83
84 for to_ref in to_refs {
85 if !self.reference_exists(to_ref) {
86 errors.push(LinkingError::BrokenReference {
87 from: from_ref.clone(),
88 to: to_ref.clone(),
89 });
90 }
91 }
92 }
93
94 if errors.is_empty() {
95 Ok(())
96 } else {
97 Err(errors)
98 }
99 }
100
101 pub fn get_statistics(&self) -> RelationshipStatistics {
103 RelationshipStatistics {
104 total_entities: self.reverse_lookup.len(),
105 releases: self
106 .registry
107 .get(&EntityType::Release)
108 .map(|m| m.len())
109 .unwrap_or(0),
110 resources: self
111 .registry
112 .get(&EntityType::Resource)
113 .map(|m| m.len())
114 .unwrap_or(0),
115 parties: self
116 .registry
117 .get(&EntityType::Party)
118 .map(|m| m.len())
119 .unwrap_or(0),
120 deals: self
121 .registry
122 .get(&EntityType::Deal)
123 .map(|m| m.len())
124 .unwrap_or(0),
125 total_relationships: self.relationships.values().map(|s| s.len()).sum(),
126 }
127 }
128}
129
130#[derive(Debug, Clone)]
132pub struct RelationshipStatistics {
133 pub total_entities: usize,
134 pub releases: usize,
135 pub resources: usize,
136 pub parties: usize,
137 pub deals: usize,
138 pub total_relationships: usize,
139}