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 fn extension_engine_slot(
347 &self,
348 family_id: ExtensionFamilyId,
349 engine_id: &EngineId,
350 ) -> Option<&FrozenExtensionEngineSlot> {
351 self.by_family_engine
352 .get(&(family_id, engine_id.clone()))
353 .map(|&index| &self.engines[index])
354 }
355}
356
357impl fmt::Debug for FrozenExtensionSlots {
358 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
359 formatter
360 .debug_struct("FrozenExtensionSlots")
361 .field("module_count", &self.modules.len())
362 .field("engine_count", &self.engines.len())
363 .field("index_count", &self.by_family_engine.len())
364 .field("owner_count", &self.owners.len())
365 .finish_non_exhaustive()
366 }
367}
368
369pub(super) fn freeze_extension_slots(
370 modules: BTreeMap<ExtensionModuleId, BoundCandidateModuleRecord>,
371) -> Result<FrozenExtensionSlots, RuntimeConfigError> {
372 let mut frozen_modules = BTreeMap::new();
373 let mut engines = Vec::new();
374 let mut by_family_engine = BTreeMap::new();
375 let mut owners = BTreeMap::new();
376
377 for (module_id, module) in modules {
378 frozen_modules.insert(module_id.clone(), Arc::clone(&module.module));
379 for (owner_id, owner) in module.owners {
380 owners.insert((module_id.clone(), owner_id), owner);
381 }
382 for ((family_id, engine_id), record) in module.engines {
383 if by_family_engine
384 .insert((family_id, engine_id.clone()), engines.len())
385 .is_some()
386 {
387 return Err(RuntimeConfigError::ConflictingRegistration {
388 key: RegistrationKey::ExtensionEngine {
389 family: family_id,
390 engine: engine_id,
391 },
392 });
393 }
394 let config = module.configs.get(&engine_id).cloned().ok_or_else(|| {
395 RuntimeConfigError::ExtensionModule {
396 source: ExtensionModuleError::MissingPlanningConfig {
397 module_id: module_id.clone(),
398 engine_id: engine_id.clone(),
399 },
400 }
401 })?;
402 engines.push(FrozenExtensionEngineSlot {
403 module_id: module_id.clone(),
404 family_id,
405 engine_id: engine_id.clone(),
406 context_identity: record.engine.context_identity(),
407 registration_identity: record.identity,
408 engine: record.engine,
409 config,
410 });
411 }
412 }
413
414 Ok(FrozenExtensionSlots {
415 modules: frozen_modules,
416 engines: engines.into(),
417 by_family_engine,
418 owners,
419 })
420}
421
422struct ExtensionRegistrationTransaction {
423 module_id: ExtensionModuleId,
424 engines: BTreeMap<(ExtensionFamilyId, EngineId), Arc<dyn ExtensionEngine>>,
425 configs: BTreeMap<EngineId, Arc<dyn ExtensionPlanningConfig>>,
426 owners: BTreeMap<CacheOwnerId, Arc<dyn RuntimeCacheOwner>>,
427}
428
429impl ExtensionRegistrationTransaction {
430 fn new(module_id: ExtensionModuleId) -> Self {
431 Self {
432 module_id,
433 engines: BTreeMap::new(),
434 configs: BTreeMap::new(),
435 owners: BTreeMap::new(),
436 }
437 }
438
439 fn into_candidate(
440 self,
441 module: Arc<dyn ExtensionModule>,
442 ) -> Result<CandidateModuleRecord, ExtensionModuleError> {
443 self.validate()?;
444 let engines = self
445 .engines
446 .into_iter()
447 .map(|(key, engine)| {
448 (
449 key,
450 CandidateExtensionEngine {
451 engine,
452 identity: CandidateRegistrationIdentity::New,
453 },
454 )
455 })
456 .collect();
457 Ok(CandidateModuleRecord {
458 module,
459 engines,
460 configs: self.configs,
461 owners: self.owners,
462 })
463 }
464
465 fn validate(&self) -> Result<(), ExtensionModuleError> {
466 for &(family_id, ref engine_id) in self.engines.keys() {
467 match self.configs.get(engine_id) {
468 Some(config) if config.family_id() == family_id => {}
469 Some(config) => {
470 return Err(ExtensionModuleError::PlanningConfigFamilyMismatch {
471 module_id: self.module_id.clone(),
472 engine_id: engine_id.clone(),
473 expected: family_id,
474 actual: config.family_id(),
475 });
476 }
477 None => {
478 return Err(ExtensionModuleError::MissingPlanningConfig {
479 module_id: self.module_id.clone(),
480 engine_id: engine_id.clone(),
481 });
482 }
483 }
484 }
485 for engine_id in self.configs.keys() {
486 if engine_match_count(&self.engines, engine_id) != 1 {
487 return Err(ExtensionModuleError::PlanningConfigWithoutEngine {
488 module_id: self.module_id.clone(),
489 engine_id: engine_id.clone(),
490 });
491 }
492 }
493 Ok(())
494 }
495}
496
497pub(super) fn bind_candidate_module(
498 module: CandidateModuleRecord,
499 allocate_identity: &mut impl FnMut() -> Result<RegistrationIdentity, RuntimeConfigError>,
500) -> Result<BoundCandidateModuleRecord, RuntimeConfigError> {
501 let engines = module
502 .engines
503 .into_iter()
504 .map(|(key, record)| {
505 let identity = match record.identity {
506 CandidateRegistrationIdentity::New => allocate_identity()?,
507 CandidateRegistrationIdentity::Preserved(identity) => identity,
508 };
509 Ok((
510 key,
511 BoundCandidateExtensionEngine {
512 engine: record.engine,
513 identity,
514 },
515 ))
516 })
517 .collect::<Result<BTreeMap<_, _>, RuntimeConfigError>>()?;
518 Ok(BoundCandidateModuleRecord {
519 module: module.module,
520 engines,
521 configs: module.configs,
522 owners: module.owners,
523 })
524}
525
526pub struct ExtensionModuleRegistrar<'a> {
528 transaction: &'a mut ExtensionRegistrationTransaction,
529}
530
531impl ExtensionModuleRegistrar<'_> {
532 pub fn register_engine(
539 &mut self,
540 engine: Arc<dyn ExtensionEngine>,
541 ) -> Result<(), ExtensionModuleError> {
542 let key = (engine.family_id(), engine.engine_id().clone());
543 match self.transaction.engines.get(&key) {
544 Some(existing) if Arc::ptr_eq(existing, &engine) => Ok(()),
545 Some(_) => Err(ExtensionModuleError::ConflictingEngine {
546 module_id: self.transaction.module_id.clone(),
547 family_id: key.0,
548 engine_id: key.1,
549 }),
550 None => {
551 self.transaction.engines.insert(key, engine);
552 Ok(())
553 }
554 }
555 }
556
557 pub fn register_planning_config(
564 &mut self,
565 engine_id: EngineId,
566 config: Arc<dyn ExtensionPlanningConfig>,
567 ) -> Result<(), ExtensionModuleError> {
568 let Some((family_id, _)) = unique_engine_for_config(&self.transaction.engines, &engine_id)
569 else {
570 return Err(ExtensionModuleError::PlanningConfigWithoutEngine {
571 module_id: self.transaction.module_id.clone(),
572 engine_id,
573 });
574 };
575 if config.family_id() != family_id {
576 return Err(ExtensionModuleError::PlanningConfigFamilyMismatch {
577 module_id: self.transaction.module_id.clone(),
578 engine_id,
579 expected: family_id,
580 actual: config.family_id(),
581 });
582 }
583
584 match self.transaction.configs.get(&engine_id) {
585 Some(existing) if config_payloads_equal(existing.as_ref(), config.as_ref()) => Ok(()),
586 Some(_) => Err(ExtensionModuleError::ConflictingPlanningConfig {
587 module_id: self.transaction.module_id.clone(),
588 engine_id,
589 }),
590 None => {
591 self.transaction.configs.insert(engine_id, config);
592 Ok(())
593 }
594 }
595 }
596
597 pub fn register_cache_owner(
604 &mut self,
605 id: CacheOwnerId,
606 owner: Arc<dyn RuntimeCacheOwner>,
607 ) -> Result<(), ExtensionModuleError> {
608 match self.transaction.owners.get(&id) {
609 Some(existing) if Arc::ptr_eq(existing, &owner) => Ok(()),
610 Some(_) => Err(ExtensionModuleError::ConflictingCacheOwner {
611 module_id: self.transaction.module_id.clone(),
612 owner: id,
613 }),
614 None => {
615 self.transaction.owners.insert(id, owner);
616 Ok(())
617 }
618 }
619 }
620}
621
622impl fmt::Debug for ExtensionModuleRegistrar<'_> {
623 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
624 formatter
625 .debug_struct("ExtensionModuleRegistrar")
626 .field("module_id", &self.transaction.module_id)
627 .field("engine_count", &self.transaction.engines.len())
628 .field("config_count", &self.transaction.configs.len())
629 .field("owner_count", &self.transaction.owners.len())
630 .finish_non_exhaustive()
631 }
632}
633
634pub(super) fn configure_module(
635 module: Arc<dyn ExtensionModule>,
636) -> Result<CandidateModuleRecord, ExtensionModuleError> {
637 let module_id = module.module_id().clone();
638 let mut transaction = ExtensionRegistrationTransaction::new(module_id);
639 {
640 let mut registrar = ExtensionModuleRegistrar {
641 transaction: &mut transaction,
642 };
643 module.configure(&mut registrar)?;
644 }
645 transaction.into_candidate(module)
646}
647
648pub(super) fn extension_cache_owner_id(
649 module_id: &ExtensionModuleId,
650 local: &CacheOwnerId,
651) -> CacheOwnerId {
652 let module = module_id.as_str();
653 let local = local.as_str();
654 CacheOwnerId::from_canonical_owner_id(Arc::<str>::from(format!(
655 "extension[{}]:{module}[{}]:{local}",
656 module.len(),
657 local.len(),
658 )))
659}
660
661fn unique_engine_for_config(
662 engines: &BTreeMap<(ExtensionFamilyId, EngineId), Arc<dyn ExtensionEngine>>,
663 engine_id: &EngineId,
664) -> Option<(ExtensionFamilyId, EngineId)> {
665 let mut matches = engines
666 .keys()
667 .filter(|(_, candidate_engine)| candidate_engine == engine_id);
668 let first = matches.next()?;
669 matches.next().is_none().then(|| (first.0, first.1.clone()))
670}
671
672fn engine_match_count(
673 engines: &BTreeMap<(ExtensionFamilyId, EngineId), Arc<dyn ExtensionEngine>>,
674 engine_id: &EngineId,
675) -> usize {
676 engines
677 .keys()
678 .filter(|(_, candidate_engine)| candidate_engine == engine_id)
679 .count()
680}
681
682fn config_payloads_equal(
683 left: &dyn ExtensionPlanningConfig,
684 right: &dyn ExtensionPlanningConfig,
685) -> bool {
686 payload_hash(left) == payload_hash(right) && left.payload_eq(right)
687}
688
689fn payload_hash(config: &dyn ExtensionPlanningConfig) -> u64 {
690 let mut hasher = DefaultHasher::new();
691 config.payload_hash(&mut hasher);
692 hasher.finish()
693}