blob: 602931b860055dba82bbd4f8902a6e59bdb53b1b [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 {
crate::{
availability::AvailabilityServiceVisitor,
capability_source::{
AggregateCapability, CapabilitySourceInterface, ComponentCapability, InternalCapability,
},
collection::{AggregateServiceProvider, CollectionCapabilityProvider},
component_instance::{
ComponentInstanceInterface, ExtendedInstanceInterface, ResolvedInstanceInterface,
TopInstanceInterface,
},
error::RoutingError,
DebugRouteMapper, RegistrationDecl,
},
cm_rust::{
name_mappings_to_map, Availability, CapabilityDecl, CapabilityDeclCommon, CapabilityName,
ExposeDecl, ExposeDeclCommon, ExposeServiceDecl, ExposeSource, ExposeTarget, OfferDecl,
OfferDeclCommon, OfferServiceDecl, OfferSource, OfferTarget, RegistrationDeclCommon,
RegistrationSource, ServiceDecl, UseDecl, UseDeclCommon, UseServiceDecl, UseSource,
},
derivative::Derivative,
from_enum::FromEnum,
moniker::{AbsoluteMoniker, ChildMoniker, ChildMonikerBase},
std::collections::{HashMap, HashSet},
std::{marker::PhantomData, sync::Arc},
};
/// Routes a capability to its source based on a particular routing strategy.
///
/// Callers invoke builder-like methods to construct the routing strategy.
///
/// # Example
/// ```
/// let router = RoutingStrategy::new()
/// .use_::<UseProtocolDecl>()
/// .offer::<OfferProtocolDecl>()
/// .expose::<ExposeProtocolDecl>();
/// ```
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Copy(bound = ""))]
pub struct RoutingStrategy<Start = (), Offer = (), Expose = ()>(
PhantomData<(Start, Offer, Expose)>,
);
impl RoutingStrategy {
/// Creates a new `Router` that must be configured with a routing strategy by
/// calling the various builder-like methods.
pub const fn new() -> Self {
RoutingStrategy(PhantomData)
}
/// Configure the `Router` to start routing from a `Use` declaration.
pub const fn use_<U>(self) -> RoutingStrategy<Use<U>, (), ()> {
RoutingStrategy(PhantomData)
}
/// Configure the `Router` to start routing from an environment `Registration`
/// declaration.
pub const fn registration<R>(self) -> RoutingStrategy<Registration<R>, (), ()> {
RoutingStrategy(PhantomData)
}
}
// Implement the Use routing strategy. In this strategy, there is neither no
// Expose nor Offer. This strategy allows components to route capabilities to
// themselves.
impl<U> RoutingStrategy<Use<U>, (), ()>
where
U: UseDeclCommon + ErrorNotFoundFromParent + Into<UseDecl> + Clone,
{
/// Configure the `Router` to route from a `Use` declaration to a matching `Offer`
/// declaration.
pub fn offer<O>(self) -> RoutingStrategy<Use<U>, Offer<O>, ()> {
RoutingStrategy(PhantomData)
}
/// Routes a capability from its `Use` declaration to its source by
/// capabilities declarations.
/// `sources` defines what are the valid sources of the capability.
/// See [`AllowedSourcesBuilder`].
/// `visitor` is invoked for each `Capability` declaration if `sources`
/// permits.
pub async fn route<C, S, V, M>(
self,
use_decl: U,
target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
mapper.add_use(target.abs_moniker().clone(), use_decl.clone().into());
let target_capabilities = target.lock_resolved_state().await?.capabilities();
Ok(CapabilitySourceInterface::<C>::Component {
capability: sources.find_component_source(
use_decl.source_name(),
target.abs_moniker(),
&target_capabilities,
visitor,
mapper,
)?,
component: target.as_weak(),
})
}
}
impl<R> RoutingStrategy<Registration<R>, (), ()> {
/// Configure the `Router` to route from an environment `Registration` declaration to a
/// matching `Offer` declaration.
pub const fn offer<O>(self) -> RoutingStrategy<Registration<R>, Offer<O>, ()> {
RoutingStrategy(PhantomData)
}
}
impl<S, O> RoutingStrategy<S, Offer<O>, ()> {
/// Configure the `Router` to route from an `Offer` declaration to a matching `Expose`
/// declaration.
pub const fn expose<E>(self) -> RoutingStrategy<S, Offer<O>, Expose<E>> {
RoutingStrategy(PhantomData)
}
}
// Implement the Use -> Offer routing strategy. In this strategy, there is no Expose.
impl<U, O> RoutingStrategy<Use<U>, Offer<O>, ()>
where
U: UseDeclCommon + ErrorNotFoundFromParent + Into<UseDecl> + Clone,
O: OfferDeclCommon + ErrorNotFoundFromParent + FromEnum<OfferDecl> + Into<OfferDecl> + Clone,
{
pub async fn route<C, S, V, M>(
self,
use_decl: U,
use_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources,
V: OfferVisitor<OfferDecl = O>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
M: DebugRouteMapper + 'static,
{
let mut route = vec![];
self.route_extended_strategy(use_decl, use_target, sources, visitor, mapper, &mut route)
.await
}
/// Routes a capability from its `Use` declaration to its source by following `Offer`
/// declarations.
/// `sources` defines what are the valid sources of the capability.
/// See [`AllowedSourcesBuilder`].
/// `visitor` is invoked for each `Offer` declaration in the routing path, as well as the final
/// `Capability` declaration if `sources` permits.
pub async fn route_extended_strategy<C, S, V, M>(
self,
use_decl: U,
use_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources,
V: OfferVisitor<OfferDecl = O>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
M: DebugRouteMapper + 'static,
{
let availability = use_decl.availability().clone();
match Use::route(use_decl, use_target, &sources, visitor, mapper).await? {
UseResult::Source(source) => return Ok(source),
UseResult::FromParent(offers, component) => {
if offers.len() == 1 {
let offer_opt: Option<&O> = offers.first();
self.route_from_offer(
offer_opt.unwrap().clone(),
component,
sources,
visitor,
mapper,
route,
)
.await
} else {
create_aggregate_source(availability, offers, component, mapper.clone())
}
}
UseResult::FromChild(_use_decl, _component) => {
unreachable!("found use from child but capability cannot be exposed")
}
}
}
/// Routes a capability from its `Offer` declaration to its source by following `Offer`
/// declarations.
/// `sources` defines what are the valid sources of the capability.
/// See [`AllowedSourcesBuilder`].
/// `visitor` is invoked for each `Offer` declaration in the routing path, as well as the final
/// `Capability` declaration if `sources` permits.
pub async fn route_from_offer<C, S, V, M>(
self,
offer_decl: O,
offer_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources,
V: OfferVisitor<OfferDecl = O>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
M: DebugRouteMapper,
{
match Offer::route(offer_decl, offer_target, &sources, visitor, mapper, route).await? {
OfferResult::Source(source) => Ok(source),
OfferResult::OfferFromChild(_, _) => {
// This condition should not happen since cm_fidl_validator ensures
// that this kind of declaration cannot exist.
unreachable!("found offer from child but capability cannot be exposed")
}
OfferResult::OfferFromCollection(_, _, _) => {
// This condition should not happen since cm_fidl_validator ensures
// that this kind of declaration cannot exist.
unreachable!("found offer from collection but capability cannot be exposed")
}
OfferResult::OfferFromAggregate(_, _) => {
// This condition should not happen since cm_fidl_validator ensures
// that this kind of declaration cannot exist.
unreachable!("found offer from aggregate but capability cannot be exposed")
}
}
}
}
// Implement the Use -> Offer -> Expose routing strategy. This is the full, common routing
// strategy.
impl<U, O, E> RoutingStrategy<Use<U>, Offer<O>, Expose<E>>
where
U: UseDeclCommon
+ ErrorNotFoundFromParent
+ ErrorNotFoundInChild
+ Into<UseDecl>
+ Clone
+ 'static,
O: OfferDeclCommon
+ ErrorNotFoundFromParent
+ ErrorNotFoundInChild
+ FromEnum<OfferDecl>
+ Into<OfferDecl>
+ Clone
+ 'static,
E: ExposeDeclCommon
+ ErrorNotFoundInChild
+ FromEnum<ExposeDecl>
+ Into<ExposeDecl>
+ Clone
+ 'static,
{
pub async fn route<C, S, V, M>(
self,
use_decl: U,
use_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: OfferVisitor<OfferDecl = O>,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
let mut route = vec![];
self.route_extended_strategy(use_decl, use_target, sources, visitor, mapper, &mut route)
.await
}
/// Routes a capability from its `Use` declaration to its source by following `Offer` and
/// `Expose` declarations.
/// `sources` defines what are the valid sources of the capability.
/// See [`AllowedSourcesBuilder`].
/// `visitor` is invoked for each `Offer` and `Expose` declaration in the routing path, as well
/// as the final `Capability` declaration if `sources` permits.
pub async fn route_extended_strategy<C, S, V, M>(
self,
use_decl: U,
use_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: OfferVisitor<OfferDecl = O>,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
let availability = use_decl.availability().clone();
match Use::route(use_decl, use_target.clone(), &sources, visitor, mapper).await? {
UseResult::Source(source) => return Ok(source),
UseResult::FromParent(offers, component) => {
if offers.len() == 1 {
let offer_opt: Option<&O> = offers.first();
self.route_from_offer_extended(
offer_opt.unwrap().clone(),
component,
sources,
visitor,
mapper,
route,
)
.await
} else {
create_aggregate_source(availability, offers, component, mapper.clone())
}
}
UseResult::FromChild(use_decl, child_component) => {
let child_exposes = child_component.lock_resolved_state().await?.exposes();
let expose_decl = find_matching_expose(use_decl.source_name(), &child_exposes)
.cloned()
.ok_or_else(|| {
let child_moniker = child_component
.instanced_child_moniker()
.expect("InstancedChildMoniker should exist")
.without_instance_id();
<U as ErrorNotFoundInChild>::error_not_found_in_child(
use_target.abs_moniker().clone(),
child_moniker,
use_decl.source_name().clone(),
)
})?;
self.route_from_expose_extended(
expose_decl,
child_component,
sources,
visitor,
mapper,
route,
)
.await
}
}
}
}
// Implement the Registration -> Offer -> Expose routing strategy. This is the strategy used
// to route capabilities registered in environments.
impl<R, O, E> RoutingStrategy<Registration<R>, Offer<O>, Expose<E>>
where
R: RegistrationDeclCommon
+ ErrorNotFoundFromParent
+ ErrorNotFoundInChild
+ Into<RegistrationDecl>
+ Clone
+ 'static,
O: OfferDeclCommon
+ ErrorNotFoundFromParent
+ ErrorNotFoundInChild
+ FromEnum<OfferDecl>
+ Into<OfferDecl>
+ Clone
+ 'static,
E: ExposeDeclCommon
+ ErrorNotFoundInChild
+ FromEnum<ExposeDecl>
+ Into<ExposeDecl>
+ Clone
+ 'static,
{
pub async fn route<C, S, V, M>(
self,
registration_decl: R,
registration_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: OfferVisitor<OfferDecl = O>,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
let mut route = vec![];
self.route_extended_strategy(
registration_decl,
registration_target,
sources,
visitor,
mapper,
&mut route,
)
.await
}
/// Routes a capability from its environment `Registration` declaration to its source by
/// following `Offer` and `Expose` declarations.
/// `sources` defines what are the valid sources of the capability.
/// See [`AllowedSourcesBuilder`].
/// `visitor` is invoked for each `Offer` and `Expose` declaration in the routing path, as
/// well as the final `Capability` declaration if `sources` permits.
pub async fn route_extended_strategy<C, S, V, M>(
self,
registration_decl: R,
registration_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: OfferVisitor<OfferDecl = O>,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
match Registration::route(registration_decl, registration_target, &sources, visitor, mapper)
.await?
{
RegistrationResult::Source(source) => return Ok(source),
RegistrationResult::FromParent(offers, component) => {
if offers.len() == 1 {
let offer_opt: Option<&O> = offers.first();
self.route_from_offer_extended(
offer_opt.unwrap().clone(),
component,
sources,
visitor,
mapper,
route,
)
.await
} else {
// capabilities used in a registgration are always required.
create_aggregate_source(
Availability::Required,
offers,
component,
mapper.clone(),
)
}
}
RegistrationResult::FromChild(expose, component) => {
self.route_from_expose_extended(expose, component, sources, visitor, mapper, route)
.await
}
}
}
}
// Common offer routing shared between Registration and Use routing.
impl<B, O, E> RoutingStrategy<B, Offer<O>, Expose<E>>
where
B: Send + Sync + 'static,
O: OfferDeclCommon
+ ErrorNotFoundFromParent
+ ErrorNotFoundInChild
+ FromEnum<OfferDecl>
+ Into<OfferDecl>
+ Clone
+ 'static,
E: ExposeDeclCommon
+ ErrorNotFoundInChild
+ FromEnum<ExposeDecl>
+ Into<ExposeDecl>
+ Clone
+ 'static,
{
pub async fn route_from_offer<C, S, V, M>(
self,
offer_decl: O,
offer_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: OfferVisitor<OfferDecl = O>,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
let mut route = vec![];
self.route_from_offer_extended(
offer_decl,
offer_target,
sources,
visitor,
mapper,
&mut route,
)
.await
}
/// Routes a capability from its `Offer` declaration to its source by following `Offer` and
/// `Expose` declarations.
/// `sources` defines what are the valid sources of the capability.
/// See [`AllowedSourcesBuilder`].
/// `visitor` is invoked for each `Offer` and `Expose` declaration in the routing path, as well
/// as the final `Capability` declaration if `sources` permits.
pub async fn route_from_offer_extended<C, S, V, M>(
self,
offer_decl: O,
offer_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: OfferVisitor<OfferDecl = O>,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
match Offer::route(offer_decl, offer_target, &sources, visitor, mapper, route).await? {
OfferResult::Source(source) => return Ok(source),
OfferResult::OfferFromChild(offer, component) => {
let offer_decl: OfferDecl = offer.clone().into();
let (expose, component) = change_directions(offer, component).await?;
let capability_source = self
.route_from_expose_extended(expose, component, sources, visitor, mapper, route)
.await?;
if let OfferDecl::Service(offer_service_decl) = offer_decl {
if offer_service_decl.source_instance_filter.is_some()
|| offer_service_decl.renamed_instances.is_some()
{
// TODO(https://fxbug.dev/97147) support collection sources as well.
if let CapabilitySourceInterface::Component { capability, component } =
capability_source
{
return Ok(CapabilitySourceInterface::<C>::FilteredService {
capability: capability,
component: component,
source_instance_filter: offer_service_decl
.source_instance_filter
.unwrap_or(vec![]),
instance_name_source_to_target: offer_service_decl
.renamed_instances
.map_or(HashMap::new(), name_mappings_to_map),
});
}
}
}
Ok(capability_source)
}
OfferResult::OfferFromCollection(offer_decl, collection_component, collection_name) => {
Ok(CapabilitySourceInterface::<C>::Collection {
capability: AggregateCapability::Service(offer_decl.source_name().clone()),
component: collection_component.as_weak(),
capability_provider: Box::new(CollectionCapabilityProvider {
router: self,
collection_name: collection_name.clone(),
collection_component: collection_component.as_weak(),
capability_name: offer_decl.source_name().clone(),
sources: sources.clone(),
visitor: visitor.clone(),
mapper: mapper.clone(),
}),
collection_name,
})
}
OfferResult::OfferFromAggregate(offer_decls, aggregation_component) => {
// TODO(102532): get optional to work with aggregate services
create_aggregate_source(
Availability::Required,
offer_decls,
aggregation_component,
mapper.clone(),
)
}
}
}
}
fn create_aggregate_source<C, O, M>(
starting_availability: Availability,
offer_decls: Vec<O>,
aggregation_component: Arc<C>,
mapper: M,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
O: OfferDeclCommon + FromEnum<OfferDecl> + Into<OfferDecl> + Clone,
M: DebugRouteMapper + 'static,
{
// Check that all of the service offers contain non-conflicting filter instances.
{
let mut seen_instances: HashSet<String> = HashSet::new();
for o in offer_decls.iter() {
if let OfferDecl::Service(offer_service_decl) = o.clone().into() {
match offer_service_decl.source_instance_filter {
None => {
return Err(RoutingError::unsupported_route_source(
"Aggregate offers must be of service capabilities with source_instance_filter set",
));
}
Some(allowed_instances) => {
for instance in allowed_instances.iter() {
if !seen_instances.insert(instance.clone()) {
return Err(RoutingError::unsupported_route_source(format!(
"Instance {} found in multiple offers of the same service.",
instance
)));
}
}
}
}
} else {
return Err(RoutingError::unsupported_route_source(
"Aggregate source must consist of only service capabilities",
));
}
}
}
let (source_name, offer_service_decls) = offer_decls.iter().fold(
(CapabilityName("".to_string()), Vec::<OfferServiceDecl>::new()),
|(_, mut decls), o| {
if let OfferDecl::Service(offer_service_decl) = o.clone().into() {
decls.push(offer_service_decl);
}
(o.source_name().clone(), decls)
},
);
// TODO(fxbug.dev/71881) Make the Collection CapabilitySourceInterface type generic
// for other types of aggregations.
Ok(CapabilitySourceInterface::<C>::Collection {
capability: AggregateCapability::Service(source_name),
component: aggregation_component.as_weak(),
collection_name: "".to_string(),
capability_provider: Box::new(AggregateServiceProvider::new(
offer_service_decls,
aggregation_component.as_weak(),
RoutingStrategy::new()
.use_::<UseServiceDecl>()
.offer::<OfferServiceDecl>()
.expose::<ExposeServiceDecl>(),
AllowedSourcesBuilder::<ServiceDecl>::new().component().collection(),
AvailabilityServiceVisitor(starting_availability.into()),
mapper,
)),
})
}
// Common expose routing shared between Registration and Use routing.
impl<B, O, E> RoutingStrategy<B, O, Expose<E>>
where
B: Send + Sync + 'static,
O: Send + Sync + 'static,
E: ExposeDeclCommon
+ ErrorNotFoundInChild
+ FromEnum<ExposeDecl>
+ Into<ExposeDecl>
+ Clone
+ 'static,
{
pub async fn route_from_expose<C, S, V, M>(
self,
expose_decl: E,
expose_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
let mut route = vec![];
self.route_from_expose_extended(
expose_decl,
expose_target,
sources,
visitor,
mapper,
&mut route,
)
.await
}
/// Routes a capability from its `Expose` declaration to its source by following `Expose`
/// declarations.
/// `sources` defines what are the valid sources of the capability.
/// See [`AllowedSourcesBuilder`].
/// `visitor` is invoked for each `Expose` declaration in the routing path, as well
/// as the final `Capability` declaration if `sources` permits.
pub async fn route_from_expose_extended<C, S, V, M>(
self,
expose_decl: E,
expose_target: Arc<C>,
sources: S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<CapabilitySourceInterface<C>, RoutingError>
where
C: ComponentInstanceInterface + 'static,
S: Sources + 'static,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
V: Clone + Send + Sync + 'static,
M: DebugRouteMapper + 'static,
{
match Expose::route(expose_decl, expose_target, &sources, visitor, mapper, route).await? {
ExposeResult::Source(source) => Ok(source),
ExposeResult::ExposeFromCollection(
expose_decl,
collection_component,
collection_name,
) => Ok(CapabilitySourceInterface::<C>::Collection {
capability: AggregateCapability::Service(expose_decl.source_name().clone()),
component: collection_component.as_weak(),
capability_provider: Box::new(CollectionCapabilityProvider {
router: self,
collection_name: collection_name.clone(),
collection_component: collection_component.as_weak(),
capability_name: expose_decl.source_name().clone(),
sources: sources.clone(),
visitor: visitor.clone(),
mapper: mapper.clone(),
}),
collection_name: collection_name.clone(),
}),
}
}
}
/// A trait for extracting the source of a capability.
pub trait Sources: Clone + Send + Sync {
/// The supported capability declaration type for namespace, component and built-in sources.
type CapabilityDecl;
/// Return the [`InternalCapability`] representing this framework capability source, or
/// [`RoutingError::UnsupportedRouteSource`] if unsupported.
fn framework_source<M>(
&self,
name: CapabilityName,
mapper: &mut M,
) -> Result<InternalCapability, RoutingError>
where
M: DebugRouteMapper;
/// Checks whether capability sources are supported, returning [`RoutingError::UnsupportedRouteSource`]
/// if they are not.
// TODO(fxb/61861): Add route mapping for capability sources.
fn capability_source(&self) -> Result<(), RoutingError>;
/// Checks whether collection sources are supported, returning [`ModelError::Unsupported`]
/// if they are not.
// TODO(fxb/61861): Consider adding route mapping for collection sources, although we won't need this
// for the route static analyzer.
fn collection_source(&self) -> Result<(), RoutingError>;
/// Checks whether namespace capability sources are supported.
fn is_namespace_supported(&self) -> bool;
/// Looks for a namespace capability in the list of capability sources.
/// If found, the declaration is visited by `visitor` and the declaration is wrapped
/// in a [`ComponentCapability`].
/// Returns [`RoutingError::UnsupportedRouteSource`] if namespace capabilities are unsupported.
fn find_namespace_source<V, M>(
&self,
name: &CapabilityName,
capabilities: &[CapabilityDecl],
visitor: &mut V,
mapper: &mut M,
) -> Result<Option<ComponentCapability>, RoutingError>
where
V: CapabilityVisitor<CapabilityDecl = Self::CapabilityDecl>,
M: DebugRouteMapper;
/// Looks for a built-in capability in the list of capability sources.
/// If found, the capability's name is wrapped in an [`InternalCapability`].
/// Returns [`RoutingError::UnsupportedRouteSource`] if built-in capabilities are unsupported.
fn find_builtin_source<V, M>(
&self,
name: &CapabilityName,
capabilities: &[CapabilityDecl],
visitor: &mut V,
mapper: &mut M,
) -> Result<Option<InternalCapability>, RoutingError>
where
V: CapabilityVisitor<CapabilityDecl = Self::CapabilityDecl>,
M: DebugRouteMapper;
/// Looks for a component capability in the list of capability sources for the component instance
/// with moniker `abs_moniker`.
/// If found, the declaration is visited by `visitor` and the declaration is wrapped
/// in a [`ComponentCapability`].
/// Returns [`RoutingError::UnsupportedRouteSource`] if component capabilities are unsupported.
fn find_component_source<V, M>(
&self,
name: &CapabilityName,
abs_moniker: &AbsoluteMoniker,
capabilities: &[CapabilityDecl],
visitor: &mut V,
mapper: &mut M,
) -> Result<ComponentCapability, RoutingError>
where
V: CapabilityVisitor<CapabilityDecl = Self::CapabilityDecl>,
M: DebugRouteMapper;
}
/// Defines which capability source types are supported.
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
pub struct AllowedSourcesBuilder<C>
where
C: CapabilityDeclCommon
+ FromEnum<CapabilityDecl>
+ Into<ComponentCapability>
+ Into<InternalCapability>
+ Clone,
{
framework: Option<fn(CapabilityName) -> InternalCapability>,
builtin: bool,
capability: bool,
collection: bool,
namespace: bool,
component: bool,
_decl: PhantomData<C>,
}
impl<
C: CapabilityDeclCommon
+ FromEnum<CapabilityDecl>
+ Into<ComponentCapability>
+ Into<InternalCapability>
+ Clone,
> AllowedSourcesBuilder<C>
{
/// Creates a new [`AllowedSourcesBuilder`] that does not allow any capability source types.
pub fn new() -> Self {
Self {
framework: None,
builtin: false,
capability: false,
collection: false,
namespace: false,
component: false,
_decl: PhantomData,
}
}
/// Allows framework capability source types (`from: "framework"` in `CML`).
pub fn framework(self, builder: fn(CapabilityName) -> InternalCapability) -> Self {
Self { framework: Some(builder), ..self }
}
/// Allows capability source types that originate from other capabilities (`from: "#storage"` in
/// `CML`).
pub fn capability(self) -> Self {
Self { capability: true, ..self }
}
/// Allows capability sources to originate from a collection.
pub fn collection(self) -> Self {
Self { collection: true, ..self }
}
/// Allows namespace capability source types, which are capabilities that are installed in
/// component_manager's incoming namespace.
pub fn namespace(self) -> Self {
Self { namespace: true, ..self }
}
/// Allows component capability source types (`from: "self"` in `CML`).
pub fn component(self) -> Self {
Self { component: true, ..self }
}
/// Allows built-in capability source types (`from: "parent"` in `CML` where the parent component_instance is
/// component_manager).
pub fn builtin(self) -> Self {
Self { builtin: true, ..self }
}
}
// Implementation of `Sources` that allows namespace, component, and/or built-in source
// types.
impl<C> Sources for AllowedSourcesBuilder<C>
where
C: CapabilityDeclCommon
+ FromEnum<CapabilityDecl>
+ Into<ComponentCapability>
+ Into<InternalCapability>
+ Into<CapabilityDecl>
+ Clone,
{
type CapabilityDecl = C;
fn framework_source<M>(
&self,
name: CapabilityName,
mapper: &mut M,
) -> Result<InternalCapability, RoutingError>
where
M: DebugRouteMapper,
{
let source = self
.framework
.as_ref()
.map(|b| b(name.clone()))
.ok_or_else(|| RoutingError::unsupported_route_source("framework"));
mapper.add_framework_capability(name);
source
}
fn capability_source(&self) -> Result<(), RoutingError> {
if self.capability {
Ok(())
} else {
Err(RoutingError::unsupported_route_source("capability"))
}
}
fn collection_source(&self) -> Result<(), RoutingError> {
if self.collection {
Ok(())
} else {
Err(RoutingError::unsupported_route_source("collection"))
}
}
fn is_namespace_supported(&self) -> bool {
self.namespace
}
fn find_namespace_source<V, M>(
&self,
name: &CapabilityName,
capabilities: &[CapabilityDecl],
visitor: &mut V,
mapper: &mut M,
) -> Result<Option<ComponentCapability>, RoutingError>
where
V: CapabilityVisitor<CapabilityDecl = Self::CapabilityDecl>,
M: DebugRouteMapper,
{
if self.namespace {
if let Some(decl) = capabilities
.iter()
.flat_map(FromEnum::from_enum)
.find(|decl: &&C| decl.name() == name)
.cloned()
{
visitor.visit(&decl)?;
mapper.add_namespace_capability(decl.clone().into());
Ok(Some(decl.into()))
} else {
Ok(None)
}
} else {
Err(RoutingError::unsupported_route_source("namespace"))
}
}
fn find_builtin_source<V, M>(
&self,
name: &CapabilityName,
capabilities: &[CapabilityDecl],
visitor: &mut V,
mapper: &mut M,
) -> Result<Option<InternalCapability>, RoutingError>
where
V: CapabilityVisitor<CapabilityDecl = Self::CapabilityDecl>,
M: DebugRouteMapper,
{
if self.builtin {
if let Some(decl) = capabilities
.iter()
.flat_map(FromEnum::from_enum)
.find(|decl: &&C| decl.name() == name)
.cloned()
{
visitor.visit(&decl)?;
mapper.add_builtin_capability(decl.clone().into());
Ok(Some(decl.into()))
} else {
Ok(None)
}
} else {
Err(RoutingError::unsupported_route_source("built-in"))
}
}
fn find_component_source<V, M>(
&self,
name: &CapabilityName,
abs_moniker: &AbsoluteMoniker,
capabilities: &[CapabilityDecl],
visitor: &mut V,
mapper: &mut M,
) -> Result<ComponentCapability, RoutingError>
where
V: CapabilityVisitor<CapabilityDecl = Self::CapabilityDecl>,
M: DebugRouteMapper,
{
if self.component {
let decl = capabilities
.iter()
.flat_map(FromEnum::from_enum)
.find(|decl: &&C| decl.name() == name)
.cloned()
.expect("CapabilityDecl missing, FIDL validation should catch this");
visitor.visit(&decl)?;
mapper.add_component_capability(abs_moniker.clone(), decl.clone().into());
Ok(decl.into())
} else {
Err(RoutingError::unsupported_route_source("component"))
}
}
}
/// The `Use` phase of routing.
pub struct Use<U>(PhantomData<U>);
/// The result of routing a Use declaration to the next phase.
enum UseResult<C: ComponentInstanceInterface, O, U> {
/// The source of the Use was found (Framework, AboveRoot, etc.)
Source(CapabilitySourceInterface<C>),
/// The Use led to a parent Offer declaration.
FromParent(Vec<O>, Arc<C>),
/// The Use led to a child Expose declaration.
/// Note: Instead of FromChild carrying an ExposeDecl of the matching child, it carries a
/// UseDecl. This is because some RoutingStrategy<> don't support Expose, but are still
/// required to enumerate over UseResult<>.
FromChild(U, Arc<C>),
}
impl<U> Use<U>
where
U: UseDeclCommon + ErrorNotFoundFromParent + Into<UseDecl> + Clone,
{
/// Routes the capability starting from the `use_` declaration at `target` to either a valid
/// source (as defined by `sources`) or the Offer declaration that ends this phase of routing.
async fn route<C, S, V, O, M>(
use_: U,
target: Arc<C>,
sources: &S,
visitor: &mut V,
mapper: &mut M,
) -> Result<UseResult<C, O, U>, RoutingError>
where
C: ComponentInstanceInterface,
S: Sources,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
O: OfferDeclCommon + FromEnum<OfferDecl> + ErrorNotFoundFromParent + Clone,
M: DebugRouteMapper,
{
mapper.add_use(target.abs_moniker().clone(), use_.clone().into());
match use_.source() {
UseSource::Framework => {
Ok(UseResult::Source(CapabilitySourceInterface::<C>::Framework {
capability: sources.framework_source(use_.source_name().clone(), mapper)?,
component: target.as_weak(),
}))
}
UseSource::Capability(_) => {
sources.capability_source()?;
Ok(UseResult::Source(CapabilitySourceInterface::<C>::Capability {
component: target.as_weak(),
source_capability: ComponentCapability::Use(use_.into()),
}))
}
UseSource::Parent => match target.try_get_parent()? {
ExtendedInstanceInterface::<C>::AboveRoot(top_instance) => {
if sources.is_namespace_supported() {
if let Some(capability) = sources.find_namespace_source(
use_.source_name(),
top_instance.namespace_capabilities(),
visitor,
mapper,
)? {
return Ok(UseResult::Source(
CapabilitySourceInterface::<C>::Namespace {
capability,
top_instance: Arc::downgrade(&top_instance),
},
));
}
}
if let Some(capability) = sources.find_builtin_source(
use_.source_name(),
top_instance.builtin_capabilities(),
visitor,
mapper,
)? {
return Ok(UseResult::Source(CapabilitySourceInterface::<C>::Builtin {
capability,
top_instance: Arc::downgrade(&top_instance),
}));
}
Err(RoutingError::use_from_component_manager_not_found(
use_.source_name().to_string(),
))
}
ExtendedInstanceInterface::<C>::Component(parent_component) => {
let parent_offers: Vec<O> = {
let parent_offers = parent_component.lock_resolved_state().await?.offers();
let child_moniker =
target.child_moniker().expect("ChildMoniker should exist");
let found_offers = find_matching_offers(
use_.source_name(),
&child_moniker,
&parent_offers,
);
if found_offers.is_empty() {
return Err(
<U as ErrorNotFoundFromParent>::error_not_found_from_parent(
target.abs_moniker().clone(),
use_.source_name().clone(),
),
);
} else {
found_offers
}
};
Ok(UseResult::FromParent(parent_offers, parent_component))
}
},
UseSource::Child(name) => {
let moniker = target.abs_moniker();
let child_component = {
let child_moniker = ChildMoniker::new(name.clone(), None);
target.lock_resolved_state().await?.get_child(&child_moniker).ok_or_else(
|| {
RoutingError::use_from_child_instance_not_found(
&child_moniker,
moniker,
name.clone(),
)
},
)?
};
Ok(UseResult::FromChild(use_, child_component))
}
UseSource::Debug => {
// This is not supported today. It might be worthwhile to support this if
// more than just protocol has a debug capability.
return Err(RoutingError::unsupported_route_source("debug capability"));
}
UseSource::Self_ => {
return Err(RoutingError::unsupported_route_source("self"));
}
}
}
}
/// The environment `Registration` phase of routing.
pub struct Registration<R>(PhantomData<R>);
/// The result of routing a Registration declaration to the next phase.
enum RegistrationResult<C: ComponentInstanceInterface, O, E> {
/// The source of the Registration was found (Framework, AboveRoot, etc.).
Source(CapabilitySourceInterface<C>),
/// The Registration led to a parent Offer declaration.
FromParent(Vec<O>, Arc<C>),
/// The Registration led to a child Expose declaration.
FromChild(E, Arc<C>),
}
impl<R> Registration<R>
where
R: RegistrationDeclCommon
+ ErrorNotFoundFromParent
+ ErrorNotFoundInChild
+ Into<RegistrationDecl>
+ Clone,
{
/// Routes the capability starting from the `registration` declaration at `target` to either a
/// valid source (as defined by `sources`) or the Offer or Expose declaration that ends this
/// phase of routing.
async fn route<C, S, V, O, E, M>(
registration: R,
target: Arc<C>,
sources: &S,
visitor: &mut V,
mapper: &mut M,
) -> Result<RegistrationResult<C, O, E>, RoutingError>
where
C: ComponentInstanceInterface,
S: Sources,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
O: OfferDeclCommon + FromEnum<OfferDecl> + Clone,
E: ExposeDeclCommon + FromEnum<ExposeDecl> + Clone,
M: DebugRouteMapper,
{
mapper.add_registration(target.abs_moniker().clone(), registration.clone().into());
match registration.source() {
RegistrationSource::Self_ => {
let target_capabilities = target.lock_resolved_state().await?.capabilities();
Ok(RegistrationResult::Source(CapabilitySourceInterface::<C>::Component {
capability: sources.find_component_source(
registration.source_name(),
target.abs_moniker(),
&target_capabilities,
visitor,
mapper,
)?,
component: target.as_weak(),
}))
}
RegistrationSource::Parent => match target.try_get_parent()? {
ExtendedInstanceInterface::<C>::AboveRoot(top_instance) => {
if sources.is_namespace_supported() {
if let Some(capability) = sources.find_namespace_source(
registration.source_name(),
top_instance.namespace_capabilities(),
visitor,
mapper,
)? {
return Ok(RegistrationResult::Source(
CapabilitySourceInterface::<C>::Namespace {
capability,
top_instance: Arc::downgrade(&top_instance),
},
));
}
}
if let Some(capability) = sources.find_builtin_source(
registration.source_name(),
top_instance.builtin_capabilities(),
visitor,
mapper,
)? {
return Ok(RegistrationResult::Source(
CapabilitySourceInterface::<C>::Builtin {
capability,
top_instance: Arc::downgrade(&top_instance),
},
));
}
Err(RoutingError::register_from_component_manager_not_found(
registration.source_name().to_string(),
))
}
ExtendedInstanceInterface::<C>::Component(parent_component) => {
let parent_offers: Vec<O> = {
let parent_offers = parent_component.lock_resolved_state().await?.offers();
let child_moniker =
target.child_moniker().expect("ChildMoniker should exist");
let found_offers = find_matching_offers(
registration.source_name(),
&child_moniker,
&parent_offers,
);
if found_offers.is_empty() {
return Err(
<R as ErrorNotFoundFromParent>::error_not_found_from_parent(
target.abs_moniker().clone(),
registration.source_name().clone(),
),
);
} else {
found_offers
}
};
Ok(RegistrationResult::FromParent(parent_offers, parent_component))
}
},
RegistrationSource::Child(child) => {
let child_component = {
let child_moniker = ChildMoniker::new(child.clone(), None);
target.lock_resolved_state().await?.get_child(&child_moniker).ok_or_else(
|| RoutingError::EnvironmentFromChildInstanceNotFound {
child_moniker,
moniker: target.abs_moniker().clone(),
capability_name: registration.source_name().clone(),
capability_type: R::TYPE.to_string(),
},
)?
};
let child_expose: E = {
let child_exposes = child_component.lock_resolved_state().await?.exposes();
find_matching_expose(registration.source_name(), &child_exposes)
.cloned()
.ok_or_else(|| {
let child_moniker = child_component
.instanced_child_moniker()
.expect("InstancedChildMoniker should exist")
.without_instance_id();
<R as ErrorNotFoundInChild>::error_not_found_in_child(
target.abs_moniker().clone(),
child_moniker,
registration.source_name().clone(),
)
})?
};
Ok(RegistrationResult::FromChild(child_expose, child_component))
}
}
}
}
/// The `Offer` phase of routing.
pub struct Offer<O>(PhantomData<O>);
/// The result of routing an Offer declaration to the next phase.
enum OfferResult<C: ComponentInstanceInterface, O> {
/// The source of the Offer was found (Framework, AboveRoot, Component, etc.).
Source(CapabilitySourceInterface<C>),
/// The Offer led to an Offer-from-child declaration.
/// Not all capabilities can be exposed, so let the caller decide how to handle this.
OfferFromChild(O, Arc<C>),
/// Offer from collection.
OfferFromCollection(O, Arc<C>, String),
/// Offer from multiple sources.
OfferFromAggregate(Vec<O>, Arc<C>),
}
impl<O> Offer<O>
where
O: OfferDeclCommon + ErrorNotFoundFromParent + FromEnum<OfferDecl> + Into<OfferDecl> + Clone,
{
/// Routes the capability starting from the `offer` declaration at `target` to either a valid
/// source (as defined by `sources`) or the declaration that ends this phase of routing.
async fn route<C, S, V, M>(
mut offer: O,
mut target: Arc<C>,
sources: &S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<OfferResult<C, O>, RoutingError>
where
C: ComponentInstanceInterface,
S: Sources,
V: OfferVisitor<OfferDecl = O>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
M: DebugRouteMapper,
{
loop {
mapper.add_offer(target.abs_moniker().clone(), offer.clone().into());
OfferVisitor::visit(visitor, &offer)?;
route.push(target.clone());
match offer.source() {
OfferSource::Void => {
panic!("an error should have been emitted by the availability walker before we reach this point");
}
OfferSource::Self_ => {
let target_capabilities = target.lock_resolved_state().await?.capabilities();
let component_capability = sources.find_component_source(
offer.source_name(),
target.abs_moniker(),
&target_capabilities,
visitor,
mapper,
)?;
// if offerdecl is for a filtered service return the associated filterd source.
return match offer.into() {
OfferDecl::Service(offer_service_decl) => {
if offer_service_decl.source_instance_filter.is_some()
|| offer_service_decl.renamed_instances.is_some()
{
Ok(OfferResult::Source(
CapabilitySourceInterface::<C>::FilteredService {
capability: component_capability,
component: target.as_weak(),
source_instance_filter: offer_service_decl
.source_instance_filter
.unwrap_or(vec![]),
instance_name_source_to_target: offer_service_decl
.renamed_instances
.map_or(HashMap::new(), name_mappings_to_map),
},
))
} else {
Ok(OfferResult::Source(CapabilitySourceInterface::<C>::Component {
capability: component_capability,
component: target.as_weak(),
}))
}
}
_ => Ok(OfferResult::Source(CapabilitySourceInterface::<C>::Component {
capability: component_capability,
component: target.as_weak(),
})),
};
}
OfferSource::Framework => {
return Ok(OfferResult::Source(CapabilitySourceInterface::<C>::Framework {
capability: sources
.framework_source(offer.source_name().clone(), mapper)?,
component: target.as_weak(),
}))
}
OfferSource::Capability(_) => {
sources.capability_source()?;
return Ok(OfferResult::Source(CapabilitySourceInterface::<C>::Capability {
source_capability: ComponentCapability::Offer(offer.into()),
component: target.as_weak(),
}));
}
OfferSource::Parent => {
let parent_component = match target.try_get_parent()? {
ExtendedInstanceInterface::<C>::AboveRoot(top_instance) => {
if sources.is_namespace_supported() {
if let Some(capability) = sources.find_namespace_source(
offer.source_name(),
top_instance.namespace_capabilities(),
visitor,
mapper,
)? {
return Ok(OfferResult::Source(
CapabilitySourceInterface::<C>::Namespace {
capability,
top_instance: Arc::downgrade(&top_instance),
},
));
}
}
if let Some(capability) = sources.find_builtin_source(
offer.source_name(),
top_instance.builtin_capabilities(),
visitor,
mapper,
)? {
return Ok(OfferResult::Source(
CapabilitySourceInterface::<C>::Builtin {
capability,
top_instance: Arc::downgrade(&top_instance),
},
));
}
return Err(RoutingError::offer_from_component_manager_not_found(
offer.source_name().to_string(),
));
}
ExtendedInstanceInterface::<C>::Component(component) => component,
};
let child_moniker = target.child_moniker().expect("ChildMoniker should exist");
let parent_offers: Vec<O> = {
let parent_offers = parent_component.lock_resolved_state().await?.offers();
let found_offers = find_matching_offers(
offer.source_name(),
&child_moniker,
&parent_offers,
);
if found_offers.is_empty() {
return Err(
<O as ErrorNotFoundFromParent>::error_not_found_from_parent(
target.abs_moniker().clone(),
offer.source_name().clone(),
),
);
} else {
found_offers
}
};
if parent_offers.len() == 1 {
offer = parent_offers.first().unwrap().clone();
target = parent_component;
} else {
return Ok(OfferResult::OfferFromAggregate(parent_offers, target));
}
}
OfferSource::Child(_) => {
return Ok(OfferResult::OfferFromChild(offer, target));
}
OfferSource::Collection(name) => {
sources.collection_source()?;
{
let target_collections = target.lock_resolved_state().await?.collections();
target_collections.iter().find(|c| &c.name == name).ok_or_else(|| {
RoutingError::OfferFromCollectionNotFound {
collection: name.clone(),
moniker: target.abs_moniker().clone(),
capability: offer.source_name().clone(),
}
})?;
}
let name = name.clone();
return Ok(OfferResult::OfferFromCollection(offer, target, name));
}
}
}
}
}
/// Finds the matching Expose declaration for an Offer-from-child, changing the
/// direction in which the Component Tree is being navigated (from up to down).
async fn change_directions<C, O, E>(
offer: O,
component: Arc<C>,
) -> Result<(E, Arc<C>), RoutingError>
where
C: ComponentInstanceInterface,
O: OfferDeclCommon + ErrorNotFoundInChild,
E: ExposeDeclCommon + FromEnum<ExposeDecl> + Clone,
{
match offer.source() {
OfferSource::Child(child) => {
let child_component = {
let child_moniker = ChildMoniker::new(child.name.clone(), child.collection.clone());
component.lock_resolved_state().await?.get_child(&child_moniker).ok_or_else(
|| RoutingError::OfferFromChildInstanceNotFound {
child_moniker,
moniker: component.abs_moniker().clone(),
capability_id: offer.source_name().clone().into(),
},
)?
};
let expose = {
let child_exposes = child_component.lock_resolved_state().await?.exposes();
find_matching_expose(offer.source_name(), &child_exposes).cloned().ok_or_else(
|| {
let child_moniker = child_component
.instanced_child_moniker()
.expect("ChildMoniker should exist")
.without_instance_id();
<O as ErrorNotFoundInChild>::error_not_found_in_child(
component.abs_moniker().clone(),
child_moniker,
offer.source_name().clone(),
)
},
)?
};
Ok((expose, child_component))
}
_ => panic!("change_direction called with offer that does not change direction"),
}
}
/// The `Expose` phase of routing.
pub struct Expose<E>(PhantomData<E>);
/// The result of routing an Expose declaration to the next phase.
enum ExposeResult<C: ComponentInstanceInterface, E> {
/// The source of the Expose was found (Framework, Component, etc.).
Source(CapabilitySourceInterface<C>),
/// The source of the Expose comes from a collection.
ExposeFromCollection(E, Arc<C>, String),
}
impl<E> Expose<E>
where
E: ExposeDeclCommon + ErrorNotFoundInChild + FromEnum<ExposeDecl> + Into<ExposeDecl> + Clone,
{
/// Routes the capability starting from the `expose` declaration at `target` to a valid source
/// (as defined by `sources`).
async fn route<C, S, V, M>(
mut expose: E,
mut target: Arc<C>,
sources: &S,
visitor: &mut V,
mapper: &mut M,
route: &mut Vec<Arc<C>>,
) -> Result<ExposeResult<C, E>, RoutingError>
where
C: ComponentInstanceInterface,
S: Sources,
V: ExposeVisitor<ExposeDecl = E>,
V: CapabilityVisitor<CapabilityDecl = S::CapabilityDecl>,
M: DebugRouteMapper,
{
loop {
mapper.add_expose(target.abs_moniker().clone(), expose.clone().into());
ExposeVisitor::visit(visitor, &expose)?;
route.push(target.clone());
match expose.source() {
ExposeSource::Self_ => {
let target_capabilities = target.lock_resolved_state().await?.capabilities();
return Ok(ExposeResult::Source(CapabilitySourceInterface::<C>::Component {
capability: sources.find_component_source(
expose.source_name(),
target.abs_moniker(),
&target_capabilities,
visitor,
mapper,
)?,
component: target.as_weak(),
}));
}
ExposeSource::Framework => {
return Ok(ExposeResult::Source(CapabilitySourceInterface::<C>::Framework {
capability: sources
.framework_source(expose.source_name().clone(), mapper)?,
component: target.as_weak(),
}))
}
ExposeSource::Capability(_) => {
sources.capability_source()?;
return Ok(ExposeResult::Source(CapabilitySourceInterface::<C>::Capability {
source_capability: ComponentCapability::Expose(expose.into()),
component: target.as_weak(),
}));
}
ExposeSource::Child(child) => {
let child_component = {
let child_moniker = ChildMoniker::new(child.clone(), None);
target.lock_resolved_state().await?.get_child(&child_moniker).ok_or_else(
|| RoutingError::ExposeFromChildInstanceNotFound {
child_moniker,
moniker: target.abs_moniker().clone(),
capability_id: expose.source_name().clone().into(),
},
)?
};
let child_expose = {
let child_exposes = child_component.lock_resolved_state().await?.exposes();
find_matching_expose(expose.source_name(), &child_exposes)
.cloned()
.ok_or_else(|| {
let child_moniker = child_component
.instanced_child_moniker()
.expect("ChildMoniker should exist")
.without_instance_id();
<E as ErrorNotFoundInChild>::error_not_found_in_child(
target.abs_moniker().clone(),
child_moniker,
expose.source_name().clone(),
)
})?
};
expose = child_expose;
target = child_component;
}
ExposeSource::Collection(name) => {
sources.collection_source()?;
{
let target_collections = target.lock_resolved_state().await?.collections();
target_collections.iter().find(|c| &c.name == name).ok_or_else(|| {
RoutingError::ExposeFromCollectionNotFound {
collection: name.clone(),
moniker: target.abs_moniker().clone(),
capability: expose.source_name().clone(),
}
})?;
}
let name = name.clone();
return Ok(ExposeResult::ExposeFromCollection(expose, target, name));
}
}
}
}
}
fn target_matches_moniker(target: &OfferTarget, child_moniker: &ChildMoniker) -> bool {
match target {
OfferTarget::Child(target_ref) => {
target_ref.name == child_moniker.name()
&& target_ref.collection.as_ref().map(|c| c.as_str()) == child_moniker.collection()
}
OfferTarget::Collection(target_collection) => {
Some(target_collection.as_str()) == child_moniker.collection()
}
}
}
/// Visitor pattern trait for visiting a variant of [`OfferDecl`] specific to a capability type.
pub trait OfferVisitor {
/// The concrete declaration type.
type OfferDecl: OfferDeclCommon;
/// Visit a variant of [`OfferDecl`] specific to the capability.
/// Returning an `Err` cancels visitation.
fn visit(&mut self, offer: &Self::OfferDecl) -> Result<(), RoutingError>;
}
/// Visitor pattern trait for visiting a variant of [`ExposeDecl`] specific to a capability type.
pub trait ExposeVisitor {
/// The concrete declaration type.
type ExposeDecl: ExposeDeclCommon;
/// Visit a variant of [`ExposeDecl`] specific to the capability.
/// Returning an `Err` cancels visitation.
fn visit(&mut self, expose: &Self::ExposeDecl) -> Result<(), RoutingError>;
}
/// Visitor pattern trait for visiting a variant of [`CapabilityDecl`] specific to a capability
/// type.
pub trait CapabilityVisitor {
/// The concrete declaration type. Can be `()` if the capability type does not support
/// namespace, component, or built-in source types.
type CapabilityDecl;
/// Visit a variant of [`CapabilityDecl`] specific to the capability.
/// Returning an `Err` cancels visitation.
fn visit(&mut self, _capability_decl: &Self::CapabilityDecl) -> Result<(), RoutingError> {
Ok(())
}
}
pub fn find_matching_offer<'a, O>(
source_name: &CapabilityName,
child_moniker: &ChildMoniker,
offers: &'a Vec<OfferDecl>,
) -> Option<&'a O>
where
O: OfferDeclCommon + FromEnum<OfferDecl>,
{
offers.iter().flat_map(FromEnum::<OfferDecl>::from_enum).find(|offer: &&O| {
*offer.target_name() == *source_name
&& target_matches_moniker(offer.target(), &child_moniker)
})
}
pub fn find_matching_offers<'a, O>(
source_name: &CapabilityName,
child_moniker: &ChildMoniker,
offers: &'a Vec<OfferDecl>,
) -> Vec<O>
where
O: OfferDeclCommon + FromEnum<OfferDecl> + Clone,
{
offers
.iter()
.flat_map(FromEnum::<OfferDecl>::from_enum)
.filter(|offer: &&O| {
*offer.target_name() == *source_name
&& target_matches_moniker(offer.target(), &child_moniker)
})
.cloned()
.collect()
}
pub fn find_matching_expose<'a, E>(
source_name: &CapabilityName,
exposes: &'a Vec<ExposeDecl>,
) -> Option<&'a E>
where
E: ExposeDeclCommon + FromEnum<ExposeDecl>,
{
exposes.iter().flat_map(FromEnum::<ExposeDecl>::from_enum).find(|expose: &&E| {
*expose.target_name() == *source_name && *expose.target() == ExposeTarget::Parent
})
}
/// Implemented by declaration types to emit a proper error when a matching offer is not found in the parent.
pub trait ErrorNotFoundFromParent {
fn error_not_found_from_parent(
decl_site_moniker: AbsoluteMoniker,
capability_name: CapabilityName,
) -> RoutingError;
}
/// Implemented by declaration types to emit a proper error when a matching expose is not found in the child.
pub trait ErrorNotFoundInChild {
fn error_not_found_in_child(
decl_site_moniker: AbsoluteMoniker,
child_moniker: ChildMoniker,
capability_name: CapabilityName,
) -> RoutingError;
}
/// Creates a unit struct that implements a visitor for each declared type.
#[macro_export]
macro_rules! make_noop_visitor {
($name:ident, {
$(OfferDecl => $offer_decl:ty,)*
$(ExposeDecl => $expose_decl:ty,)*
$(CapabilityDecl => $cap_decl:ty,)*
}) => {
#[derive(Clone)]
pub struct $name;
$(
impl $crate::router::OfferVisitor for $name {
type OfferDecl = $offer_decl;
fn visit(&mut self, _decl: &Self::OfferDecl) -> Result<(), $crate::error::RoutingError> {
Ok(())
}
}
)*
$(
impl $crate::router::ExposeVisitor for $name {
type ExposeDecl = $expose_decl;
fn visit(&mut self, _decl: &Self::ExposeDecl) -> Result<(), $crate::error::RoutingError> {
Ok(())
}
}
)*
$(
impl $crate::router::CapabilityVisitor for $name {
type CapabilityDecl = $cap_decl;
fn visit(
&mut self,
_decl: &Self::CapabilityDecl
) -> Result<(), $crate::error::RoutingError> {
Ok(())
}
}
)*
};
}