summaryrefslogtreecommitdiffstats
path: root/crates/core/tedge_api/src/message.rs
diff options
context:
space:
mode:
authorMarcel Müller <m.mueller@ifm.com>2022-04-29 09:30:10 +0200
committerMarcel Müller <m.mueller@ifm.com>2022-05-05 15:06:20 +0200
commitabab64c856c0c299b13fcc1857a5b78f449e4a9e (patch)
tree1dfacf02e269b25328275367f20c70369a2797db /crates/core/tedge_api/src/message.rs
parenta398c9783b54892aa1f625685beb7f153d7f7e6d (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.rs131
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));
+ }
+}