summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Müller <m.mueller@ifm.com>2022-04-11 10:09:06 +0200
committerMarcel Müller <m.mueller@ifm.com>2022-04-11 11:09:45 +0200
commit94c6e4359e0ca441a68400bea6938dd79aa481f4 (patch)
tree21b5f280973ce55696d31d2ae47cab3247a8d4de
parent1d5d93b1084f225f801b1509c3367d83aec62f8e (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.rs19
-rw-r--r--crates/core/tedge_api/src/error.rs1
-rw-r--r--crates/core/tedge_api/src/lib.rs18
-rw-r--r--crates/core/tedge_api/src/plugin.rs83
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>>;