diff options
author | Marcel Müller <m.mueller@ifm.com> | 2022-04-11 10:09:06 +0200 |
---|---|---|
committer | Marcel Müller <m.mueller@ifm.com> | 2022-04-11 11:09:45 +0200 |
commit | 94c6e4359e0ca441a68400bea6938dd79aa481f4 (patch) | |
tree | 21b5f280973ce55696d31d2ae47cab3247a8d4de | |
parent | 1d5d93b1084f225f801b1509c3367d83aec62f8e (diff) |
Add PluginDeclaration trait
This trait simplifies a lot of the message calls in the PluginBuilder.
Advantages: You only declare a list of handled messages _once_. This is
necessary since Rust does not allow 'queries' for types (aka "give me a
tuple of supported types" does not exist), and so the necessary crutch
is to declare them manually.
Disadvantages: You cannot pick at Runtime anymore what messages you
decide to support. This is considered to be a very niche use-case and
might actually be an anti-pattern.
Signed-off-by: Marcel Müller <m.mueller@ifm.com>
-rw-r--r-- | crates/core/tedge_api/examples/heartbeat.rs | 19 | ||||
-rw-r--r-- | crates/core/tedge_api/src/error.rs | 1 | ||||
-rw-r--r-- | crates/core/tedge_api/src/lib.rs | 18 | ||||
-rw-r--r-- | crates/core/tedge_api/src/plugin.rs | 83 |
4 files changed, 67 insertions, 54 deletions
diff --git a/crates/core/tedge_api/examples/heartbeat.rs b/crates/core/tedge_api/examples/heartbeat.rs index 15793bfc..7fae395e 100644 --- a/crates/core/tedge_api/examples/heartbeat.rs +++ b/crates/core/tedge_api/examples/heartbeat.rs @@ -9,8 +9,9 @@ use futures::FutureExt; use tedge_api::{ address::ReplySender, message::NoReply, - plugin::{BuiltPlugin, Handle, HandleTypes, Message, PluginExt}, - Address, Plugin, PluginBuilder, PluginConfiguration, PluginDirectory, PluginError, CancellationToken, + plugin::{BuiltPlugin, Handle, HandleTypes, Message, PluginDeclaration, PluginExt}, + Address, CancellationToken, Plugin, PluginBuilder, PluginConfiguration, PluginDirectory, + PluginError, }; /// A message that represents a heartbeat that gets sent to plugins @@ -78,7 +79,7 @@ impl<PD: PluginDirectory> PluginBuilder<PD> for HeartbeatServiceBuilder { monitored_services, cancellation_token, ) - .into_untyped::<()>()) + .into_untyped()) } } @@ -96,6 +97,10 @@ struct HeartbeatService { cancel_token: CancellationToken, } +impl PluginDeclaration for HeartbeatService { + type HandledMessages = (); +} + #[async_trait] impl Plugin for HeartbeatService { /// The setup function of the HeartbeatService can be used by the plugin author to setup for @@ -203,7 +208,7 @@ impl<PD: PluginDirectory> PluginBuilder<PD> for CriticalServiceBuilder { where Self: Sized, { - HandleTypes::declare_handlers_for::<(Heartbeat,), CriticalService>() + CriticalService::get_handled_types() } async fn verify_configuration( @@ -225,7 +230,7 @@ impl<PD: PluginDirectory> PluginBuilder<PD> for CriticalServiceBuilder { Ok(CriticalService { status: tokio::sync::Mutex::new(true), } - .into_untyped::<(Heartbeat,)>()) + .into_untyped()) } } @@ -259,6 +264,10 @@ impl Handle<Heartbeat> for CriticalService { } } +impl PluginDeclaration for CriticalService { + type HandledMessages = (Heartbeat,); +} + /// Because the CriticalService is of course a Plugin, it needs an implementation for that as well. #[async_trait] impl Plugin for CriticalService { diff --git a/crates/core/tedge_api/src/error.rs b/crates/core/tedge_api/src/error.rs index 4f6c9015..76c96368 100644 --- a/crates/core/tedge_api/src/error.rs +++ b/crates/core/tedge_api/src/error.rs @@ -3,7 +3,6 @@ use thiserror::Error; /// An error that a plugin might emit #[derive(Error, Debug)] pub enum PluginError { - /// Error kind if the configuration of the plugin was faulty #[error("An error in the configuration was found")] Configuration(#[from] toml::de::Error), diff --git a/crates/core/tedge_api/src/lib.rs b/crates/core/tedge_api/src/lib.rs index 8c23477d..477bc779 100644 --- a/crates/core/tedge_api/src/lib.rs +++ b/crates/core/tedge_api/src/lib.rs @@ -1,14 +1,17 @@ -#![cfg_attr(test, deny( - missing_docs, - missing_debug_implementations, - unreachable_pub, - unsafe_code, -))] +#![cfg_attr( + test, + deny( + missing_docs, + missing_debug_implementations, + unreachable_pub, + unsafe_code, + ) +)] #![doc = include_str!("../README.md")] /// All the parts required to write a plugin pub mod plugin; -pub use plugin::{PluginDirectory, Plugin, PluginBuilder, PluginConfiguration}; +pub use plugin::{Plugin, PluginBuilder, PluginConfiguration, PluginDirectory, PluginExt}; /// Addresses allow plugins to exchange messages pub mod address; @@ -22,7 +25,6 @@ pub use error::PluginError; pub mod message; pub use message::CoreMessages; - /// Cancellation token used by `tedge_api` /// pub use tokio_util::sync::CancellationToken; diff --git a/crates/core/tedge_api/src/plugin.rs b/crates/core/tedge_api/src/plugin.rs index ee905664..958c7cce 100644 --- a/crates/core/tedge_api/src/plugin.rs +++ b/crates/core/tedge_api/src/plugin.rs @@ -16,7 +16,7 @@ use async_trait::async_trait; use crate::{ address::{InternalMessage, ReceiverBundle, ReplySender}, - error::{PluginError, DirectoryError}, + error::{DirectoryError, PluginError}, message::CoreMessages, Address, }; @@ -41,7 +41,10 @@ pub trait PluginDirectory: Send + Sync { /// ## Also see /// /// - [`make_message_bundle`] On how to define your own named message bundle - fn get_address_for<RB: ReceiverBundle>(&self, name: &str) -> Result<Address<RB>, DirectoryError>; + fn get_address_for<RB: ReceiverBundle>( + &self, + name: &str, + ) -> Result<Address<RB>, DirectoryError>; /// Request an `Address` to the core itself. It will only accept messages from the /// [`CoreMessages`] bundle. @@ -93,7 +96,7 @@ pub trait PluginBuilder<PD: PluginDirectory>: Sync + Send + 'static { /// # Example /// /// ```no_run - /// # use tedge_api::{Plugin, plugin::BuiltPlugin, PluginError, PluginDirectory, PluginBuilder, PluginConfiguration}; + /// # use tedge_api::{Plugin, plugin::BuiltPlugin, PluginError, PluginExt, PluginDirectory, PluginBuilder, PluginConfiguration}; /// /// #[derive(Debug)] /// struct MyMessage; @@ -113,6 +116,10 @@ pub trait PluginBuilder<PD: PluginDirectory>: Sync + Send + 'static { /// # } /// # } /// + /// # impl tedge_api::plugin::PluginDeclaration for MyPlugin { + /// # type HandledMessages = (MyMessage,); + /// # } + /// /// #[async_trait::async_trait] /// impl tedge_api::plugin::Handle<MyMessage> for MyPlugin { /// async fn handle_message( @@ -131,7 +138,7 @@ pub trait PluginBuilder<PD: PluginDirectory>: Sync + Send + 'static { /// where /// Self: Sized, /// { - /// tedge_api::plugin::HandleTypes::declare_handlers_for::<(MyMessage,), MyPlugin>() + /// MyPlugin::get_handled_types() /// } /// // other trait functions... /// # fn kind_name() -> &'static str { @@ -200,6 +207,7 @@ pub trait PluginBuilder<PD: PluginDirectory>: Sync + Send + 'static { /// # use tedge_api::Plugin; /// # use tedge_api::PluginBuilder; /// # use tedge_api::PluginDirectory; + /// # use tedge_api::PluginExt; /// /// #[derive(Debug)] /// struct MyMessage; @@ -220,6 +228,10 @@ pub trait PluginBuilder<PD: PluginDirectory>: Sync + Send + 'static { /// # } /// # } /// + /// # impl tedge_api::plugin::PluginDeclaration for MyPlugin { + /// # type HandledMessages = (MyMessage,); + /// # } + /// /// #[async_trait::async_trait] /// impl tedge_api::plugin::Handle<MyMessage> for MyPlugin { /// async fn handle_message( @@ -245,7 +257,7 @@ pub trait PluginBuilder<PD: PluginDirectory>: Sync + Send + 'static { /// { /// use tedge_api::plugin::PluginExt; /// let p = MyPlugin {}; - /// Ok(p.into_untyped::<(MyMessage,)>()) + /// Ok(p.into_untyped()) /// } /// // other trait functions... /// # fn kind_name() -> &'static str { @@ -255,7 +267,7 @@ pub trait PluginBuilder<PD: PluginDirectory>: Sync + Send + 'static { /// # where /// # Self: Sized, /// # { - /// # tedge_api::plugin::HandleTypes::declare_handlers_for::<(MyMessage,), MyPlugin>() + /// # MyPlugin::get_handled_types() /// # } /// # async fn verify_configuration( /// # &self, @@ -304,6 +316,10 @@ pub trait Plugin: Sync + Send + DowncastSync { impl_downcast!(sync Plugin); +pub trait PluginDeclaration: Plugin { + type HandledMessages: MessageBundle; +} + /// A trait marking that a plugin is able to handle certain messages /// /// This trait can be used by plugin authors to make their plugins able to handle messages of a @@ -339,6 +355,7 @@ impl HandleTypes { /// # use tedge_api::plugin::{Handle, HandleTypes}; /// # use tedge_api::address::ReplySender; /// # use tedge_api::PluginError; + /// # use tedge_api::PluginExt; /// /// #[derive(Debug)] /// struct Heartbeat; @@ -368,8 +385,11 @@ impl HandleTypes { /// # unimplemented!() /// # } /// # } + /// # impl tedge_api::plugin::PluginDeclaration for HeartbeatPlugin { + /// # type HandledMessages = (Heartbeat,); + /// # } /// - /// println!("{:#?}", HandleTypes::declare_handlers_for::<(Heartbeat,), HeartbeatPlugin>()); + /// println!("{:#?}", HeartbeatPlugin::get_handled_types()); /// // This will print something akin to: /// // /// // HandleTypes( @@ -383,35 +403,11 @@ impl HandleTypes { /// // ], /// // ) /// ``` - /// - /// Should you ask for messages the plugin does _not_ support, you will receive a compile - /// error: - /// ```compile_fail - /// # use async_trait::async_trait; - /// # use tedge_api::plugin::{Message, Handle, HandleTypes}; - /// # use tedge_api::PluginError; - /// - /// struct Heartbeat; - /// - /// impl Message for Heartbeat {} - /// - /// struct HeartbeatPlugin; - /// - /// // This will fail to compile as the `Heartbeat` message is not handled by the plugin - /// println!("{:#?}", HandleTypes::get_handlers_for::<(Heartbeat,), HeartbeatPlugin>()); - /// ``` - /// - /// The error from the rust compiler would look like this (giving a clear indication of what is - /// missing): - /// ```text - /// error[E0277]: the trait bound `HeartbeatPlugin: Handle<Heartbeat>` is not satisfied - /// --> src/plugin.rs:XX:YYY - /// | - /// XX | println!("{:#?}", HandleTypes::get_handlers_for::<(Heartbeat,), HeartbeatPlugin>()); - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Handle<Heartbeat>` is not implemented for `HeartbeatPlugin` - /// ``` - pub fn declare_handlers_for<M: MessageBundle, Plugin: DoesHandle<M>>() -> HandleTypes { - HandleTypes(M::get_ids()) + pub fn declare_handlers_for<P: PluginDeclaration>() -> HandleTypes + where + P: DoesHandle<P::HandledMessages>, + { + HandleTypes(P::HandledMessages::get_ids()) } /// Empty list of types. A plugin that does not handle anything will not be able to receive @@ -448,20 +444,27 @@ pub trait MessageBundle { /// This trait implements an extension for all types that implement `Plugin`. /// This extension can be used by plugin authors to make their specific plugin type instance into a /// [`BuiltPlugin`]. -pub trait PluginExt: Plugin { +pub trait PluginExt: PluginDeclaration { /// Convert a `Plugin` into a `BuiltPlugin` /// /// This function is only available if the Plugin is able to handle messages that are inside /// the specified `MessageBundle`. - fn into_untyped<M: MessageBundle>(self) -> BuiltPlugin + fn into_untyped(self) -> BuiltPlugin where - Self: DoesHandle<M> + Sized, + Self: DoesHandle<Self::HandledMessages> + Sized, { self.into_built_plugin() } + + fn get_handled_types() -> HandleTypes + where + Self: DoesHandle<Self::HandledMessages> + Sized, + { + HandleTypes::declare_handlers_for::<Self>() + } } -impl<P: Plugin> PluginExt for P {} +impl<P: PluginDeclaration> PluginExt for P {} type PluginHandlerFn = for<'r> fn(&'r dyn Any, InternalMessage) -> BoxFuture<'r, Result<(), PluginError>>; |