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}