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
}