Skip to main content

a3s_boot/provider/
context_id.rs

1use super::cache::ProviderCache;
2use crate::{BootError, BootRequest, Result};
3use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, Weak};
7
8static NEXT_CONTEXT_ID: AtomicU64 = AtomicU64::new(1);
9
10/// Identity and provider cache for one dependency-injection resolution context.
11#[derive(Clone)]
12pub struct ContextId {
13    id: u64,
14    state: ContextIdStateRef,
15}
16
17#[derive(Clone)]
18enum ContextIdStateRef {
19    Strong(Arc<ContextIdState>),
20    Weak(Weak<ContextIdState>),
21}
22
23struct ContextIdState {
24    cache: ProviderCache,
25}
26
27impl ContextId {
28    fn new(id: u64) -> Self {
29        Self {
30            id,
31            state: ContextIdStateRef::Strong(Arc::new(ContextIdState {
32                cache: ProviderCache::new(),
33            })),
34        }
35    }
36
37    /// Numeric identity useful for diagnostics and correlation.
38    pub fn id(&self) -> u64 {
39        self.id
40    }
41
42    pub(crate) fn cache(&self) -> Result<ProviderCache> {
43        let state = match &self.state {
44            ContextIdStateRef::Strong(state) => Arc::clone(state),
45            ContextIdStateRef::Weak(state) => state.upgrade().ok_or_else(|| {
46                BootError::Internal(format!(
47                    "dependency-injection context {} is no longer active",
48                    self.id
49                ))
50            })?,
51        };
52        Ok(state.cache.clone())
53    }
54
55    pub(crate) fn downgrade(&self) -> Self {
56        let state = match &self.state {
57            ContextIdStateRef::Strong(state) => ContextIdStateRef::Weak(Arc::downgrade(state)),
58            ContextIdStateRef::Weak(state) => ContextIdStateRef::Weak(Weak::clone(state)),
59        };
60        Self { id: self.id, state }
61    }
62}
63
64impl fmt::Debug for ContextId {
65    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
66        formatter
67            .debug_struct("ContextId")
68            .field("id", &self.id())
69            .finish_non_exhaustive()
70    }
71}
72
73impl PartialEq for ContextId {
74    fn eq(&self, other: &Self) -> bool {
75        self.id() == other.id()
76    }
77}
78
79impl Eq for ContextId {}
80
81impl PartialOrd for ContextId {
82    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
83        Some(self.cmp(other))
84    }
85}
86
87impl Ord for ContextId {
88    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
89        self.id().cmp(&other.id())
90    }
91}
92
93impl Hash for ContextId {
94    fn hash<H>(&self, state: &mut H)
95    where
96        H: Hasher,
97    {
98        self.id().hash(state);
99    }
100}
101
102/// Creates and discovers Nest-style dependency-injection context identities.
103#[derive(Debug, Clone, Copy, Default)]
104pub struct ContextIdFactory;
105
106impl ContextIdFactory {
107    /// Create an isolated dependency-injection context.
108    pub fn create() -> ContextId {
109        ContextId::new(NEXT_CONTEXT_ID.fetch_add(1, Ordering::Relaxed))
110    }
111
112    /// Return the context already attached to a request, or create a fresh one.
113    pub fn get_by_request(request: &BootRequest) -> ContextId {
114        request.context_id().cloned().unwrap_or_else(Self::create)
115    }
116}