Skip to main content

qubit_spi/
service_provider.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Provider contract for pluggable service implementations.
11
12use std::fmt::Debug;
13use std::rc::Rc;
14use std::sync::Arc;
15
16use crate::{
17    ProviderAvailability,
18    ProviderCreateError,
19    ProviderDescriptor,
20    ProviderRegistryError,
21    ServiceSpec,
22};
23
24/// Factory contract for one service implementation.
25///
26/// A provider gives a registry stable names, optional aliases, availability
27/// checks, a priority used by automatic selection, and a factory method for
28/// creating one service instance. The associated service contract may be a
29/// trait object, such as `dyn MyService`, while registry creation methods
30/// decide whether the returned handle is a [`Box`], [`Arc`], or [`Rc`].
31///
32/// # Examples
33///
34/// Implement a provider for a trait-object service and create it through a
35/// registry:
36///
37/// ```rust
38/// use std::fmt::Debug;
39///
40/// use qubit_spi::{
41///     ProviderCreateError,
42///     ProviderDescriptor,
43///     ProviderRegistry,
44///     ProviderRegistryError,
45///     ServiceProvider,
46///     ServiceSpec,
47/// };
48///
49/// trait Encoder: Debug + Send + Sync {
50///     fn encode(&self, value: &str) -> String;
51/// }
52///
53/// #[derive(Debug)]
54/// struct PlainEncoder;
55///
56/// impl Encoder for PlainEncoder {
57///     fn encode(&self, value: &str) -> String {
58///         value.to_owned()
59///     }
60/// }
61///
62/// #[derive(Debug)]
63/// struct PlainEncoderProvider;
64///
65/// #[derive(Debug)]
66/// struct EncoderSpec;
67///
68/// impl ServiceSpec for EncoderSpec {
69///     type Config = ();
70///     type Service = dyn Encoder;
71/// }
72///
73/// impl ServiceProvider<EncoderSpec> for PlainEncoderProvider {
74///     fn descriptor(&self) -> Result<ProviderDescriptor, ProviderRegistryError> {
75///         ProviderDescriptor::new("plain")?.with_aliases(&["identity"])
76///     }
77///
78///     fn create_box(&self, _config: &()) -> Result<Box<dyn Encoder>, ProviderCreateError> {
79///         Ok(Box::new(PlainEncoder))
80///     }
81/// }
82///
83/// let mut registry = ProviderRegistry::<EncoderSpec>::new();
84/// registry
85///     .register(PlainEncoderProvider)
86///     .expect("provider id and aliases should be unique");
87///
88/// let encoder = registry
89///     .create_box("identity", &())
90///     .expect("registered provider should create an encoder");
91/// assert_eq!("payload", encoder.encode("payload"));
92/// ```
93///
94/// Use `priority` and `availability` to let automatic selection skip an
95/// unavailable preferred backend:
96///
97/// ```rust
98/// use std::fmt::Debug;
99///
100/// use qubit_spi::{
101///     ProviderCreateError,
102///     ProviderDescriptor,
103///     ProviderAvailability,
104///     ProviderRegistry,
105///     ProviderRegistryError,
106///     ServiceProvider,
107///     ServiceSpec,
108/// };
109///
110/// #[derive(Debug)]
111/// struct CacheConfig {
112///     remote_enabled: bool,
113/// }
114///
115/// trait Cache: Debug + Send + Sync {
116///     fn backend(&self) -> &'static str;
117/// }
118///
119/// #[derive(Debug)]
120/// struct NamedCache(&'static str);
121///
122/// impl Cache for NamedCache {
123///     fn backend(&self) -> &'static str {
124///         self.0
125///     }
126/// }
127///
128/// #[derive(Debug)]
129/// struct MemoryCacheProvider;
130///
131/// #[derive(Debug)]
132/// struct CacheSpec;
133///
134/// impl ServiceSpec for CacheSpec {
135///     type Config = CacheConfig;
136///     type Service = dyn Cache;
137/// }
138///
139/// impl ServiceProvider<CacheSpec> for MemoryCacheProvider {
140///     fn descriptor(&self) -> Result<ProviderDescriptor, ProviderRegistryError> {
141///         Ok(ProviderDescriptor::new("memory")?.with_priority(10))
142///     }
143///
144///     fn create_box(&self, _config: &CacheConfig) -> Result<Box<dyn Cache>, ProviderCreateError> {
145///         Ok(Box::new(NamedCache("memory")))
146///     }
147/// }
148///
149/// #[derive(Debug)]
150/// struct RemoteCacheProvider;
151///
152/// impl ServiceProvider<CacheSpec> for RemoteCacheProvider {
153///     fn descriptor(&self) -> Result<ProviderDescriptor, ProviderRegistryError> {
154///         Ok(ProviderDescriptor::new("remote")?.with_priority(20))
155///     }
156///
157///     fn availability(&self, config: &CacheConfig) -> ProviderAvailability {
158///         if config.remote_enabled {
159///             ProviderAvailability::Available
160///         } else {
161///             ProviderAvailability::unavailable("remote cache is disabled")
162///         }
163///     }
164///
165///     fn create_box(&self, _config: &CacheConfig) -> Result<Box<dyn Cache>, ProviderCreateError> {
166///         Ok(Box::new(NamedCache("remote")))
167///     }
168/// }
169///
170/// let mut registry = ProviderRegistry::<CacheSpec>::new();
171/// registry
172///     .register(MemoryCacheProvider)
173///     .expect("memory provider should register");
174/// registry
175///     .register(RemoteCacheProvider)
176///     .expect("remote provider should register");
177///
178/// let cache = registry
179///     .create_auto_box(&CacheConfig {
180///         remote_enabled: false,
181///     })
182///     .expect("memory cache should be selected as fallback");
183/// assert_eq!("memory", cache.backend());
184/// ```
185pub trait ServiceProvider<Spec>: Debug + Send + Sync
186where
187    Spec: ServiceSpec,
188{
189    /// Gets stable provider metadata.
190    ///
191    /// # Returns
192    /// Provider descriptor used by registries for name lookup and automatic
193    /// selection.
194    ///
195    /// # Errors
196    /// Returns [`ProviderRegistryError`] when the provider id or aliases are
197    /// invalid.
198    fn descriptor(&self) -> Result<ProviderDescriptor, ProviderRegistryError>;
199
200    /// Checks whether this provider can create a service.
201    ///
202    /// # Parameters
203    /// - `config`: Service configuration used for provider-specific checks.
204    ///
205    /// # Returns
206    /// Provider availability in the current runtime environment.
207    #[inline]
208    fn availability(&self, _config: &Spec::Config) -> ProviderAvailability {
209        ProviderAvailability::Available
210    }
211
212    /// Creates a boxed service instance.
213    ///
214    /// # Parameters
215    /// - `config`: Service configuration used to initialize the implementation.
216    ///
217    /// # Returns
218    /// Boxed service implementation.
219    ///
220    /// # Errors
221    /// Returns [`ProviderCreateError`] when initialization fails. Registries
222    /// translate this provider-level error into [`ProviderRegistryError`] with
223    /// provider-name context.
224    fn create_box(&self, config: &Spec::Config) -> Result<Box<Spec::Service>, ProviderCreateError>;
225
226    /// Creates an atomically shared service instance.
227    ///
228    /// # Parameters
229    /// - `config`: Service configuration used to initialize the implementation.
230    ///
231    /// # Returns
232    /// Atomically reference-counted service implementation.
233    ///
234    /// # Errors
235    /// Returns [`ProviderCreateError`] when initialization fails. The default
236    /// implementation creates a boxed service first and converts it into
237    /// [`Arc`].
238    #[inline]
239    fn create_arc(&self, config: &Spec::Config) -> Result<Arc<Spec::Service>, ProviderCreateError> {
240        self.create_box(config).map(Arc::from)
241    }
242
243    /// Creates a locally shared service instance.
244    ///
245    /// # Parameters
246    /// - `config`: Service configuration used to initialize the implementation.
247    ///
248    /// # Returns
249    /// Single-threaded reference-counted service implementation.
250    ///
251    /// # Errors
252    /// Returns [`ProviderCreateError`] when initialization fails. The default
253    /// implementation creates a boxed service first and converts it into [`Rc`].
254    #[inline]
255    fn create_rc(&self, config: &Spec::Config) -> Result<Rc<Spec::Service>, ProviderCreateError> {
256        self.create_box(config).map(Rc::from)
257    }
258}