1use crate::context::Context;
4use crate::effect::EffectHandle;
5use crate::registry::{Inject, PluginHandle, PluginOutput, plugin_async, plugin_sync};
6use crate::{Result, Value};
7use std::future::Future;
8use std::sync::Arc;
9
10pub trait Service: Send + Sync + 'static {
17 const NAME: &'static str;
19
20 fn is_available(&self) -> bool {
22 true
23 }
24}
25
26impl Context {
27 pub fn provide_service<S>(&self, service: S) -> Result<EffectHandle>
29 where
30 S: Service,
31 {
32 self.provide_service_arc(Arc::new(service))
33 }
34
35 pub fn provide_service_arc<S>(&self, service: Arc<S>) -> Result<EffectHandle>
37 where
38 S: Service,
39 {
40 let value = Value::from_arc(service);
41 let check = Arc::new(|value: &Value| {
42 value
43 .as_any()
44 .downcast_ref::<S>()
45 .map(|service| service.is_available())
46 .unwrap_or(false)
47 });
48 self.reflect()
49 .provide_value(S::NAME.to_owned(), value, Some(check))
50 }
51
52 pub fn resolve_service_config<T, F>(&self, name: &str, base: T, mut merge: F) -> Result<T>
55 where
56 T: Send + Sync + 'static,
57 F: FnMut(T, &T) -> T,
58 {
59 let mut output = base;
60 for config in self.intercepts::<T>(name)? {
61 output = merge(output, config.as_ref());
62 }
63 Ok(output)
64 }
65}
66
67pub fn service_sync<S, C, F>(
72 name: impl Into<String>,
73 inject: Inject,
74 constructor: F,
75) -> PluginHandle
76where
77 S: Service,
78 C: Send + Sync + 'static,
79 F: Fn(Context, Arc<C>) -> Result<S> + Send + Sync + 'static,
80{
81 plugin_sync(name, inject, move |ctx, config| {
82 let service = constructor(ctx.clone(), config)?;
83 ctx.provide_service(service)?;
84 Ok(PluginOutput::none())
85 })
86}
87
88pub fn service_async<S, C, F, Fut>(
90 name: impl Into<String>,
91 inject: Inject,
92 constructor: F,
93) -> PluginHandle
94where
95 S: Service,
96 C: Send + Sync + 'static,
97 F: Fn(Context, Arc<C>) -> Fut + Send + Sync + 'static,
98 Fut: Future<Output = Result<S>> + Send + 'static,
99{
100 let constructor = Arc::new(constructor);
101 plugin_async(name, inject, move |ctx, config| {
102 let constructor = constructor.clone();
103 async move {
104 let service = constructor(ctx.clone(), config).await?;
105 ctx.provide_service(service)?;
106 Ok(PluginOutput::none())
107 }
108 })
109}