Skip to main content

a3s_boot/provider/
module_ref.rs

1use super::{AnyProvider, FromModuleRef, ProviderDefinition, ProviderScope, ProviderToken};
2use crate::{BootError, Result};
3use std::collections::BTreeMap;
4use std::fmt;
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, RwLock};
7
8/// Runtime provider container. This is Boot's Rust equivalent of Nest's ModuleRef.
9#[derive(Clone, Default)]
10pub struct ModuleRef {
11    providers: Arc<RwLock<BTreeMap<ProviderToken, ProviderEntry>>>,
12    provider_order: Arc<RwLock<Vec<ProviderToken>>>,
13    visible_scopes: Arc<RwLock<Vec<ModuleRef>>>,
14    request_cache: Option<ProviderCache>,
15    resolution_stack: Option<ProviderResolutionStack>,
16}
17
18type ProviderCache = Arc<RwLock<BTreeMap<ProviderCacheKey, Arc<AnyProvider>>>>;
19type ProviderResolutionStack = Arc<RwLock<Vec<ProviderToken>>>;
20
21static NEXT_PROVIDER_CACHE_KEY: AtomicU64 = AtomicU64::new(1);
22
23fn new_resolution_stack() -> ProviderResolutionStack {
24    Arc::new(RwLock::new(Vec::new()))
25}
26
27fn enter_resolution_stack(
28    resolution_stack: &ProviderResolutionStack,
29    token: &ProviderToken,
30) -> Result<()> {
31    let mut stack = resolution_stack.write().map_err(|_| {
32        BootError::Internal("provider resolution stack lock is poisoned".to_string())
33    })?;
34    if let Some(index) = stack.iter().position(|active| active == token) {
35        let mut chain = stack[index..].to_vec();
36        chain.push(token.clone());
37        let chain = chain
38            .iter()
39            .map(ToString::to_string)
40            .collect::<Vec<_>>()
41            .join(" -> ");
42        return Err(BootError::Internal(format!(
43            "cyclic provider dependency detected: {chain}"
44        )));
45    }
46
47    stack.push(token.clone());
48    Ok(())
49}
50
51fn exit_resolution_stack(resolution_stack: &ProviderResolutionStack) -> Result<()> {
52    let mut stack = resolution_stack.write().map_err(|_| {
53        BootError::Internal("provider resolution stack lock is poisoned".to_string())
54    })?;
55    stack
56        .pop()
57        .ok_or_else(|| BootError::Internal("provider resolution stack underflow".to_string()))?;
58    Ok(())
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
62struct ProviderCacheKey(u64);
63
64impl ProviderCacheKey {
65    fn next() -> Self {
66        Self(NEXT_PROVIDER_CACHE_KEY.fetch_add(1, Ordering::Relaxed))
67    }
68}
69
70#[derive(Clone)]
71struct ProviderEntry {
72    cache_key: ProviderCacheKey,
73    definition: ProviderDefinition,
74    singleton: ProviderCache,
75    owner: Option<ModuleRef>,
76}
77
78impl ProviderEntry {
79    fn new(definition: ProviderDefinition) -> Self {
80        Self {
81            cache_key: ProviderCacheKey::next(),
82            definition,
83            singleton: Arc::new(RwLock::new(BTreeMap::new())),
84            owner: None,
85        }
86    }
87
88    fn with_owner(mut self, owner: ModuleRef) -> Self {
89        if self.owner.is_none() {
90            self.owner = Some(owner);
91        }
92        self
93    }
94
95    fn scope(&self) -> ProviderScope {
96        self.definition.scope()
97    }
98
99    fn is_local_singleton(&self) -> bool {
100        self.scope() == ProviderScope::Singleton && !self.definition.is_alias()
101    }
102
103    fn is_async_factory(&self) -> bool {
104        self.definition.is_async_factory()
105    }
106
107    fn resolve(
108        &self,
109        module_ref: &ModuleRef,
110        request_cache: Option<ProviderCache>,
111        resolution_stack: &ProviderResolutionStack,
112        alias_path: &mut Vec<ProviderToken>,
113    ) -> Result<Arc<AnyProvider>> {
114        let base_ref = self.owner.as_ref().unwrap_or(module_ref);
115        if let Some(target) = self.definition.alias_target() {
116            return self.resolve_alias(
117                base_ref,
118                target,
119                request_cache,
120                resolution_stack,
121                alias_path,
122            );
123        }
124
125        match self.scope() {
126            ProviderScope::Singleton => self.resolve_singleton(base_ref, resolution_stack),
127            ProviderScope::Transient => {
128                let factory_ref =
129                    self.factory_ref(base_ref, request_cache.clone(), resolution_stack);
130                self.build_with_resolution_stack(&factory_ref, resolution_stack)
131            }
132            ProviderScope::Request => {
133                let factory_ref =
134                    self.factory_ref(base_ref, request_cache.clone(), resolution_stack);
135                self.resolve_request(&factory_ref, request_cache, resolution_stack)
136            }
137        }
138    }
139
140    fn resolve_alias(
141        &self,
142        module_ref: &ModuleRef,
143        target: &ProviderToken,
144        request_cache: Option<ProviderCache>,
145        resolution_stack: &ProviderResolutionStack,
146        alias_path: &mut Vec<ProviderToken>,
147    ) -> Result<Arc<AnyProvider>> {
148        if alias_path.contains(self.definition.token()) {
149            alias_path.push(self.definition.token().clone());
150            let chain = alias_path
151                .iter()
152                .map(ToString::to_string)
153                .collect::<Vec<_>>()
154                .join(" -> ");
155            return Err(BootError::Internal(format!(
156                "cyclic provider alias detected: {chain}"
157            )));
158        }
159
160        alias_path.push(self.definition.token().clone());
161        let value = module_ref.get_any_with_request_cache_inner(
162            target,
163            request_cache,
164            resolution_stack,
165            alias_path,
166        )?;
167        alias_path.pop();
168
169        value.ok_or_else(|| BootError::MissingProvider(target.to_string()))
170    }
171
172    fn resolve_singleton(
173        &self,
174        module_ref: &ModuleRef,
175        resolution_stack: &ProviderResolutionStack,
176    ) -> Result<Arc<AnyProvider>> {
177        if let Some(value) = self
178            .read_cache(&self.singleton)?
179            .get(&self.cache_key)
180            .cloned()
181        {
182            return Ok(value);
183        }
184
185        let factory_ref = module_ref.with_resolution_stack(Arc::clone(resolution_stack));
186        let value = self.build_with_resolution_stack(&factory_ref, resolution_stack)?;
187        self.write_cache(&self.singleton)?
188            .insert(self.cache_key, Arc::clone(&value));
189        Ok(value)
190    }
191
192    fn resolve_request(
193        &self,
194        module_ref: &ModuleRef,
195        request_cache: Option<ProviderCache>,
196        resolution_stack: &ProviderResolutionStack,
197    ) -> Result<Arc<AnyProvider>> {
198        let Some(request_cache) = request_cache else {
199            return self.build_with_resolution_stack(module_ref, resolution_stack);
200        };
201
202        if let Some(value) = self
203            .read_cache(&request_cache)?
204            .get(&self.cache_key)
205            .cloned()
206        {
207            return Ok(value);
208        }
209
210        let value = self.build_with_resolution_stack(module_ref, resolution_stack)?;
211        self.write_cache(&request_cache)?
212            .insert(self.cache_key, Arc::clone(&value));
213        Ok(value)
214    }
215
216    async fn seed_singleton_async(&self, module_ref: ModuleRef) -> Result<()> {
217        let resolution_stack = new_resolution_stack();
218        let module_ref = module_ref.with_resolution_stack(Arc::clone(&resolution_stack));
219        enter_resolution_stack(&resolution_stack, self.definition.token())?;
220        let result = self.definition.build_async(module_ref).await;
221        let exit_result = exit_resolution_stack(&resolution_stack);
222        let value = match (result, exit_result) {
223            (Ok(value), Ok(())) => value,
224            (Err(error), _) => return Err(error),
225            (Ok(_), Err(error)) => return Err(error),
226        };
227        self.seed_singleton(value)
228    }
229
230    fn seed_singleton(&self, value: Arc<AnyProvider>) -> Result<()> {
231        self.write_cache(&self.singleton)?
232            .insert(self.cache_key, value);
233        Ok(())
234    }
235
236    fn on_module_init(&self, module_ref: &ModuleRef) -> Result<()> {
237        let Some(hook) = self.definition.lifecycle().on_module_init() else {
238            return Ok(());
239        };
240
241        let resolution_stack = new_resolution_stack();
242        let value = self.resolve_singleton(module_ref, &resolution_stack)?;
243        hook(value, module_ref)
244    }
245
246    async fn on_application_bootstrap(&self, module_ref: ModuleRef) -> Result<()> {
247        let Some(hook) = self.definition.lifecycle().on_application_bootstrap() else {
248            return Ok(());
249        };
250
251        let resolution_stack = new_resolution_stack();
252        let value = self.resolve_singleton(&module_ref, &resolution_stack)?;
253        hook(value, module_ref).await
254    }
255
256    async fn on_application_shutdown(&self, module_ref: ModuleRef) -> Result<()> {
257        let Some(hook) = self.definition.lifecycle().on_application_shutdown() else {
258            return Ok(());
259        };
260
261        let resolution_stack = new_resolution_stack();
262        let value = self.resolve_singleton(&module_ref, &resolution_stack)?;
263        hook(value, module_ref).await
264    }
265
266    fn factory_ref(
267        &self,
268        module_ref: &ModuleRef,
269        request_cache: Option<ProviderCache>,
270        resolution_stack: &ProviderResolutionStack,
271    ) -> ModuleRef {
272        let factory_ref = module_ref.with_resolution_stack(Arc::clone(resolution_stack));
273        match request_cache {
274            Some(request_cache) => factory_ref.with_request_cache(request_cache),
275            None => factory_ref,
276        }
277    }
278
279    fn build_with_resolution_stack(
280        &self,
281        module_ref: &ModuleRef,
282        resolution_stack: &ProviderResolutionStack,
283    ) -> Result<Arc<AnyProvider>> {
284        enter_resolution_stack(resolution_stack, self.definition.token())?;
285        let result = self.definition.build(module_ref);
286        let exit_result = exit_resolution_stack(resolution_stack);
287        match (result, exit_result) {
288            (Ok(value), Ok(())) => Ok(value),
289            (Err(error), _) => Err(error),
290            (Ok(_), Err(error)) => Err(error),
291        }
292    }
293
294    fn read_cache<'a>(
295        &self,
296        cache: &'a ProviderCache,
297    ) -> Result<std::sync::RwLockReadGuard<'a, BTreeMap<ProviderCacheKey, Arc<AnyProvider>>>> {
298        cache
299            .read()
300            .map_err(|_| BootError::Internal("provider cache lock is poisoned".to_string()))
301    }
302
303    fn write_cache<'a>(
304        &self,
305        cache: &'a ProviderCache,
306    ) -> Result<std::sync::RwLockWriteGuard<'a, BTreeMap<ProviderCacheKey, Arc<AnyProvider>>>> {
307        cache
308            .write()
309            .map_err(|_| BootError::Internal("provider cache lock is poisoned".to_string()))
310    }
311}
312
313impl fmt::Debug for ModuleRef {
314    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315        let len = self
316            .providers
317            .read()
318            .map(|providers| providers.len())
319            .unwrap_or(0);
320        let visible = self
321            .visible_scopes
322            .read()
323            .map(|scopes| scopes.len())
324            .unwrap_or(0);
325        f.debug_struct("ModuleRef")
326            .field("providers", &len)
327            .field("visible_scopes", &visible)
328            .finish()
329    }
330}
331
332impl ModuleRef {
333    pub fn new() -> Self {
334        Self::default()
335    }
336
337    pub fn request_scope(&self) -> Self {
338        self.with_request_cache(Arc::new(RwLock::new(BTreeMap::new())))
339    }
340
341    fn with_request_cache(&self, request_cache: ProviderCache) -> Self {
342        Self {
343            providers: Arc::clone(&self.providers),
344            provider_order: Arc::clone(&self.provider_order),
345            visible_scopes: Arc::clone(&self.visible_scopes),
346            request_cache: Some(request_cache),
347            resolution_stack: self.resolution_stack.clone(),
348        }
349    }
350
351    fn with_resolution_stack(&self, resolution_stack: ProviderResolutionStack) -> Self {
352        Self {
353            providers: Arc::clone(&self.providers),
354            provider_order: Arc::clone(&self.provider_order),
355            visible_scopes: Arc::clone(&self.visible_scopes),
356            request_cache: self.request_cache.clone(),
357            resolution_stack: Some(resolution_stack),
358        }
359    }
360
361    pub fn register(&self, definition: ProviderDefinition) -> Result<()> {
362        let token = definition.token().clone();
363        self.validate_registration(&token, &definition)?;
364        if definition.is_async_factory() {
365            return Err(BootError::Internal(format!(
366                "async provider factory requires async registration: {token}"
367            )));
368        }
369
370        let entry = ProviderEntry::new(definition);
371        self.insert_entry(token, entry)
372    }
373
374    pub async fn register_async(&self, definition: ProviderDefinition) -> Result<()> {
375        let token = definition.token().clone();
376        self.validate_registration(&token, &definition)?;
377
378        let entry = ProviderEntry::new(definition);
379        self.insert_entry(token, entry)
380    }
381
382    pub fn insert<T>(&self, value: T) -> Result<()>
383    where
384        T: Send + Sync + 'static,
385    {
386        self.insert_arc(Arc::new(value))
387    }
388
389    pub fn insert_arc<T>(&self, value: Arc<T>) -> Result<()>
390    where
391        T: Send + Sync + 'static,
392    {
393        let token = ProviderToken::of::<T>();
394        let entry = ProviderEntry::new(ProviderDefinition::from_arc(value));
395        self.insert_entry(token, entry)
396    }
397
398    pub fn get<T>(&self) -> Result<Arc<T>>
399    where
400        T: Send + Sync + 'static,
401    {
402        self.get_token::<T>(&ProviderToken::of::<T>())
403    }
404
405    pub fn get_named<T>(&self, token: &str) -> Result<Arc<T>>
406    where
407        T: Send + Sync + 'static,
408    {
409        self.get_token::<T>(&ProviderToken::named(token))
410    }
411
412    pub fn get_optional<T>(&self) -> Result<Option<Arc<T>>>
413    where
414        T: Send + Sync + 'static,
415    {
416        self.get_optional_token::<T>(&ProviderToken::of::<T>())
417    }
418
419    pub fn get_optional_named<T>(&self, token: &str) -> Result<Option<Arc<T>>>
420    where
421        T: Send + Sync + 'static,
422    {
423        self.get_optional_token::<T>(&ProviderToken::named(token))
424    }
425
426    /// Resolve a typed provider in a fresh resolution context.
427    ///
428    /// This mirrors Nest's `ModuleRef.resolve(...)`: singleton providers reuse
429    /// their application instance, while request-scoped dependencies share one
430    /// temporary context for this resolution and transient providers are rebuilt.
431    pub fn resolve<T>(&self) -> Result<Arc<T>>
432    where
433        T: Send + Sync + 'static,
434    {
435        self.resolve_token::<T>(&ProviderToken::of::<T>())
436    }
437
438    /// Resolve a named provider in a fresh resolution context.
439    pub fn resolve_named<T>(&self, token: &str) -> Result<Arc<T>>
440    where
441        T: Send + Sync + 'static,
442    {
443        self.resolve_token::<T>(&ProviderToken::named(token))
444    }
445
446    /// Resolve a typed provider in a fresh resolution context when it exists.
447    pub fn resolve_optional<T>(&self) -> Result<Option<Arc<T>>>
448    where
449        T: Send + Sync + 'static,
450    {
451        self.resolve_optional_token::<T>(&ProviderToken::of::<T>())
452    }
453
454    /// Resolve a named provider in a fresh resolution context when it exists.
455    pub fn resolve_optional_named<T>(&self, token: &str) -> Result<Option<Arc<T>>>
456    where
457        T: Send + Sync + 'static,
458    {
459        self.resolve_optional_token::<T>(&ProviderToken::named(token))
460    }
461
462    /// Create an injectable value without registering it in the provider graph.
463    pub fn create<T>(&self) -> Result<T>
464    where
465        T: FromModuleRef,
466    {
467        T::from_module_ref(self)
468    }
469
470    /// Create an injectable `Arc<T>` without registering it in the provider graph.
471    pub fn create_arc<T>(&self) -> Result<Arc<T>>
472    where
473        T: FromModuleRef,
474    {
475        Ok(Arc::new(self.create::<T>()?))
476    }
477
478    pub fn contains(&self, token: &ProviderToken) -> Result<bool> {
479        Ok(self.get_entry(token)?.is_some())
480    }
481
482    pub fn contains_provider<T>(&self) -> Result<bool>
483    where
484        T: Send + Sync + 'static,
485    {
486        self.contains(&ProviderToken::of::<T>())
487    }
488
489    pub fn contains_named(&self, token: &str) -> Result<bool> {
490        self.contains(&ProviderToken::named(token))
491    }
492
493    pub fn tokens(&self) -> Result<Vec<ProviderToken>> {
494        let mut tokens = BTreeMap::new();
495        self.collect_tokens(&mut tokens)?;
496        Ok(tokens.into_keys().collect())
497    }
498
499    fn insert_entry(&self, token: ProviderToken, entry: ProviderEntry) -> Result<()> {
500        let mut provider_order = self.write_provider_order()?;
501        let mut providers = self.write_providers()?;
502        if providers.contains_key(&token) {
503            return Err(BootError::DuplicateProvider(token.to_string()));
504        }
505        providers.insert(token.clone(), entry);
506        provider_order.push(token);
507        Ok(())
508    }
509
510    fn validate_registration(
511        &self,
512        token: &ProviderToken,
513        definition: &ProviderDefinition,
514    ) -> Result<()> {
515        if self.contains_local(token)? {
516            return Err(BootError::DuplicateProvider(token.to_string()));
517        }
518        if definition.is_async_factory() && definition.scope() != ProviderScope::Singleton {
519            return Err(BootError::Internal(format!(
520                "async provider factories require singleton scope: {token}"
521            )));
522        }
523        if definition.lifecycle().has_hooks() && definition.scope() != ProviderScope::Singleton {
524            return Err(BootError::Internal(format!(
525                "provider lifecycle hooks require singleton scope: {token}"
526            )));
527        }
528        if definition.lifecycle().has_hooks() && definition.is_alias() {
529            return Err(BootError::Internal(format!(
530                "provider aliases cannot define lifecycle hooks: {token}"
531            )));
532        }
533        Ok(())
534    }
535
536    pub(crate) fn add_visible_scope(&self, module_ref: ModuleRef) -> Result<()> {
537        self.write_visible_scopes()?.push(module_ref);
538        Ok(())
539    }
540
541    pub(crate) fn export_from(&self, module_ref: &ModuleRef, token: &ProviderToken) -> Result<()> {
542        let entry = module_ref
543            .get_entry(token)?
544            .ok_or_else(|| BootError::MissingProvider(token.to_string()))?;
545        self.insert_entry(token.clone(), entry.with_owner(module_ref.clone()))
546    }
547
548    pub(crate) fn local_tokens(&self) -> Result<Vec<ProviderToken>> {
549        Ok(self.read_provider_order()?.clone())
550    }
551
552    pub(crate) fn initialize_local_singletons(&self) -> Result<()> {
553        for entry in self.local_entries()? {
554            if entry.is_local_singleton() {
555                let resolution_stack = new_resolution_stack();
556                entry.resolve_singleton(self, &resolution_stack)?;
557            }
558        }
559        Ok(())
560    }
561
562    pub(crate) async fn initialize_local_singletons_async(&self) -> Result<()> {
563        for entry in self.local_entries()? {
564            if entry.is_local_singleton() && entry.is_async_factory() {
565                entry.seed_singleton_async(self.clone()).await?;
566            }
567        }
568
569        self.initialize_local_singletons()
570    }
571
572    pub(crate) fn initialize_local_providers(&self) -> Result<()> {
573        for entry in self.local_entries()? {
574            entry.on_module_init(self)?;
575        }
576        Ok(())
577    }
578
579    pub(crate) async fn bootstrap_local_providers(&self) -> Result<()> {
580        for entry in self.local_entries()? {
581            entry.on_application_bootstrap(self.clone()).await?;
582        }
583        Ok(())
584    }
585
586    pub(crate) async fn shutdown_local_providers(&self) -> Result<()> {
587        let mut entries = self.local_entries()?;
588        entries.reverse();
589        for entry in entries {
590            entry.on_application_shutdown(self.clone()).await?;
591        }
592        Ok(())
593    }
594
595    fn get_token<T>(&self, token: &ProviderToken) -> Result<Arc<T>>
596    where
597        T: Send + Sync + 'static,
598    {
599        let value = self
600            .get_any(token)?
601            .ok_or_else(|| BootError::MissingProvider(token.to_string()))?;
602
603        Arc::downcast::<T>(value).map_err(|_| BootError::ProviderTypeMismatch(token.to_string()))
604    }
605
606    fn get_optional_token<T>(&self, token: &ProviderToken) -> Result<Option<Arc<T>>>
607    where
608        T: Send + Sync + 'static,
609    {
610        let value = self.get_any(token)?;
611        match value {
612            Some(value) => Arc::downcast::<T>(value)
613                .map(Some)
614                .map_err(|_| BootError::ProviderTypeMismatch(token.to_string())),
615            None => Ok(None),
616        }
617    }
618
619    fn resolve_token<T>(&self, token: &ProviderToken) -> Result<Arc<T>>
620    where
621        T: Send + Sync + 'static,
622    {
623        self.request_scope().get_token(token)
624    }
625
626    fn resolve_optional_token<T>(&self, token: &ProviderToken) -> Result<Option<Arc<T>>>
627    where
628        T: Send + Sync + 'static,
629    {
630        self.request_scope().get_optional_token(token)
631    }
632
633    fn get_any(&self, token: &ProviderToken) -> Result<Option<Arc<AnyProvider>>> {
634        let mut alias_path = Vec::new();
635        let resolution_stack = self
636            .resolution_stack
637            .clone()
638            .unwrap_or_else(new_resolution_stack);
639        self.get_any_with_request_cache_inner(
640            token,
641            self.request_cache.clone(),
642            &resolution_stack,
643            &mut alias_path,
644        )
645    }
646
647    fn get_any_with_request_cache_inner(
648        &self,
649        token: &ProviderToken,
650        request_cache: Option<ProviderCache>,
651        resolution_stack: &ProviderResolutionStack,
652        alias_path: &mut Vec<ProviderToken>,
653    ) -> Result<Option<Arc<AnyProvider>>> {
654        if let Some(entry) = self.read_providers()?.get(token).cloned() {
655            return entry
656                .resolve(self, request_cache, resolution_stack, alias_path)
657                .map(Some);
658        }
659
660        for scope in self.visible_scopes()? {
661            if let Some(value) = scope.get_any_with_request_cache_inner(
662                token,
663                request_cache.clone(),
664                resolution_stack,
665                alias_path,
666            )? {
667                return Ok(Some(value));
668            }
669        }
670
671        Ok(None)
672    }
673
674    fn get_entry(&self, token: &ProviderToken) -> Result<Option<ProviderEntry>> {
675        if let Some(entry) = self.read_providers()?.get(token).cloned() {
676            return Ok(Some(entry));
677        }
678
679        for scope in self.visible_scopes()? {
680            if let Some(entry) = scope.get_entry(token)? {
681                return Ok(Some(entry));
682            }
683        }
684
685        Ok(None)
686    }
687
688    fn contains_local(&self, token: &ProviderToken) -> Result<bool> {
689        Ok(self.read_providers()?.contains_key(token))
690    }
691
692    fn collect_tokens(&self, tokens: &mut BTreeMap<ProviderToken, ()>) -> Result<()> {
693        for token in self.read_providers()?.keys() {
694            tokens.insert(token.clone(), ());
695        }
696        for scope in self.visible_scopes()? {
697            scope.collect_tokens(tokens)?;
698        }
699        Ok(())
700    }
701
702    fn local_entries(&self) -> Result<Vec<ProviderEntry>> {
703        let provider_order = self.read_provider_order()?.clone();
704        let providers = self.read_providers()?;
705        let mut entries = Vec::with_capacity(provider_order.len());
706        for token in provider_order {
707            if let Some(entry) = providers.get(&token) {
708                entries.push(entry.clone());
709            }
710        }
711        Ok(entries)
712    }
713
714    fn visible_scopes(&self) -> Result<Vec<ModuleRef>> {
715        Ok(self.read_visible_scopes()?.clone())
716    }
717
718    fn read_providers(
719        &self,
720    ) -> Result<std::sync::RwLockReadGuard<'_, BTreeMap<ProviderToken, ProviderEntry>>> {
721        self.providers
722            .read()
723            .map_err(|_| BootError::Internal("provider registry lock is poisoned".to_string()))
724    }
725
726    fn write_providers(
727        &self,
728    ) -> Result<std::sync::RwLockWriteGuard<'_, BTreeMap<ProviderToken, ProviderEntry>>> {
729        self.providers
730            .write()
731            .map_err(|_| BootError::Internal("provider registry lock is poisoned".to_string()))
732    }
733
734    fn read_provider_order(&self) -> Result<std::sync::RwLockReadGuard<'_, Vec<ProviderToken>>> {
735        self.provider_order
736            .read()
737            .map_err(|_| BootError::Internal("provider order lock is poisoned".to_string()))
738    }
739
740    fn write_provider_order(&self) -> Result<std::sync::RwLockWriteGuard<'_, Vec<ProviderToken>>> {
741        self.provider_order
742            .write()
743            .map_err(|_| BootError::Internal("provider order lock is poisoned".to_string()))
744    }
745
746    fn read_visible_scopes(&self) -> Result<std::sync::RwLockReadGuard<'_, Vec<ModuleRef>>> {
747        self.visible_scopes
748            .read()
749            .map_err(|_| BootError::Internal("provider registry lock is poisoned".to_string()))
750    }
751
752    fn write_visible_scopes(&self) -> Result<std::sync::RwLockWriteGuard<'_, Vec<ModuleRef>>> {
753        self.visible_scopes
754            .write()
755            .map_err(|_| BootError::Internal("provider registry lock is poisoned".to_string()))
756    }
757}