From 5874d04838623e9c0ef1dac313705c1fa1cb39d3 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Jun 2022 16:55:39 +0200 Subject: Update PluginBuilder description Signed-off-by: Matthias Beyer --- crates/core/tedge_api/goals.md | 237 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 225 insertions(+), 12 deletions(-) (limited to 'crates/core') diff --git a/crates/core/tedge_api/goals.md b/crates/core/tedge_api/goals.md index 6ab505f2..cb36f686 100644 --- a/crates/core/tedge_api/goals.md +++ b/crates/core/tedge_api/goals.md @@ -135,24 +135,230 @@ So, to get started: It looks like this: ```rust -/// A plugin builder for a given plugin #[async_trait] -pub trait PluginBuilder: Sync + Send + 'static { - /// The a name for the kind of plugins this creates, this should be unique and will prevent startup otherwise - fn kind_name(&self) -> &'static str; - - /// This may be called anytime to verify whether a plugin could be instantiated with the - /// passed configuration. +pub trait PluginBuilder: Sync + Send + 'static { + /// The name for the kind of plugins this creates, this should be unique and will prevent startup otherwise + /// + /// The "kind name" of a plugin is used by the configuration to name what plugin is to be + /// instantiated. For example, if the configuration asks thin-edge to instantiate a plugin + /// of kind "foo", but only a plugin implementation of kind "bar" is compiled into thin-edge, + /// the software is able to report misconfiguration on startup. + fn kind_name() -> &'static str + where + Self: Sized; + + /// A list of message types the plugin this builder creates supports + /// + /// This function must return a `HandleTypes` object which represents the types of messages + /// that a plugin is able to handle. + /// + /// To create an instance of this type, you must call the [`PluginExt::get_handled_types`] + /// method on the plugin this PluginBuilder will build + /// + /// # Example + /// + /// ```no_run + /// # use tedge_api::{Plugin, plugin::BuiltPlugin, PluginError, PluginExt, PluginDirectory, PluginBuilder, PluginConfiguration}; + /// # use type_uuid::TypeUuid; + /// + /// #[derive(Debug, TypeUuid)] + /// #[uuid = "46f5d318-4158-4726-83dd-9b310cae3328"] + /// struct MyMessage; + /// impl tedge_api::Message for MyMessage { } + /// + /// struct MyPluginBuilder; + /// struct MyPlugin; // + some impl Plugin for MyPlugin + /// # #[async_trait::async_trait] + /// # impl Plugin for MyPlugin { + /// # async fn start(&mut self) -> Result<(), PluginError> { + /// # unimplemented!() + /// # } + /// # async fn shutdown(&mut self) -> Result<(), PluginError> { + /// # unimplemented!() + /// # } + /// # } + /// + /// # impl tedge_api::plugin::PluginDeclaration for MyPlugin { + /// # type HandledMessages = (MyMessage,); + /// # } + /// + /// #[async_trait::async_trait] + /// impl tedge_api::plugin::Handle for MyPlugin { + /// async fn handle_message( + /// &self, + /// message: MyMessage, + /// sender: tedge_api::address::ReplySenderFor + /// ) -> Result<(), tedge_api::error::PluginError> { + /// // ... Do something with it + ///# Ok(()) + /// } + /// } + /// + /// #[async_trait::async_trait] + /// impl PluginBuilder for MyPluginBuilder { + /// fn kind_message_types() -> tedge_api::plugin::HandleTypes + /// where + /// Self: Sized, + /// { + /// MyPlugin::get_handled_types() + /// } + /// // other trait functions... + /// # fn kind_name() -> &'static str { + /// # unimplemented!() + /// # } + /// # async fn verify_configuration( + /// # &self, + /// # _config: &PluginConfiguration, + /// # ) -> Result<(), tedge_api::error::PluginError> { + /// # unimplemented!() + /// # } + /// # async fn instantiate( + /// # &self, + /// # config: PluginConfiguration, + /// # cancellation_token: tedge_api::CancellationToken, + /// # core_comms: &PD, + /// # ) -> Result + /// # where + /// # PD: 'async_trait, + /// # { + /// # unimplemented!() + /// # } + /// } + /// ``` + fn kind_message_types() -> HandleTypes + where + Self: Sized; + + /// Get a generic configuration description of what kind of input the + /// plugin expects. + /// + /// See [`ConfigDescription`] as well as [`AsConfig`](crate::config::AsConfig) for how to + /// implement and use these types and interfaces. + fn kind_configuration() -> Option + where + Self: Sized, + { + None + } + + /// Verify the configuration of the plugin for this plugin kind + /// + /// This function will be used by the core implementation to verify that a given plugin + /// configuration can be used by a plugin. + /// + /// After the plugin configuration got loaded and deserialized, it might still contain settings + /// which are erroneous, for example + /// + /// ```toml + /// timeout = -1 + /// ``` + /// + /// This function can be used by plugin authors to verify that a given configuration is sound, + /// before the plugins are instantiated (to be able to fail early). + /// + /// # Note + /// + /// This may be called anytime (also while plugins are already running) to verify whether a + /// plugin could be instantiated with the passed configuration. async fn verify_configuration(&self, config: &PluginConfiguration) -> Result<(), PluginError>; /// Instantiate a new instance of this plugin using the given configuration /// - /// This _must not_ block + /// This function is called by the core of thin-edge to create a new plugin instance. + /// + /// The [`PluginExt::finish()`] function can be used to make any type implementing [`Plugin`] + /// into a `BuiltPlugin`, which the function requires to be returned (see example below). + /// + /// # Note + /// + /// This function _must not_ block. + /// + /// # Example + /// + /// ```no_run + /// # use tedge_api::plugin::BuiltPlugin; + /// # use tedge_api::PluginConfiguration; + /// # use tedge_api::Plugin; + /// # use tedge_api::PluginBuilder; + /// # use tedge_api::PluginDirectory; + /// # use tedge_api::PluginExt; + /// # use type_uuid::TypeUuid; + /// + /// #[derive(Debug, TypeUuid)] + /// #[uuid = "39046e3e-05ad-4b16-bbf1-8c2d2da5b668"] + /// struct MyMessage; + /// impl tedge_api::Message for MyMessage { } + /// + /// + /// struct MyPluginBuilder; + /// struct MyPlugin; // + some impl Plugin for MyPlugin + /// # #[async_trait::async_trait] + /// # impl Plugin for MyPlugin { + /// # async fn start(&mut self) -> Result<(), tedge_api::error::PluginError> { + /// # unimplemented!() + /// # } + /// # async fn shutdown(&mut self) -> Result<(), tedge_api::error::PluginError> { + /// # unimplemented!() + /// # } + /// # } + /// + /// # impl tedge_api::plugin::PluginDeclaration for MyPlugin { + /// # type HandledMessages = (MyMessage,); + /// # } + /// + /// #[async_trait::async_trait] + /// impl tedge_api::plugin::Handle for MyPlugin { + /// async fn handle_message( + /// &self, + /// _message: MyMessage, + /// _sender: tedge_api::address::ReplySenderFor, + /// ) -> Result<(), tedge_api::error::PluginError> { + /// // implementation... + /// # unimplemented!() + /// } + /// } + /// + /// #[async_trait::async_trait] + /// impl PluginBuilder for MyPluginBuilder { + /// async fn instantiate( + /// &self, + /// config: PluginConfiguration, + /// cancellation_token: tedge_api::CancellationToken, + /// core_comms: &PD, + /// ) -> Result + /// where + /// PD: 'async_trait, + /// { + /// use tedge_api::plugin::PluginExt; + /// let p = MyPlugin {}; + /// Ok(p.finish()) + /// } + /// // other trait functions... + /// # fn kind_name() -> &'static str { + /// # unimplemented!() + /// # } + /// # fn kind_message_types() -> tedge_api::plugin::HandleTypes + /// # where + /// # Self: Sized, + /// # { + /// # MyPlugin::get_handled_types() + /// # } + /// # async fn verify_configuration( + /// # &self, + /// # _config: &PluginConfiguration, + /// # ) -> Result<(), tedge_api::error::PluginError> { + /// # unimplemented!() + /// # } + /// } + /// ``` async fn instantiate( &self, config: PluginConfiguration, - tedge_comms: Comms, - ) -> Result, PluginError>; + cancellation_token: crate::CancellationToken, + core_comms: &PD, + ) -> Result + where + PD: 'async_trait; } ``` @@ -162,8 +368,15 @@ Things of note here: - `verify_configuration` allows one to check if a given configuration _could even work_ - It however is not required to prove it - `instantiate` which actually constructs your plugin and returns a new instance of it - - One argument is the `Comms` object which holds the sender part of a - channel to the tedge core, and through which messages can be sent + - One argument is a reference to a `PluginDirectory` object which can be + used to retrieve addresses of other plugins. + These address can be used to send messages to other plugins. The address + objects are typed, so it is guaranteed that you cannot accidentially send + messages to a plugin that this plugin cannot handle. + - Another argument is the `CancellationToken`, which you can (but don't have + to) use to receive cancellation requests, once thin-edge.io tries to + shut down, to do cleanup work of allocated resources (such as closing file + handles, sending notifications about shutdown to external services, etc). 2. Implement `Plugin` for your plugin struct. -- cgit v1.2.3