1use std::collections::{hash_map::DefaultHasher, BTreeMap};
2use std::fmt;
3use std::hash::Hasher;
4use std::sync::Arc;
5
6use super::identity::validate_identifier;
7use super::{
8 CacheOwnerId, EngineId, ExtensionEngine, ExtensionPlanningConfig, RegistrationIdentity,
9 RuntimeCacheOwner,
10};
11use super::{
12 ExecutionContextIdentity, ExtensionModuleError, IdentityError, IdentityKind, RegistrationKey,
13 RuntimeConfigError,
14};
15
16pub(super) type ExtensionFamilyId = &'static str;
17
18#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
31pub struct ExtensionModuleId(Arc<str>);
32
33impl ExtensionModuleId {
34 pub fn new(value: impl Into<Arc<str>>) -> Result<Self, IdentityError> {
41 validate_identifier(value.into(), IdentityKind::ExtensionModule).map(Self)
42 }
43
44 pub fn as_str(&self) -> &str {
46 &self.0
47 }
48}
49
50pub trait ExtensionModule: fmt::Debug + Send + Sync + 'static {
52 fn module_id(&self) -> &ExtensionModuleId;
54
55 fn configure(
62 &self,
63 registrar: &mut ExtensionModuleRegistrar<'_>,
64 ) -> Result<(), ExtensionModuleError>;
65}
66
67pub(super) struct CandidateModuleRecord {
68 pub(super) module: Arc<dyn ExtensionModule>,
69 pub(super) engines: BTreeMap<(ExtensionFamilyId, EngineId), CandidateExtensionEngine>,
70 pub(super) configs: BTreeMap<EngineId, Arc<dyn ExtensionPlanningConfig>>,
71 pub(super) owners: BTreeMap<CacheOwnerId, Arc<dyn RuntimeCacheOwner>>,
72}
73
74impl fmt::Debug for CandidateModuleRecord {
75 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
76 formatter
77 .debug_struct("CandidateModuleRecord")
78 .field("module_id", self.module.module_id())
79 .field("engine_count", &self.engines.len())
80 .field("config_count", &self.configs.len())
81 .field("owner_count", &self.owners.len())
82 .finish_non_exhaustive()
83 }
84}
85
86impl Clone for CandidateModuleRecord {
87 fn clone(&self) -> Self {
88 Self {
89 module: Arc::clone(&self.module),
90 engines: self.engines.clone(),
91 configs: self.configs.clone(),
92 owners: self.owners.clone(),
93 }
94 }
95}
96
97impl CandidateModuleRecord {
98 pub(super) fn module_identical(&self, module: &Arc<dyn ExtensionModule>) -> bool {
99 Arc::ptr_eq(&self.module, module)
100 }
101}
102
103pub(super) struct CandidateExtensionEngine {
104 pub(super) engine: Arc<dyn ExtensionEngine>,
105 pub(super) identity: CandidateRegistrationIdentity,
106}
107
108#[derive(Clone, Debug)]
109pub(super) enum CandidateRegistrationIdentity {
110 New,
111 Preserved(RegistrationIdentity),
112}
113
114impl Clone for CandidateExtensionEngine {
115 fn clone(&self) -> Self {
116 Self {
117 engine: Arc::clone(&self.engine),
118 identity: self.identity.clone(),
119 }
120 }
121}
122
123impl fmt::Debug for CandidateExtensionEngine {
124 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
125 formatter
126 .debug_struct("CandidateExtensionEngine")
127 .field("family_id", &self.engine.family_id())
128 .field("engine_id", self.engine.engine_id())
129 .field("context_identity", &self.engine.context_identity())
130 .field("identity", &self.identity)
131 .finish_non_exhaustive()
132 }
133}
134
135pub(super) struct BoundCandidateExtensionEngine {
136 pub(super) engine: Arc<dyn ExtensionEngine>,
137 pub(super) identity: RegistrationIdentity,
138}
139
140pub(super) struct BoundCandidateModuleRecord {
141 pub(super) module: Arc<dyn ExtensionModule>,
142 pub(super) engines: BTreeMap<(ExtensionFamilyId, EngineId), BoundCandidateExtensionEngine>,
143 pub(super) configs: BTreeMap<EngineId, Arc<dyn ExtensionPlanningConfig>>,
144 pub(super) owners: BTreeMap<CacheOwnerId, Arc<dyn RuntimeCacheOwner>>,
145}
146
147#[derive(Clone)]
148pub(super) struct FrozenExtensionEngineSlot {
149 module_id: ExtensionModuleId,
150 family_id: ExtensionFamilyId,
151 engine_id: EngineId,
152 context_identity: ExecutionContextIdentity,
153 registration_identity: RegistrationIdentity,
154 engine: Arc<dyn ExtensionEngine>,
155 config: Arc<dyn ExtensionPlanningConfig>,
156}
157
158impl fmt::Debug for FrozenExtensionEngineSlot {
159 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
160 formatter
161 .debug_struct("FrozenExtensionEngineSlot")
162 .field("module_id", &self.module_id)
163 .field("family_id", &self.family_id)
164 .field("engine_id", &self.engine_id)
165 .field("context_identity", &self.context_identity)
166 .field("registration_identity", &self.registration_identity)
167 .field("config_retained_bytes", &self.config.retained_bytes())
168 .finish_non_exhaustive()
169 }
170}
171
172#[derive(Clone)]
173pub(super) struct FrozenExtensionSlots {
174 modules: BTreeMap<ExtensionModuleId, Arc<dyn ExtensionModule>>,
175 engines: Arc<[FrozenExtensionEngineSlot]>,
176 by_family_engine: BTreeMap<(ExtensionFamilyId, EngineId), usize>,
177 owners: BTreeMap<(ExtensionModuleId, CacheOwnerId), Arc<dyn RuntimeCacheOwner>>,
178}
179
180pub(super) struct ExtensionEngineSnapshotView<'a> {
181 slot: &'a FrozenExtensionEngineSlot,
182}
183
184impl<'a> ExtensionEngineSnapshotView<'a> {
185 pub(super) fn module_id(&self) -> &'a ExtensionModuleId {
186 &self.slot.module_id
187 }
188
189 pub(super) fn family_id(&self) -> ExtensionFamilyId {
190 self.slot.family_id
191 }
192
193 pub(super) fn engine_id(&self) -> &'a EngineId {
194 &self.slot.engine_id
195 }
196
197 pub(super) fn context_identity(&self) -> ExecutionContextIdentity {
198 self.slot.context_identity
199 }
200
201 pub(super) fn registration_identity(&self) -> RegistrationIdentity {
202 self.slot.registration_identity
203 }
204
205 pub(super) fn engine(&self) -> &'a Arc<dyn ExtensionEngine> {
206 &self.slot.engine
207 }
208
209 pub(super) fn config(&self) -> &'a Arc<dyn ExtensionPlanningConfig> {
210 &self.slot.config
211 }
212}
213
214#[cfg(test)]
215pub(super) type ExtensionSlotFullForTest<'a> = (
216 &'a ExtensionModuleId,
217 ExtensionFamilyId,
218 &'a EngineId,
219 RegistrationIdentity,
220 &'a Arc<dyn ExtensionEngine>,
221 &'a Arc<dyn ExtensionPlanningConfig>,
222);
223
224impl FrozenExtensionSlots {
225 pub(super) fn module_count(&self) -> usize {
226 self.modules.len()
227 }
228
229 pub(super) fn engine_count(&self) -> usize {
230 self.engines.len()
231 }
232
233 pub(super) fn has_family(&self, family_id: ExtensionFamilyId) -> bool {
234 self.engines.iter().any(|slot| slot.family_id == family_id)
235 }
236
237 pub(super) fn to_candidate_modules(
238 &self,
239 ) -> BTreeMap<ExtensionModuleId, CandidateModuleRecord> {
240 let mut modules = BTreeMap::new();
241 for (module_id, module) in &self.modules {
242 modules.insert(
243 module_id.clone(),
244 CandidateModuleRecord {
245 module: Arc::clone(module),
246 engines: BTreeMap::new(),
247 configs: BTreeMap::new(),
248 owners: BTreeMap::new(),
249 },
250 );
251 }
252 for slot in self.engines.iter() {
253 if let Some(record) = modules.get_mut(&slot.module_id) {
254 record.engines.insert(
255 (slot.family_id, slot.engine_id.clone()),
256 CandidateExtensionEngine {
257 engine: Arc::clone(&slot.engine),
258 identity: CandidateRegistrationIdentity::Preserved(
259 slot.registration_identity,
260 ),
261 },
262 );
263 record
264 .configs
265 .insert(slot.engine_id.clone(), Arc::clone(&slot.config));
266 }
267 }
268 for ((module_id, owner_id), owner) in &self.owners {
269 if let Some(record) = modules.get_mut(module_id) {
270 record.owners.insert(owner_id.clone(), Arc::clone(owner));
271 }
272 }
273 modules
274 }
275
276 pub(super) fn cache_owner_records(
277 &self,
278 ) -> impl Iterator<Item = (CacheOwnerId, Arc<dyn RuntimeCacheOwner>)> + '_ {
279 self.owners.iter().map(|((module_id, owner_id), owner)| {
280 (
281 extension_cache_owner_id(module_id, owner_id),
282 Arc::clone(owner),
283 )
284 })
285 }
286
287 #[cfg(test)]
288 pub(super) fn slots_for_test(
289 &self,
290 ) -> impl Iterator<
291 Item = (
292 &ExtensionModuleId,
293 ExtensionFamilyId,
294 &EngineId,
295 RegistrationIdentity,
296 ),
297 > {
298 self.engines.iter().map(|slot| {
299 (
300 &slot.module_id,
301 slot.family_id,
302 &slot.engine_id,
303 slot.registration_identity,
304 )
305 })
306 }
307
308 #[cfg(test)]
309 pub(super) fn slot_identity_for_test(
310 &self,
311 family_id: ExtensionFamilyId,
312 engine_id: &EngineId,
313 ) -> Option<RegistrationIdentity> {
314 self.extension_engine_slot(family_id, engine_id)
315 .map(|slot| slot.registration_identity)
316 }
317
318 #[cfg(test)]
319 pub(super) fn slot_full_for_test(
320 &self,
321 family_id: ExtensionFamilyId,
322 engine_id: &EngineId,
323 ) -> Option<ExtensionSlotFullForTest<'_>> {
324 self.extension_engine_slot(family_id, engine_id)
325 .map(|slot| {
326 (
327 &slot.module_id,
328 slot.family_id,
329 &slot.engine_id,
330 slot.registration_identity,
331 &slot.engine,
332 &slot.config,
333 )
334 })
335 }
336
337 pub(super) fn slot_for_preparation(
338 &self,
339 family_id: ExtensionFamilyId,
340 engine_id: &EngineId,
341 ) -> Option<ExtensionEngineSnapshotView<'_>> {
342 self.extension_engine_slot(family_id, engine_id)
343 .map(|slot| ExtensionEngineSnapshotView { slot })
344 }
345
346 pub(super) fn has_engine(&self, family_id: ExtensionFamilyId, engine_id: &EngineId) -> bool {
347 self.by_family_engine
348 .contains_key(&(family_id, engine_id.clone()))
349 }
350
351 pub(super) fn has_module_identical(&self, module: &Arc<dyn ExtensionModule>) -> bool {
357 self.modules
358 .get(module.module_id())
359 .is_some_and(|installed| Arc::ptr_eq(installed, module))
360 }
361
362 pub(super) fn has_module_engine(
368 &self,
369 module_id: &ExtensionModuleId,
370 family_id: ExtensionFamilyId,
371 engine_id: &EngineId,
372 ) -> bool {
373 self.extension_engine_slot(family_id, engine_id)
374 .is_some_and(|slot| &slot.module_id == module_id)
375 }
376
377 fn extension_engine_slot(
378 &self,
379 family_id: ExtensionFamilyId,
380 engine_id: &EngineId,
381 ) -> Option<&FrozenExtensionEngineSlot> {
382 self.by_family_engine
383 .get(&(family_id, engine_id.clone()))
384 .map(|&index| &self.engines[index])
385 }
386}
387
388impl fmt::Debug for FrozenExtensionSlots {
389 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
390 formatter
391 .debug_struct("FrozenExtensionSlots")
392 .field("module_count", &self.modules.len())
393 .field("engine_count", &self.engines.len())
394 .field("index_count", &self.by_family_engine.len())
395 .field("owner_count", &self.owners.len())
396 .finish_non_exhaustive()
397 }
398}
399
400pub(super) fn freeze_extension_slots(
401 modules: BTreeMap<ExtensionModuleId, BoundCandidateModuleRecord>,
402) -> Result<FrozenExtensionSlots, RuntimeConfigError> {
403 let mut frozen_modules = BTreeMap::new();
404 let mut engines = Vec::new();
405 let mut by_family_engine = BTreeMap::new();
406 let mut owners = BTreeMap::new();
407
408 for (module_id, module) in modules {
409 frozen_modules.insert(module_id.clone(), Arc::clone(&module.module));
410 for (owner_id, owner) in module.owners {
411 owners.insert((module_id.clone(), owner_id), owner);
412 }
413 for ((family_id, engine_id), record) in module.engines {
414 if by_family_engine
415 .insert((family_id, engine_id.clone()), engines.len())
416 .is_some()
417 {
418 return Err(RuntimeConfigError::ConflictingRegistration {
419 key: RegistrationKey::ExtensionEngine {
420 family: family_id,
421 engine: engine_id,
422 },
423 });
424 }
425 let config = module.configs.get(&engine_id).cloned().ok_or_else(|| {
426 RuntimeConfigError::ExtensionModule {
427 source: ExtensionModuleError::MissingPlanningConfig {
428 module_id: module_id.clone(),
429 engine_id: engine_id.clone(),
430 },
431 }
432 })?;
433 engines.push(FrozenExtensionEngineSlot {
434 module_id: module_id.clone(),
435 family_id,
436 engine_id: engine_id.clone(),
437 context_identity: record.engine.context_identity(),
438 registration_identity: record.identity,
439 engine: record.engine,
440 config,
441 });
442 }
443 }
444
445 Ok(FrozenExtensionSlots {
446 modules: frozen_modules,
447 engines: engines.into(),
448 by_family_engine,
449 owners,
450 })
451}
452
453struct ExtensionRegistrationTransaction {
454 module_id: ExtensionModuleId,
455 engines: BTreeMap<(ExtensionFamilyId, EngineId), Arc<dyn ExtensionEngine>>,
456 configs: BTreeMap<EngineId, Arc<dyn ExtensionPlanningConfig>>,
457 owners: BTreeMap<CacheOwnerId, Arc<dyn RuntimeCacheOwner>>,
458}
459
460impl ExtensionRegistrationTransaction {
461 fn new(module_id: ExtensionModuleId) -> Self {
462 Self {
463 module_id,
464 engines: BTreeMap::new(),
465 configs: BTreeMap::new(),
466 owners: BTreeMap::new(),
467 }
468 }
469
470 fn into_candidate(
471 self,
472 module: Arc<dyn ExtensionModule>,
473 ) -> Result<CandidateModuleRecord, ExtensionModuleError> {
474 self.validate()?;
475 let engines = self
476 .engines
477 .into_iter()
478 .map(|(key, engine)| {
479 (
480 key,
481 CandidateExtensionEngine {
482 engine,
483 identity: CandidateRegistrationIdentity::New,
484 },
485 )
486 })
487 .collect();
488 Ok(CandidateModuleRecord {
489 module,
490 engines,
491 configs: self.configs,
492 owners: self.owners,
493 })
494 }
495
496 fn validate(&self) -> Result<(), ExtensionModuleError> {
497 for &(family_id, ref engine_id) in self.engines.keys() {
498 match self.configs.get(engine_id) {
499 Some(config) if config.family_id() == family_id => {}
500 Some(config) => {
501 return Err(ExtensionModuleError::PlanningConfigFamilyMismatch {
502 module_id: self.module_id.clone(),
503 engine_id: engine_id.clone(),
504 expected: family_id,
505 actual: config.family_id(),
506 });
507 }
508 None => {
509 return Err(ExtensionModuleError::MissingPlanningConfig {
510 module_id: self.module_id.clone(),
511 engine_id: engine_id.clone(),
512 });
513 }
514 }
515 }
516 for engine_id in self.configs.keys() {
517 if engine_match_count(&self.engines, engine_id) != 1 {
518 return Err(ExtensionModuleError::PlanningConfigWithoutEngine {
519 module_id: self.module_id.clone(),
520 engine_id: engine_id.clone(),
521 });
522 }
523 }
524 Ok(())
525 }
526}
527
528pub(super) fn bind_candidate_module(
529 module: CandidateModuleRecord,
530 allocate_identity: &mut impl FnMut() -> Result<RegistrationIdentity, RuntimeConfigError>,
531) -> Result<BoundCandidateModuleRecord, RuntimeConfigError> {
532 let engines = module
533 .engines
534 .into_iter()
535 .map(|(key, record)| {
536 let identity = match record.identity {
537 CandidateRegistrationIdentity::New => allocate_identity()?,
538 CandidateRegistrationIdentity::Preserved(identity) => identity,
539 };
540 Ok((
541 key,
542 BoundCandidateExtensionEngine {
543 engine: record.engine,
544 identity,
545 },
546 ))
547 })
548 .collect::<Result<BTreeMap<_, _>, RuntimeConfigError>>()?;
549 Ok(BoundCandidateModuleRecord {
550 module: module.module,
551 engines,
552 configs: module.configs,
553 owners: module.owners,
554 })
555}
556
557pub struct ExtensionModuleRegistrar<'a> {
559 transaction: &'a mut ExtensionRegistrationTransaction,
560}
561
562impl ExtensionModuleRegistrar<'_> {
563 pub fn register_engine(
570 &mut self,
571 engine: Arc<dyn ExtensionEngine>,
572 ) -> Result<(), ExtensionModuleError> {
573 let key = (engine.family_id(), engine.engine_id().clone());
574 match self.transaction.engines.get(&key) {
575 Some(existing) if Arc::ptr_eq(existing, &engine) => Ok(()),
576 Some(_) => Err(ExtensionModuleError::ConflictingEngine {
577 module_id: self.transaction.module_id.clone(),
578 family_id: key.0,
579 engine_id: key.1,
580 }),
581 None => {
582 self.transaction.engines.insert(key, engine);
583 Ok(())
584 }
585 }
586 }
587
588 pub fn register_planning_config(
595 &mut self,
596 engine_id: EngineId,
597 config: Arc<dyn ExtensionPlanningConfig>,
598 ) -> Result<(), ExtensionModuleError> {
599 let Some((family_id, _)) = unique_engine_for_config(&self.transaction.engines, &engine_id)
600 else {
601 return Err(ExtensionModuleError::PlanningConfigWithoutEngine {
602 module_id: self.transaction.module_id.clone(),
603 engine_id,
604 });
605 };
606 if config.family_id() != family_id {
607 return Err(ExtensionModuleError::PlanningConfigFamilyMismatch {
608 module_id: self.transaction.module_id.clone(),
609 engine_id,
610 expected: family_id,
611 actual: config.family_id(),
612 });
613 }
614
615 match self.transaction.configs.get(&engine_id) {
616 Some(existing) if config_payloads_equal(existing.as_ref(), config.as_ref()) => Ok(()),
617 Some(_) => Err(ExtensionModuleError::ConflictingPlanningConfig {
618 module_id: self.transaction.module_id.clone(),
619 engine_id,
620 }),
621 None => {
622 self.transaction.configs.insert(engine_id, config);
623 Ok(())
624 }
625 }
626 }
627
628 pub fn register_cache_owner(
635 &mut self,
636 id: CacheOwnerId,
637 owner: Arc<dyn RuntimeCacheOwner>,
638 ) -> Result<(), ExtensionModuleError> {
639 match self.transaction.owners.get(&id) {
640 Some(existing) if Arc::ptr_eq(existing, &owner) => Ok(()),
641 Some(_) => Err(ExtensionModuleError::ConflictingCacheOwner {
642 module_id: self.transaction.module_id.clone(),
643 owner: id,
644 }),
645 None => {
646 self.transaction.owners.insert(id, owner);
647 Ok(())
648 }
649 }
650 }
651}
652
653impl fmt::Debug for ExtensionModuleRegistrar<'_> {
654 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
655 formatter
656 .debug_struct("ExtensionModuleRegistrar")
657 .field("module_id", &self.transaction.module_id)
658 .field("engine_count", &self.transaction.engines.len())
659 .field("config_count", &self.transaction.configs.len())
660 .field("owner_count", &self.transaction.owners.len())
661 .finish_non_exhaustive()
662 }
663}
664
665pub(super) fn configure_module(
666 module: Arc<dyn ExtensionModule>,
667) -> Result<CandidateModuleRecord, ExtensionModuleError> {
668 let module_id = module.module_id().clone();
669 let mut transaction = ExtensionRegistrationTransaction::new(module_id);
670 {
671 let mut registrar = ExtensionModuleRegistrar {
672 transaction: &mut transaction,
673 };
674 module.configure(&mut registrar)?;
675 }
676 transaction.into_candidate(module)
677}
678
679pub(super) fn extension_cache_owner_id(
680 module_id: &ExtensionModuleId,
681 local: &CacheOwnerId,
682) -> CacheOwnerId {
683 let module = module_id.as_str();
684 let local = local.as_str();
685 CacheOwnerId::from_canonical_owner_id(Arc::<str>::from(format!(
686 "extension[{}]:{module}[{}]:{local}",
687 module.len(),
688 local.len(),
689 )))
690}
691
692fn unique_engine_for_config(
693 engines: &BTreeMap<(ExtensionFamilyId, EngineId), Arc<dyn ExtensionEngine>>,
694 engine_id: &EngineId,
695) -> Option<(ExtensionFamilyId, EngineId)> {
696 let mut matches = engines
697 .keys()
698 .filter(|(_, candidate_engine)| candidate_engine == engine_id);
699 let first = matches.next()?;
700 matches.next().is_none().then(|| (first.0, first.1.clone()))
701}
702
703fn engine_match_count(
704 engines: &BTreeMap<(ExtensionFamilyId, EngineId), Arc<dyn ExtensionEngine>>,
705 engine_id: &EngineId,
706) -> usize {
707 engines
708 .keys()
709 .filter(|(_, candidate_engine)| candidate_engine == engine_id)
710 .count()
711}
712
713fn config_payloads_equal(
714 left: &dyn ExtensionPlanningConfig,
715 right: &dyn ExtensionPlanningConfig,
716) -> bool {
717 payload_hash(left) == payload_hash(right) && left.payload_eq(right)
718}
719
720fn payload_hash(config: &dyn ExtensionPlanningConfig) -> u64 {
721 let mut hasher = DefaultHasher::new();
722 config.payload_hash(&mut hasher);
723 hasher.finish()
724}