use cordis::utils::BoxFuture;
use cordis::{Config, Context, Inject, Plugin, PluginHandle, PluginOutput, Result};
use cordis_group::Group;
use cordis_include::IMPORT_NAME;
use cordis_include::resolver::unknown_plugin;
use std::collections::HashMap;
use std::sync::Arc;
type Factory = Arc<dyn Fn() -> PluginHandle + Send + Sync>;
#[derive(Clone, Default)]
pub struct PluginRegistry {
factories: HashMap<String, Factory>,
}
impl PluginRegistry {
pub fn new() -> Self {
let mut registry = Self::default();
registry.register("group", Group::handle);
registry.register(IMPORT_NAME, || PluginHandle::new(Import));
registry
}
pub fn register<F>(&mut self, name: impl Into<String>, factory: F)
where
F: Fn() -> PluginHandle + Send + Sync + 'static,
{
self.factories.insert(name.into(), Arc::new(factory));
}
pub fn register_plugin<P: Plugin>(&mut self, plugin: P) {
let name = plugin.name().to_owned();
let shared = Arc::new(plugin);
self.register(name, move || {
PluginHandle::new(SharedPlugin(shared.clone()))
});
}
pub fn names(&self) -> impl Iterator<Item = &str> {
self.factories.keys().map(String::as_str)
}
}
impl cordis_include::PluginResolver for PluginRegistry {
fn resolve(&self, name: &str) -> Result<PluginHandle> {
match self.factories.get(name) {
Some(factory) => Ok(factory()),
None => Err(unknown_plugin(name)),
}
}
}
pub(crate) struct Import;
impl Plugin for Import {
fn name(&self) -> &str {
IMPORT_NAME
}
fn apply(&self, _ctx: Context, _config: Config) -> BoxFuture<Result<PluginOutput>> {
Box::pin(async { Ok(PluginOutput::default()) })
}
}
struct SharedPlugin<P>(Arc<P>);
impl<P: Plugin> Plugin for SharedPlugin<P> {
fn name(&self) -> &str {
self.0.name()
}
fn inject(&self) -> &Inject {
self.0.inject()
}
fn validate_config(&self, config: Config) -> Result<Config> {
self.0.validate_config(config)
}
fn apply(&self, ctx: Context, config: Config) -> BoxFuture<Result<PluginOutput>> {
self.0.apply(ctx, config)
}
}
pub(crate) struct WithInject {
handle: PluginHandle,
inject: Inject,
}
impl WithInject {
pub fn wrap(handle: PluginHandle, inject: Vec<String>) -> PluginHandle {
if inject.is_empty() {
handle
} else {
PluginHandle::new(WithInject {
handle,
inject: Inject::new(inject),
})
}
}
}
impl Plugin for WithInject {
fn name(&self) -> &str {
self.handle.name()
}
fn inject(&self) -> &Inject {
&self.inject
}
fn validate_config(&self, config: Config) -> Result<Config> {
self.handle.plugin().validate_config(config)
}
fn apply(&self, ctx: Context, config: Config) -> BoxFuture<Result<PluginOutput>> {
self.handle.plugin().apply(ctx, config)
}
}