use crate::events::error::EventError;
use crate::identity::ComponentIdentity;
use fidl::endpoints::ServerEnd;
use fidl::prelude::*;
use fidl_table_validation::ValidFidlTable;
use moniker::ExtendedMoniker;
use std::sync::Arc;
use {
fidl_fuchsia_component as fcomponent, fidl_fuchsia_inspect as finspect,
fidl_fuchsia_logger as flogger,
};
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum EventType {
LogSinkRequested,
InspectSinkRequested,
}
impl AsRef<str> for EventType {
fn as_ref(&self) -> &str {
match &self {
Self::LogSinkRequested => "log_sink_requested",
Self::InspectSinkRequested => "inspect_sink_requested",
}
}
}
#[derive(Debug)]
pub struct Event {
pub payload: EventPayload,
pub timestamp: zx::BootInstant,
}
impl Event {
pub fn ty(&self) -> EventType {
match &self.payload {
EventPayload::LogSinkRequested(_) => EventType::LogSinkRequested,
EventPayload::InspectSinkRequested(_) => EventType::InspectSinkRequested,
}
}
}
#[derive(Debug)]
pub enum EventPayload {
LogSinkRequested(LogSinkRequestedPayload),
InspectSinkRequested(InspectSinkRequestedPayload),
}
pub struct InspectSinkRequestedPayload {
pub component: Arc<ComponentIdentity>,
pub request_stream: finspect::InspectSinkRequestStream,
}
impl std::fmt::Debug for InspectSinkRequestedPayload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InspectSinkRequestedPayload").field("component", &self.component).finish()
}
}
pub struct LogSinkRequestedPayload {
pub component: Arc<ComponentIdentity>,
pub request_stream: flogger::LogSinkRequestStream,
}
impl std::fmt::Debug for LogSinkRequestedPayload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LogSinkRequestedPayload").field("component", &self.component).finish()
}
}
#[derive(Debug, ValidFidlTable)]
#[fidl_table_src(fcomponent::EventHeader)]
#[fidl_table_strict]
pub struct ValidatedEventHeader {
event_type: fcomponent::EventType,
component_url: String,
moniker: String,
timestamp: zx::BootInstant,
}
#[derive(Debug, ValidFidlTable)]
#[fidl_table_src(fcomponent::Event)]
#[fidl_table_strict]
pub struct ValidatedEvent {
pub header: ValidatedEventHeader,
pub payload: fcomponent::EventPayload,
}
impl TryFrom<fcomponent::Event> for Event {
type Error = EventError;
fn try_from(event: fcomponent::Event) -> Result<Event, Self::Error> {
if let Ok(event) = ValidatedEvent::try_from(event) {
let identity = ComponentIdentity::new(
ExtendedMoniker::parse_str(&event.header.moniker)?,
&event.header.component_url,
);
match event.header.event_type {
fcomponent::EventType::CapabilityRequested => match event.payload {
fcomponent::EventPayload::CapabilityRequested(capability_requested) => {
let name =
capability_requested.name.ok_or(EventError::MissingField("name"))?;
if name == flogger::LogSinkMarker::PROTOCOL_NAME {
let capability = capability_requested
.capability
.ok_or(EventError::MissingField("capability"))?;
let request_stream =
ServerEnd::<flogger::LogSinkMarker>::new(capability).into_stream();
Ok(Event {
timestamp: event.header.timestamp,
payload: EventPayload::LogSinkRequested(LogSinkRequestedPayload {
component: Arc::new(identity),
request_stream,
}),
})
} else if name == finspect::InspectSinkMarker::PROTOCOL_NAME {
let capability = capability_requested
.capability
.ok_or(EventError::MissingField("capability"))?;
let request_stream =
ServerEnd::<finspect::InspectSinkMarker>::new(capability)
.into_stream();
Ok(Event {
timestamp: event.header.timestamp,
payload: EventPayload::InspectSinkRequested(
InspectSinkRequestedPayload {
component: Arc::new(identity),
request_stream,
},
),
})
} else {
Err(EventError::IncorrectName {
received: name,
expected: flogger::LogSinkMarker::PROTOCOL_NAME,
})
}
}
_ => Err(EventError::UnknownResult(event.payload)),
},
_ => Err(EventError::InvalidEventType(event.header.event_type)),
}
} else {
Err(EventError::MissingField("Payload or header is missing"))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::logs::testing::create_log_sink_requested_event;
use assert_matches::assert_matches;
use fidl_fuchsia_logger::LogSinkMarker;
use {fidl_fuchsia_component as fcomponent, fidl_fuchsia_inspect as finspect};
fn create_inspect_sink_requested_event(
target_moniker: String,
target_url: String,
capability: zx::Channel,
) -> fcomponent::Event {
fcomponent::Event {
header: Some(fcomponent::EventHeader {
event_type: Some(fcomponent::EventType::CapabilityRequested),
moniker: Some(target_moniker),
component_url: Some(target_url),
timestamp: Some(zx::BootInstant::get()),
..Default::default()
}),
payload: Some(fcomponent::EventPayload::CapabilityRequested(
fcomponent::CapabilityRequestedPayload {
name: Some(finspect::InspectSinkMarker::PROTOCOL_NAME.into()),
capability: Some(capability),
..Default::default()
},
)),
..Default::default()
}
}
#[fuchsia::test]
fn inspect_sink_capability_requested() {
let _exec = fuchsia_async::LocalExecutor::new();
let (_, inspect_sink_server_end) =
fidl::endpoints::create_endpoints::<finspect::InspectSinkMarker>();
let event = create_inspect_sink_requested_event(
"a/b".into(),
"".into(),
inspect_sink_server_end.into_channel(),
);
let actual = event.try_into().unwrap();
assert_matches!(actual, Event { payload, .. } => {
assert_matches!(payload,
EventPayload::InspectSinkRequested(InspectSinkRequestedPayload {
component, ..
}) => {
assert_eq!(component, ComponentIdentity::from(vec!["a", "b"]).into());
})
});
}
#[fuchsia::test]
fn log_sink_capability_requested() {
let _exec = fuchsia_async::LocalExecutor::new();
let (_, log_sink_server_end) = fidl::endpoints::create_endpoints::<LogSinkMarker>();
let event = create_log_sink_requested_event(
"a/b".into(),
"".into(),
log_sink_server_end.into_channel(),
);
let actual = event.try_into().unwrap();
assert_matches!(actual, Event { payload, .. } => {
assert_matches!(payload,
EventPayload::LogSinkRequested(LogSinkRequestedPayload {
component, ..
}) => {
assert_eq!(component, ComponentIdentity::from(vec!["a", "b"]).into());
})
});
}
}