Skip to main content

knowledge_base_extension_framework/
registry.rs

1use crate::bindings::{BindingValue, ResolvedBindings};
2use crate::contracts::{BindingKind, BindingReference, ExtensionId, ExtensionMetadata, KnowledgeBaseExtension};
3use crate::error::FrameworkError;
4use knowledge_base_validation::KnowledgeBaseValidator;
5use std::collections::{BTreeMap, BTreeSet};
6use std::sync::Arc;
7
8/// The statically compiled extensions available in one distribution.
9pub struct ExtensionRegistry {
10    extensions: BTreeMap<ExtensionId, Arc<dyn KnowledgeBaseExtension>>,
11}
12
13impl ExtensionRegistry {
14    /// Registers extensions after validating their metadata and declared dependencies.
15    pub fn new(extensions: impl IntoIterator<Item = Arc<dyn KnowledgeBaseExtension>>) -> Result<Self, FrameworkError> {
16        let mut registered = BTreeMap::new();
17        for extension in extensions {
18            let metadata = extension.metadata();
19            validate_metadata(metadata)?;
20            let id = metadata.id.clone();
21            if registered.insert(id.clone(), extension).is_some() {
22                return Err(FrameworkError::DuplicateExtension(id));
23            }
24        }
25
26        let registry = Self { extensions: registered };
27        for extension in registry.extensions.values() {
28            registry.validate_dependencies(extension.metadata())?;
29            registry.validate_requirements(extension.metadata())?;
30        }
31        Ok(registry)
32    }
33
34    /// Resolves an explicitly activated set into stable dependency-first order.
35    pub fn resolve_active(&self, requested: impl IntoIterator<Item = ExtensionId>) -> Result<ActiveExtensions, FrameworkError> {
36        let requested = requested.into_iter().collect::<BTreeSet<_>>();
37        let mut ordered = Vec::new();
38        let mut state = BTreeMap::new();
39        for id in &requested {
40            self.visit(id, &requested, &mut state, &mut ordered, &mut Vec::new())?;
41        }
42        Ok(ActiveExtensions { ordered })
43    }
44
45    /// Returns compiled extension metadata by canonical identifier.
46    pub fn metadata(&self, id: &ExtensionId) -> Option<&ExtensionMetadata> {
47        self.extensions.get(id).map(|extension| extension.metadata())
48    }
49
50    /// Returns every compiled extension in canonical identifier order.
51    pub fn extensions(&self) -> impl Iterator<Item = &dyn KnowledgeBaseExtension> {
52        self.extensions.values().map(AsRef::as_ref)
53    }
54
55    fn validate_dependencies(&self, metadata: &ExtensionMetadata) -> Result<(), FrameworkError> {
56        for dependency in &metadata.dependencies {
57            let available = self.extensions.get(&dependency.id).ok_or_else(|| FrameworkError::MissingDependency {
58                extension: metadata.id.clone(),
59                dependency: dependency.id.clone(),
60            })?;
61            if available.metadata().contract != dependency.contract {
62                return Err(FrameworkError::UnsupportedContract {
63                    extension: metadata.id.clone(),
64                    required: dependency.contract,
65                    available: available.metadata().contract,
66                });
67            }
68        }
69        Ok(())
70    }
71
72    fn validate_requirements(&self, metadata: &ExtensionMetadata) -> Result<(), FrameworkError> {
73        for requirement in &metadata.ontology_requirements.entity_types {
74            self.validate_requirement_binding(metadata, &requirement.binding, BindingKind::EntityType)?;
75        }
76        for requirement in &metadata.ontology_requirements.properties {
77            self.validate_requirement_binding(metadata, &requirement.binding, BindingKind::Property)?;
78            for binding in &requirement.subject_types {
79                self.validate_requirement_binding(metadata, binding, BindingKind::EntityType)?;
80            }
81            if let Some(target_types) = &requirement.target_types {
82                for binding in target_types {
83                    self.validate_requirement_binding(metadata, binding, BindingKind::EntityType)?;
84                }
85            }
86            for binding in &requirement.allowed_qualifiers {
87                self.validate_requirement_binding(metadata, binding, BindingKind::Property)?;
88            }
89        }
90        Ok(())
91    }
92
93    fn validate_requirement_binding(&self, metadata: &ExtensionMetadata, reference: &BindingReference, expected: BindingKind) -> Result<(), FrameworkError> {
94        if reference.extension_id() != &metadata.id && !metadata.dependencies.iter().any(|dependency| dependency.id == *reference.extension_id()) {
95            return Err(FrameworkError::InaccessibleBinding {
96                extension: metadata.id.clone(),
97                binding: reference.clone(),
98            });
99        }
100        let owner = self.extensions.get(reference.extension_id()).expect("dependency availability was validated").metadata();
101        let declaration = owner
102            .bindings
103            .iter()
104            .find(|binding| binding.key == *reference.key())
105            .ok_or_else(|| FrameworkError::UndeclaredBinding {
106                extension: owner.id.clone(),
107                binding: reference.clone(),
108            })?;
109        if declaration.kind != expected {
110            return Err(FrameworkError::InvalidRequirement {
111                extension: metadata.id.clone(),
112                binding: reference.clone(),
113                expected,
114            });
115        }
116        Ok(())
117    }
118
119    /// Depth-first traversal detects cycles and appends each node after its dependencies.
120    fn visit(
121        &self,
122        id: &ExtensionId,
123        requested: &BTreeSet<ExtensionId>,
124        state: &mut BTreeMap<ExtensionId, Visit>,
125        ordered: &mut Vec<Arc<dyn KnowledgeBaseExtension>>,
126        stack: &mut Vec<ExtensionId>,
127    ) -> Result<(), FrameworkError> {
128        match state.get(id) {
129            Some(Visit::Done) => return Ok(()),
130            Some(Visit::Visiting) => {
131                let start = stack.iter().position(|item| item == id).expect("visiting extension is on stack");
132                let mut cycle = stack[start..].to_vec();
133                cycle.push(id.clone());
134                return Err(FrameworkError::DependencyCycle(cycle));
135            }
136            None => {}
137        }
138        let extension = self
139            .extensions
140            .get(id)
141            .ok_or_else(|| FrameworkError::MissingDependency {
142                extension: id.clone(),
143                dependency: id.clone(),
144            })?
145            .clone();
146        state.insert(id.clone(), Visit::Visiting);
147        stack.push(id.clone());
148        for dependency in &extension.metadata().dependencies {
149            if !requested.contains(&dependency.id) {
150                return Err(FrameworkError::InactiveDependency {
151                    extension: id.clone(),
152                    dependency: dependency.id.clone(),
153                });
154            }
155            self.visit(&dependency.id, requested, state, ordered, stack)?;
156        }
157        stack.pop();
158        state.insert(id.clone(), Visit::Done);
159        ordered.push(extension);
160        Ok(())
161    }
162}
163
164#[derive(Clone, Copy)]
165enum Visit {
166    Visiting,
167    Done,
168}
169
170/// The dependency-first extensions active for one repository configuration.
171pub struct ActiveExtensions {
172    ordered: Vec<Arc<dyn KnowledgeBaseExtension>>,
173}
174
175impl ActiveExtensions {
176    pub fn extensions(&self) -> &[Arc<dyn KnowledgeBaseExtension>] {
177        &self.ordered
178    }
179
180    /// Constructs every active extension validator in dependency-first order.
181    pub fn validators(&self, bindings: &ResolvedBindings) -> Result<Vec<Arc<dyn KnowledgeBaseValidator>>, FrameworkError> {
182        let mut validators = Vec::new();
183        for extension in &self.ordered {
184            validators.extend(extension.validators(bindings)?);
185        }
186        Ok(validators)
187    }
188
189    /// Verifies that every active declared binding has a value of its declared kind.
190    pub fn resolve_bindings(&self, declared: BTreeMap<BindingReference, BindingValue>) -> Result<ResolvedBindings, FrameworkError> {
191        let metadata = self
192            .ordered
193            .iter()
194            .map(|extension| (extension.metadata().id.clone(), extension.metadata()))
195            .collect::<BTreeMap<_, _>>();
196        let mut resolved = ResolvedBindings::default();
197        for (reference, value) in declared {
198            let owner = metadata.get(reference.extension_id()).ok_or_else(|| FrameworkError::MissingBinding(reference.clone()))?;
199            let declaration = owner
200                .bindings
201                .iter()
202                .find(|item| item.key == *reference.key())
203                .ok_or_else(|| FrameworkError::UndeclaredBinding {
204                    extension: owner.id.clone(),
205                    binding: reference.clone(),
206                })?;
207            if declaration.kind != value.kind() {
208                return Err(FrameworkError::BindingKindMismatch {
209                    binding: reference,
210                    expected: declaration.kind,
211                    actual: value.kind(),
212                });
213            }
214            resolved.values.insert(reference, value);
215        }
216        for extension in &self.ordered {
217            for declaration in &extension.metadata().bindings {
218                let reference = BindingReference::new(extension.metadata().id.clone(), declaration.key.clone());
219                if !resolved.values.contains_key(&reference) {
220                    return Err(FrameworkError::MissingBinding(reference));
221                }
222            }
223        }
224        Ok(resolved)
225    }
226}
227
228fn validate_metadata(metadata: &ExtensionMetadata) -> Result<(), FrameworkError> {
229    let mut dependencies = BTreeSet::new();
230    for dependency in &metadata.dependencies {
231        if !dependencies.insert(dependency.id.clone()) {
232            return Err(FrameworkError::DuplicateDependency {
233                extension: metadata.id.clone(),
234                dependency: dependency.id.clone(),
235            });
236        }
237    }
238    let mut bindings = BTreeMap::new();
239    for binding in &metadata.bindings {
240        if bindings.insert(binding.key.clone(), binding.kind).is_some() {
241            return Err(FrameworkError::DuplicateBinding {
242                extension: metadata.id.clone(),
243                key: binding.key.clone(),
244            });
245        }
246    }
247    Ok(())
248}