| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Service-wide MessageHub definition. |
| //! |
| //! The service mod defines a MessageHub (and associated Address, Payload, and |
| //! Role namespaces) to facilitate service-wide communication. All |
| //! communication, both intra-component and inter-component, should be sent |
| //! through this hub. The address space of this MessageHub allows any component |
| //! to be reached when given a public address. The Role space allows granular |
| //! message filtering and audience targeting. |
| //! |
| //! The static Address and Role definitions below provide a way to reference |
| //! such values at build time. However, many use-cases that rely on these |
| //! features can be done through values generated at runtime instead. Care |
| //! should be taken before expanding either enumeration. |
| //! |
| //! Currently, service communication happens in a number of domain-specific |
| //! message hubs located in the internal mod. Communication from these hubs |
| //! should migrate here over time. |
| |
| use crate::base::SettingType; |
| use crate::handler::{base as handler, setting_handler as controller}; |
| use crate::message::message_hub; |
| use crate::{agent, event, job, storage}; |
| |
| pub struct MessageHub; |
| |
| impl MessageHub { |
| pub(crate) fn create_hub() -> message::Delegate { |
| message_hub::MessageHub::create() |
| } |
| } |
| |
| pub(crate) mod message { |
| use crate::message::{base, delegate, message_client, messenger, receptor}; |
| |
| pub(crate) type Delegate = delegate::Delegate; |
| pub(crate) type Audience = base::Audience; |
| pub(crate) type Messenger = messenger::MessengerClient; |
| pub(crate) type MessageError = base::MessageError; |
| pub(crate) type MessageEvent = base::MessageEvent; |
| pub(crate) type MessageClient = message_client::MessageClient; |
| pub(crate) type MessengerType = base::MessengerType; |
| pub(crate) type Receptor = receptor::Receptor; |
| pub(crate) type Signature = base::Signature; |
| } |
| |
| /// The `Address` enumeration defines a namespace for entities that can be |
| /// reached by a predefined name. Care should be taken when adding new child |
| /// namespaces here. Each address can only belong to a single entity. |
| /// Most communication can be instead facilitated with a messenger's signature, |
| /// which is available at messenger creation time. |
| #[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)] |
| pub enum Address { |
| Handler(SettingType), |
| EventSource(event::Address), |
| Storage, |
| /// This value is reserved for testing purposes. |
| #[cfg(test)] |
| Test(u64), |
| } |
| |
| /// The types of data that can be sent through the service `MessageHub`. This |
| /// enumeration is meant to provide a top level definition. Further definitions |
| /// for particular domains should be located in the appropriate mod. |
| #[derive(Clone, PartialEq, Debug)] |
| pub enum Payload { |
| /// The Setting type captures communication pertaining to settings, |
| /// including requests to access/change settings and the associated |
| /// responses. |
| Setting(handler::Payload), |
| /// The communication to and from a controller to handle requests and |
| /// lifetime. |
| Controller(controller::Payload), |
| /// Agent payloads contain communication between the agent authority and individual agents. |
| Agent(agent::Payload), |
| /// Event payloads contain data about events that occur throughout the system. |
| Event(event::Payload), |
| /// Job payloads contain information related to new sources of jobs to be executed. |
| Job(job::Payload), |
| /// Storage payloads contain read and write requests to storage and their responses. |
| Storage(storage::Payload), |
| /// This value is reserved for testing purposes. |
| #[cfg(test)] |
| Test(test::Payload), |
| } |
| |
| #[cfg(test)] |
| pub(crate) mod test { |
| use crate::audio::types::AudioInfo; |
| use crate::payload_convert; |
| |
| /// This payload should be expanded to include any sort of data that tests would send that is |
| /// outside the scope of production payloads. |
| #[derive(PartialEq, Clone, Debug)] |
| pub enum Payload { |
| Integer(i64), |
| Audio(AudioInfo), |
| } |
| |
| // Conversions for Handler Payload. |
| payload_convert!(Test, Payload); |
| } |
| |
| /// A trait implemented by payloads for extracting the payload and associated |
| /// [`message::MessageClient`] from a [`crate::message::base::MessageEvent`]. |
| pub(crate) trait TryFromWithClient<T>: Sized { |
| type Error; |
| |
| fn try_from_with_client(value: T) -> Result<(Self, message::MessageClient), Self::Error>; |
| } |
| |
| /// The payload_convert macro helps convert between the domain-specific payloads |
| /// (variants of [`Payload`]) and the [`Payload`] container(to/from) & MessageHub |
| /// MessageEvent (from). The first matcher is the [`Payload`] variant name where |
| /// the payload type can be found. Note that this is the direct variant name |
| /// and not fully qualified. The second matcher is the domain-specific payload |
| /// type. |
| /// |
| /// The macro implements the following in a mod called convert: |
| /// - Into from domain Payload to service MessageHub Payload |
| /// - TryFrom from service MessageHub Payload to domain Payload |
| /// - TryFromWithClient from service MessageHub MessageEvent to domain Payload |
| /// and client. |
| /// - TryFromWithClient from a service MessageHub MessageEvent option to domain |
| /// Payload and client. |
| #[macro_export] |
| macro_rules! payload_convert { |
| ($service_payload_type:ident, $payload_type:ty) => { |
| pub(super) mod convert { |
| use super::*; |
| use $crate::service; |
| use $crate::service::TryFromWithClient; |
| |
| impl From<$payload_type> for service::Payload { |
| fn from(payload: $payload_type) -> service::Payload { |
| paste::paste! { |
| service::Payload::[<$service_payload_type>](payload) |
| } |
| } |
| } |
| |
| impl TryFrom<service::Payload> for $payload_type { |
| type Error = String; |
| |
| fn try_from(value: service::Payload) -> Result<Self, Self::Error> { |
| paste::paste! { |
| match value { |
| service::Payload::[<$service_payload_type>](payload) => Ok(payload), |
| _=> Err(format!("unexpected payload encountered: {:?}", value)), |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<service::message::MessageEvent> for $payload_type { |
| type Error = String; |
| |
| fn try_from(value: service::message::MessageEvent) -> Result<Self, Self::Error> { |
| paste::paste! { |
| if let service::message::MessageEvent::Message(payload, _) = value { |
| Payload::try_from(payload) |
| } else { |
| Err(String::from("wrong message type")) |
| } |
| } |
| } |
| } |
| |
| impl TryFromWithClient<service::message::MessageEvent> for $payload_type { |
| type Error = String; |
| |
| fn try_from_with_client( |
| value: service::message::MessageEvent, |
| ) -> Result<(Self, service::message::MessageClient), Self::Error> { |
| if let service::message::MessageEvent::Message(payload, client) = value { |
| Ok((Payload::try_from(payload)?, client)) |
| } else { |
| Err(String::from("wrong message type")) |
| } |
| } |
| } |
| } |
| }; |
| } |
| |
| #[cfg(test)] |
| pub(crate) async fn build_event_listener(delegate: &message::Delegate) -> message::Receptor { |
| delegate.create_sink().await.expect("Should be able to retrieve receptor").1 |
| } |