cordis_loader/
registry.rs1use cordis::utils::BoxFuture;
5use cordis::{Config, Context, Inject, Plugin, PluginHandle, PluginOutput, Result};
6use cordis_group::Group;
7use cordis_include::IMPORT_NAME;
8use cordis_include::resolver::unknown_plugin;
9use std::collections::HashMap;
10#[cfg(feature = "dynamic")]
11use std::path::PathBuf;
12use std::sync::Arc;
13
14type Factory = Arc<dyn Fn() -> PluginHandle + Send + Sync>;
16
17#[derive(Clone, Default)]
29pub struct PluginRegistry {
30 factories: HashMap<String, Factory>,
31 #[cfg(feature = "dynamic")]
32 dynamic: Option<crate::dynamic::DynamicPluginResolver>,
33}
34
35impl PluginRegistry {
36 pub fn new() -> Self {
38 let mut registry = Self::default();
39 registry.register("group", Group::handle);
40 registry.register(IMPORT_NAME, || PluginHandle::new(Import));
41 registry
42 }
43
44 pub fn register<F>(&mut self, name: impl Into<String>, factory: F)
46 where
47 F: Fn() -> PluginHandle + Send + Sync + 'static,
48 {
49 self.factories.insert(name.into(), Arc::new(factory));
50 }
51
52 pub fn register_plugin<P: Plugin>(&mut self, plugin: P) {
55 let name = plugin.name().to_owned();
56 let shared = Arc::new(plugin);
57 self.register(name, move || {
58 PluginHandle::new(SharedPlugin(shared.clone()))
59 });
60 }
61
62 pub fn names(&self) -> impl Iterator<Item = &str> {
64 self.factories.keys().map(String::as_str)
65 }
66
67 #[cfg(feature = "dynamic")]
70 pub fn set_dynamic(&mut self, resolver: crate::dynamic::DynamicPluginResolver) {
71 self.dynamic = Some(resolver);
72 }
73
74 #[cfg(feature = "dynamic")]
77 pub fn with_dynamic_dirs<I, D>(mut self, dirs: I) -> Self
78 where
79 I: IntoIterator<Item = D>,
80 D: Into<PathBuf>,
81 {
82 self.set_dynamic(crate::dynamic::DynamicPluginResolver::new(dirs));
83 self
84 }
85
86 #[cfg(feature = "dynamic")]
88 pub fn dynamic(&self) -> Option<&crate::dynamic::DynamicPluginResolver> {
89 self.dynamic.as_ref()
90 }
91}
92
93impl cordis_include::PluginResolver for PluginRegistry {
94 fn resolve(&self, name: &str) -> Result<PluginHandle> {
95 match self.factories.get(name) {
96 Some(factory) => Ok(factory()),
97 #[cfg(feature = "dynamic")]
98 None => match &self.dynamic {
99 Some(resolver) => resolver.resolve(name),
100 None => Err(unknown_plugin(name)),
101 },
102 #[cfg(not(feature = "dynamic"))]
103 None => Err(unknown_plugin(name)),
104 }
105 }
106}
107
108pub(crate) struct Import;
111
112impl Plugin for Import {
113 fn name(&self) -> &str {
114 IMPORT_NAME
115 }
116
117 fn apply(&self, _ctx: Context, _config: Config) -> BoxFuture<Result<PluginOutput>> {
118 Box::pin(async { Ok(PluginOutput::default()) })
119 }
120}
121
122struct SharedPlugin<P>(Arc<P>);
124
125impl<P: Plugin> Plugin for SharedPlugin<P> {
126 fn name(&self) -> &str {
127 self.0.name()
128 }
129
130 fn inject(&self) -> &Inject {
131 self.0.inject()
132 }
133
134 fn validate_config(&self, config: Config) -> Result<Config> {
135 self.0.validate_config(config)
136 }
137
138 fn apply(&self, ctx: Context, config: Config) -> BoxFuture<Result<PluginOutput>> {
139 self.0.apply(ctx, config)
140 }
141}
142
143pub(crate) struct WithInject {
150 handle: PluginHandle,
151 inject: Inject,
152}
153
154impl WithInject {
155 pub fn wrap(handle: PluginHandle, inject: Vec<String>) -> PluginHandle {
160 if inject.is_empty() {
161 return handle;
162 }
163 let mut names: Vec<String> = handle
164 .plugin()
165 .inject()
166 .names()
167 .map(ToString::to_string)
168 .collect();
169 for name in inject {
170 if !names.contains(&name) {
171 names.push(name);
172 }
173 }
174 PluginHandle::new(WithInject {
175 handle,
176 inject: Inject::new(names),
177 })
178 }
179}
180
181impl Plugin for WithInject {
182 fn name(&self) -> &str {
183 self.handle.name()
184 }
185
186 fn inject(&self) -> &Inject {
187 &self.inject
188 }
189
190 fn validate_config(&self, config: Config) -> Result<Config> {
191 self.handle.plugin().validate_config(config)
192 }
193
194 fn apply(&self, ctx: Context, config: Config) -> BoxFuture<Result<PluginOutput>> {
195 self.handle.plugin().apply(ctx, config)
196 }
197}