#![doc = include_str!("../../Plugin.md")]
pub mod component;
pub mod lazy;
pub mod service;
use crate::error::Result;
use crate::{app::AppBuilder, error::AppError};
use async_trait::async_trait;
use component::ComponentRef;
pub use lazy::LazyComponent;
pub use service::Service;
use std::{
any::{self, Any},
ops::Deref,
sync::Arc,
};
inventory::collect!(&'static dyn Plugin);
#[macro_export]
macro_rules! submit_component_plugin {
($ty:ident) => {
$crate::submit_inventory! {
&$ty as &dyn $crate::plugin::Plugin
}
};
}
#[derive(Clone)]
pub struct PluginRef(Arc<dyn Plugin>);
#[async_trait]
pub trait Plugin: Any + Send + Sync {
async fn build(&self, _app: &mut AppBuilder) {}
fn immediately_build(&self, _app: &mut AppBuilder) {}
fn name(&self) -> &str {
std::any::type_name::<Self>()
}
fn dependencies(&self) -> Vec<&str> {
vec![]
}
fn immediately(&self) -> bool {
false
}
}
impl PluginRef {
pub(crate) fn new<T: Plugin>(plugin: T) -> Self {
Self(Arc::new(plugin))
}
}
impl Deref for PluginRef {
type Target = dyn Plugin;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
pub trait ComponentRegistry {
fn get_component_ref<T>(&self) -> Option<ComponentRef<T>>
where
T: Any + Send + Sync;
fn get_expect_component_ref<T>(&self) -> ComponentRef<T>
where
T: Clone + Send + Sync + 'static,
{
self.get_component_ref().unwrap_or_else(|| {
panic!(
"{} component not exists in registry",
std::any::type_name::<T>()
)
})
}
fn try_get_component_ref<T>(&self) -> Result<ComponentRef<T>>
where
T: Clone + Send + Sync + 'static,
{
self.get_component_ref()
.ok_or_else(|| AppError::ComponentNotExist(std::any::type_name::<T>()))
}
fn get_component<T>(&self) -> Option<T>
where
T: Clone + Send + Sync + 'static;
fn get_expect_component<T>(&self) -> T
where
T: Clone + Send + Sync + 'static,
{
self.get_component().unwrap_or_else(|| {
panic!(
"{} component not exists in registry",
std::any::type_name::<T>()
)
})
}
fn try_get_component<T>(&self) -> Result<T>
where
T: Clone + Send + Sync + 'static,
{
self.get_component()
.ok_or_else(|| AppError::ComponentNotExist(std::any::type_name::<T>()))
}
fn has_component<T>(&self) -> bool
where
T: Any + Send + Sync;
}
pub trait MutableComponentRegistry: ComponentRegistry {
fn add_component<C>(&mut self, component: C) -> &mut Self
where
C: Clone + any::Any + Send + Sync;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::app::AppBuilder;
#[derive(Clone)]
struct TestComponent {
value: i32,
}
#[derive(Clone)]
struct AnotherComponent {
name: String,
}
#[tokio::test]
async fn test_component_registry_add_and_get() {
let mut app = AppBuilder::default();
let test_comp = TestComponent { value: 42 };
app.add_component(test_comp);
let retrieved = app.get_component::<TestComponent>();
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().value, 42);
}
#[tokio::test]
async fn test_component_registry_get_nonexistent() {
let app = AppBuilder::default();
let retrieved = app.get_component::<TestComponent>();
assert!(retrieved.is_none());
}
#[tokio::test]
async fn test_component_registry_has_component() {
let mut app = AppBuilder::default();
assert!(!app.has_component::<TestComponent>());
app.add_component(TestComponent { value: 100 });
assert!(app.has_component::<TestComponent>());
assert!(!app.has_component::<AnotherComponent>());
}
#[tokio::test]
async fn test_component_registry_multiple_types() {
let mut app = AppBuilder::default();
app.add_component(TestComponent { value: 1 });
app.add_component(AnotherComponent {
name: "test".to_string(),
});
let test_comp = app.get_component::<TestComponent>();
let another_comp = app.get_component::<AnotherComponent>();
assert!(test_comp.is_some());
assert!(another_comp.is_some());
assert_eq!(test_comp.unwrap().value, 1);
assert_eq!(another_comp.unwrap().name, "test");
}
#[tokio::test]
async fn test_get_component_ref() {
let mut app = AppBuilder::default();
app.add_component(TestComponent { value: 999 });
let comp_ref = app.get_component_ref::<TestComponent>();
assert!(comp_ref.is_some());
let comp_ref = comp_ref.unwrap();
assert_eq!(comp_ref.value, 999);
}
#[tokio::test]
async fn test_try_get_component_success() {
let mut app = AppBuilder::default();
app.add_component(TestComponent { value: 50 });
let result = app.try_get_component::<TestComponent>();
assert!(result.is_ok());
assert_eq!(result.unwrap().value, 50);
}
#[tokio::test]
async fn test_try_get_component_failure() {
let app = AppBuilder::default();
let result = app.try_get_component::<TestComponent>();
assert!(result.is_err());
}
#[tokio::test]
#[should_panic(expected = "component not exists in registry")]
async fn test_get_expect_component_panic() {
let app = AppBuilder::default();
let _comp = app.get_expect_component::<TestComponent>();
}
#[tokio::test]
async fn test_get_expect_component_success() {
let mut app = AppBuilder::default();
app.add_component(TestComponent { value: 777 });
let comp = app.get_expect_component::<TestComponent>();
assert_eq!(comp.value, 777);
}
struct TestPlugin {
name: &'static str,
}
#[async_trait]
impl Plugin for TestPlugin {
async fn build(&self, app: &mut AppBuilder) {
app.add_component(TestComponent { value: 123 });
}
fn name(&self) -> &str {
self.name
}
}
struct DependentPlugin;
#[async_trait]
impl Plugin for DependentPlugin {
async fn build(&self, app: &mut AppBuilder) {
let test_comp = app.get_component::<TestComponent>();
assert!(test_comp.is_some());
app.add_component(AnotherComponent {
name: format!("dependent_{}", test_comp.unwrap().value),
});
}
fn name(&self) -> &str {
"DependentPlugin"
}
fn dependencies(&self) -> Vec<&str> {
vec!["TestPlugin"]
}
}
#[tokio::test]
async fn test_plugin_registration() {
let mut app = AppBuilder::default();
app.add_plugin(TestPlugin { name: "TestPlugin" });
assert!(app.is_plugin_added::<TestPlugin>());
}
#[tokio::test]
async fn test_plugin_build_adds_component() {
let mut app = AppBuilder::default();
app.add_plugin(TestPlugin { name: "TestPlugin" });
let plugin = TestPlugin { name: "TestPlugin" };
plugin.build(&mut app).await;
let comp = app.get_component::<TestComponent>();
assert!(comp.is_some());
assert_eq!(comp.unwrap().value, 123);
}
#[tokio::test]
async fn test_plugin_dependencies_order() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
let _ = tracing_subscriber::fmt().try_init();
});
let mut app = AppBuilder::default();
app.add_plugin(DependentPlugin);
app.add_plugin(TestPlugin { name: "TestPlugin" });
let test_plugin = TestPlugin { name: "TestPlugin" };
test_plugin.build(&mut app).await;
let dependent_plugin = DependentPlugin;
dependent_plugin.build(&mut app).await;
assert!(app.has_component::<TestComponent>());
assert!(app.has_component::<AnotherComponent>());
let another = app.get_component::<AnotherComponent>().unwrap();
assert_eq!(another.name, "dependent_123");
}
struct ImmediatePlugin;
#[async_trait]
impl Plugin for ImmediatePlugin {
fn immediately_build(&self, app: &mut AppBuilder) {
app.add_component(TestComponent { value: 999 });
}
fn immediately(&self) -> bool {
true
}
fn name(&self) -> &str {
"ImmediatePlugin"
}
}
#[tokio::test]
async fn test_immediate_plugin() {
let mut app = AppBuilder::default();
app.add_plugin(ImmediatePlugin);
assert!(app.has_component::<TestComponent>());
let comp = app.get_component::<TestComponent>().unwrap();
assert_eq!(comp.value, 999);
}
#[tokio::test]
#[should_panic(expected = "plugin was already added")]
async fn test_duplicate_plugin_panic() {
let mut app = AppBuilder::default();
app.add_plugin(TestPlugin { name: "TestPlugin" });
app.add_plugin(TestPlugin { name: "TestPlugin" }); }
#[tokio::test]
#[should_panic(expected = "component was already added")]
async fn test_duplicate_component_panic() {
let mut app = AppBuilder::default();
app.add_component(TestComponent { value: 1 });
app.add_component(TestComponent { value: 2 }); }
}