settings/service.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
// 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
}