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