blob: 8cd2b70906c1028fddfcafeaaf87c9ffa1eb7902 [file] [log] [blame]
// Copyright 2020 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.
use {
element_management::Element,
fidl_fuchsia_session::{
AnnotationError, ElementControllerRequest, ElementControllerRequestStream,
},
fuchsia_async as fasync,
futures::TryStreamExt,
};
/// A trait which is used by the ElementRepository to respond to incoming events.
pub(crate) trait EventHandler {
/// Called when a new element is added to the repository.
fn add_element(&mut self, element: Element, stream: Option<ElementControllerRequestStream>);
/// An optional method which will be called when the repository receives a shutdown event
/// Implementers of this method can perform any extra cleanup if they need to.
fn shutdown(&mut self) {}
}
pub(crate) struct ElementEventHandler {
elements: Vec<ElementHolder>,
}
/// The ElementEventHandler is a concrete implementation of the EventHandler trait which
/// manages the lifecycle of elements.
impl ElementEventHandler {
pub fn new() -> ElementEventHandler {
ElementEventHandler { elements: vec![] }
}
}
impl EventHandler for ElementEventHandler {
fn add_element(&mut self, element: Element, stream: Option<ElementControllerRequestStream>) {
self.elements.push(ElementHolder::new(element, stream));
}
// We do not implement the shutdown method because we do not have any cleanup to do at this time.
}
struct ElementHolder {
_element: Option<Element>,
}
/// An ElementHolder helps to manage the lifetime of an Element.
///
/// If an element has an associated ElementController it will serve the ElementController protocol.
/// If not, it will just hold the elemen to keep it alive.
impl ElementHolder {
fn new(element: Element, stream: Option<ElementControllerRequestStream>) -> ElementHolder {
//TODO(47078): Watch the element and respond when it closes.
if let Some(stream) = stream {
ElementHolder::spawn_request_stream_server(element, stream);
return ElementHolder::empty();
}
ElementHolder { _element: Some(element) }
}
fn spawn_request_stream_server(
mut element: Element,
mut stream: ElementControllerRequestStream,
) {
//TODO(47078): need to shut down the request stream when the component the element represents dies.
fasync::spawn_local(async move {
while let Ok(Some(request)) = stream.try_next().await {
match request {
ElementControllerRequest::SetAnnotations { annotations, responder } => {
let _ = responder.send(
&mut element
.set_annotations(annotations)
.map_err(|_: anyhow::Error| AnnotationError::Rejected),
);
}
ElementControllerRequest::GetAnnotations { responder } => {
let _ = responder.send(
&mut element
.get_annotations()
.map_err(|_: anyhow::Error| AnnotationError::NotFound),
);
}
}
}
});
}
// An empty ElementHolder is returned to signal that we are managing the lifetime of this
// element but we are doing so via request stream loop.
fn empty() -> ElementHolder {
ElementHolder { _element: None }
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::element_repository::testing_utils::{init_logger, make_mock_element},
anyhow::Error,
fidl::endpoints::create_proxy_and_stream,
fidl_fuchsia_session::{Annotation, Annotations, ElementControllerMarker, Value},
};
#[test]
fn add_element_adds_to_the_collection() {
init_logger();
let (element, _channel) = make_mock_element();
let mut handler = ElementEventHandler::new();
handler.add_element(element, None);
assert_eq!(handler.elements.len(), 1);
}
#[test]
fn element_holder_without_stream_new() {
init_logger();
let (element, _channel) = make_mock_element();
let holder = ElementHolder::new(element, None);
assert!(holder._element.is_some());
}
#[fasync::run_singlethreaded(test)]
async fn element_holder_with_stream_manages_lifetime_via_stream() -> Result<(), Error> {
init_logger();
let (element, _channel) = make_mock_element();
let (_proxy, stream) = create_proxy_and_stream::<ElementControllerMarker>()?;
let holder = ElementHolder::new(element, Some(stream));
assert!(holder._element.is_none());
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn element_holder_listens_to_event_stream() -> Result<(), Error> {
init_logger();
let (element, _channel) = make_mock_element();
let (proxy, stream) = create_proxy_and_stream::<ElementControllerMarker>()?;
let _holder = ElementHolder::new(element, Some(stream));
let annotation = Annotation {
key: "foo".to_string(),
value: Some(Box::new(Value::Text("bar".to_string()))),
};
let _ =
proxy.set_annotations(Annotations { custom_annotations: Some(vec![annotation]) }).await;
let result = proxy.get_annotations().await?;
let key = &result.unwrap().custom_annotations.unwrap()[0].key;
assert_eq!(key, "foo");
Ok(())
}
}