diff options
author | Marcel Müller <m.mueller@ifm.com> | 2022-04-29 09:30:10 +0200 |
---|---|---|
committer | Marcel Müller <m.mueller@ifm.com> | 2022-05-05 15:06:20 +0200 |
commit | abab64c856c0c299b13fcc1857a5b78f449e4a9e (patch) | |
tree | 1dfacf02e269b25328275367f20c70369a2797db /crates/core/tedge_api/src/message.rs | |
parent | a398c9783b54892aa1f625685beb7f153d7f7e6d (diff) |
Add AnyMessage & AnyMessages
This patch adds the ability to send any kind of messages to plugins that
declare handling `AnyMessage` + `AnyMessages`.
It does it with these changes:
- Remove the `Message::Reply` associated type
- This allows `Message` to be used as a trait object
- Add the `MessageType` which allows to identify messages
- This permits `AnyMessage` to be received by plugins
Signed-off-by: Marcel Müller <m.mueller@ifm.com>
Diffstat (limited to 'crates/core/tedge_api/src/message.rs')
-rw-r--r-- | crates/core/tedge_api/src/message.rs | 131 |
1 files changed, 123 insertions, 8 deletions
diff --git a/crates/core/tedge_api/src/message.rs b/crates/core/tedge_api/src/message.rs index decb41e5..2fd5faa4 100644 --- a/crates/core/tedge_api/src/message.rs +++ b/crates/core/tedge_api/src/message.rs @@ -1,19 +1,134 @@ -use crate::plugin::Message; +use crate::{address::AnyMessageBox, plugin::Message}; +/// A message that can contain any other message +/// +/// This is solely used in conjunction with [`AnyMessages`](crate::plugin::AnyMessages) and should not generally be used +/// otherwise. +/// +/// To construct it, you will need to have a message and call [`AnyMessage::from_message`] #[derive(Debug)] -/// A message which cannot be constructed and thus can be used when no reply is expected. -pub enum NoReply {} +pub struct AnyMessage(pub(crate) AnyMessageBox); -impl Message for NoReply { - type Reply = NoReply; +impl std::ops::Deref for AnyMessage { + type Target = dyn Message; + + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl AnyMessage { + /// Construct a new [`AnyMessage`] from a message + pub fn from_message<M: Message>(m: M) -> Self { + AnyMessage(Box::new(m)) + } + + /// Try to downcast this message to a specific message + pub fn downcast<M: Message>(self) -> Result<M, Self> { + Ok(*self.0.downcast().map_err(AnyMessage)?) + } + + /// Take out the raw boxed message + /// + /// Note + /// + /// This is an advanced API and should only be used if needed. + /// Prefer using `AnyMessage::downcast` if possible + pub fn into_raw(self) -> AnyMessageBox { + self.0 + } +} + +impl Message for AnyMessage {} + +/// The type of a message as used by `tedge_api` to represent a type +#[derive(Debug, Clone)] +pub struct MessageType { + name: &'static str, + kind: MessageKind, +} + +#[derive(Debug, Clone)] +enum MessageKind { + Wildcard, + Typed(std::any::TypeId), +} + +impl MessageType { + /// Does this [`MessageType`] satisfy another [`MessageType`] + /// + /// ## Note + /// A message type from [`AnyMessage`] acts as a 'wildcard', being satisfied by any other type + /// (even itself). + /// The reverse is not true, a specific type cannot be satisfied by a 'wildcard' (i.e. + /// [`AnyMessage`]). + /// + /// [`MessageType::satisfy`] is thus reflexive but not symmetric nor transitive, meaning that it cannot be + /// used for `PartialEq`. + #[must_use] + pub fn satisfy(&self, other: &Self) -> bool { + match (&self.kind, &other.kind) { + (MessageKind::Wildcard, _) => true, + (_, MessageKind::Wildcard) => false, + (MessageKind::Typed(ty_l), MessageKind::Typed(ty_r)) => ty_l.eq(ty_r), + } + } + + /// Get the [`MessageType`] for a `M`:[`Message`] + #[must_use] + pub fn for_message<M: Message>() -> Self { + let id = std::any::TypeId::of::<M>(); + MessageType { + name: std::any::type_name::<M>(), + kind: if id == std::any::TypeId::of::<AnyMessage>() { + MessageKind::Wildcard + } else { + MessageKind::Typed(id) + }, + } + } + + /// Get the type's name + #[must_use] + pub fn name(&self) -> &str { + self.name + } } /// A message to tell the core to stop thin-edge #[derive(Debug)] pub struct StopCore; -impl Message for StopCore { - type Reply = NoReply; -} +impl Message for StopCore {} crate::make_receiver_bundle!(pub struct CoreMessages(StopCore)); + +#[cfg(test)] +mod tests { + use crate::Message; + + use super::{AnyMessage, MessageType}; + + #[derive(Debug)] + struct Bar; + + impl Message for Bar {} + + #[derive(Debug)] + struct Foo; + + impl Message for Foo {} + + #[test] + fn assert_satisfy_laws_for_types() { + let bar_type = MessageType::for_message::<Bar>(); + let any_message_type = MessageType::for_message::<AnyMessage>(); + let foo_type = MessageType::for_message::<Foo>(); + + assert!(any_message_type.satisfy(&bar_type)); + assert!(any_message_type.satisfy(&foo_type)); + + assert!(!bar_type.satisfy(&any_message_type)); + assert!(!bar_type.satisfy(&foo_type)); + } +} |