Skip to main content

appcore_capabilities/
selection.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: selection.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/07/22 15:41:18 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/07/22 15:41:18 by dnettoRaw
8//      ###########      S: 1.0.1-rc.8
9// =============================================================================
10
11use appcore_core::{CapabilityDescriptor, CoreId};
12use appcore_distributed_contracts::PeerRecord;
13
14/// A local or discovered remote provider selected for a capability.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum CapabilityProvider {
17    /// Provider hosted by the current runtime process.
18    Local {
19        /// Identity of the local core.
20        core_id: CoreId,
21        /// Capability contract advertised by the handler.
22        descriptor: CapabilityDescriptor,
23    },
24    /// Provider advertised by a discovered peer.
25    Remote {
26        /// Peer identity, endpoints, and advertised capabilities.
27        peer: Box<PeerRecord>,
28        /// Capability contract advertised by the peer.
29        descriptor: CapabilityDescriptor,
30        /// Whether discovery metadata marks this peer as preferred.
31        preferred: bool,
32    },
33}
34
35impl CapabilityProvider {
36    /// Returns the selected provider's capability descriptor.
37    pub fn descriptor(&self) -> &CapabilityDescriptor {
38        match self {
39            Self::Local { descriptor, .. } | Self::Remote { descriptor, .. } => descriptor,
40        }
41    }
42
43    /// Returns the selected provider's core identity.
44    pub fn core_id(&self) -> &CoreId {
45        match self {
46            Self::Local { core_id, .. } => core_id,
47            Self::Remote { peer, .. } => &peer.identity.core_id,
48        }
49    }
50
51    /// Returns `true` when the provider must be invoked through peer RPC.
52    pub fn is_remote(&self) -> bool {
53        matches!(self, Self::Remote { .. })
54    }
55}
56
57/// Basic locality policy used by the default provider selector.
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub struct ResolutionPolicy {
60    /// Prefer a healthy local provider over remote candidates.
61    pub prefer_local: bool,
62    /// Permit discovered remote providers to be selected.
63    pub allow_remote: bool,
64}
65
66impl Default for ResolutionPolicy {
67    fn default() -> Self {
68        Self {
69            prefer_local: true,
70            allow_remote: true,
71        }
72    }
73}
74
75/// Pluggable policy for selecting one provider from compatible candidates.
76pub trait CapabilitySelectionPolicy: Send + Sync {
77    /// Selects a provider or returns `None` when no candidate is acceptable.
78    fn select(&self, candidates: &[CapabilityProvider]) -> Option<CapabilityProvider>;
79}
80
81/// Deterministic selector that applies locality and discovery preference flags.
82#[derive(Debug, Clone, Copy, Default)]
83pub struct DefaultCapabilitySelectionPolicy {
84    /// Locality constraints applied during selection.
85    pub policy: ResolutionPolicy,
86}
87
88impl CapabilitySelectionPolicy for DefaultCapabilitySelectionPolicy {
89    fn select(&self, candidates: &[CapabilityProvider]) -> Option<CapabilityProvider> {
90        if self.policy.prefer_local {
91            if let Some(local) = candidates
92                .iter()
93                .find(|candidate| matches!(candidate, CapabilityProvider::Local { .. }))
94            {
95                return Some(local.clone());
96            }
97        }
98
99        if self.policy.allow_remote {
100            if let Some(remote) = candidates.iter().find(|candidate| {
101                matches!(
102                    candidate,
103                    CapabilityProvider::Remote {
104                        preferred: true,
105                        ..
106                    }
107                )
108            }) {
109                return Some(remote.clone());
110            }
111
112            if let Some(remote) = candidates
113                .iter()
114                .find(|candidate| matches!(candidate, CapabilityProvider::Remote { .. }))
115            {
116                return Some(remote.clone());
117            }
118        }
119
120        candidates
121            .iter()
122            .find(|candidate| matches!(candidate, CapabilityProvider::Local { .. }))
123            .cloned()
124    }
125}