Skip to main content

runtime_capabilities/
router.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{CapabilityBound, WeakInstanceToken};
6use async_trait::async_trait;
7use capability_source::CapabilitySource;
8use cm_rust::{Availability, CapabilityTypeName};
9use cm_types::Name;
10use fidl_fuchsia_component_runtime::RouteRequest;
11use router_error::RouterError;
12use std::fmt;
13use std::sync::Arc;
14
15/// Types that implement [`Routable`] let the holder asynchronously request capabilities
16/// from them.
17#[async_trait]
18pub trait Routable<T>: Send + Sync
19where
20    T: CapabilityBound,
21{
22    async fn route(
23        &self,
24        request: RouteRequest,
25        // A reference to the requesting component.
26        target: Arc<WeakInstanceToken>,
27    ) -> Result<Option<Arc<T>>, RouterError>;
28
29    /// Performs the same operation as `route`, but returns a
30    /// `fidl_fuchsia_internal::CapabilitySource` persisted into bytes.
31    async fn route_debug(
32        &self,
33        request: RouteRequest,
34        // A reference to the requesting component.
35        target: Arc<WeakInstanceToken>,
36    ) -> Result<CapabilitySource, RouterError>;
37
38    /// Returns diagnostic data about the capability being routed.
39    fn error_info(&self) -> Option<RouterErrorInfo> {
40        None
41    }
42}
43
44/// Diagnostic data derived from the capability decl used to instantiate the router.
45#[derive(Clone, Debug, PartialEq, Eq)]
46pub struct RouterErrorInfo {
47    pub capability_type: CapabilityTypeName,
48    /// The capability name. The semantics of this name depends on the type of the
49    /// decl:
50    ///   - Expose -> `target_name`
51    ///   - Offer -> `target_name`
52    ///   - Capability -> `name`
53    ///   - Use -> `source_name`
54    pub name: Name,
55    pub availability: Availability,
56}
57
58/// A [`Router`] is a capability that lets the holder obtain other capabilities
59/// asynchronously. [`Router`] is the object capability representation of
60/// [`Routable`].
61///
62/// During routing, a request usually traverses through the component topology,
63/// passing through several routers, ending up at some router that will fulfill
64/// the request instead of forwarding it upstream.
65pub struct Router<T: CapabilityBound> {
66    routable: Box<dyn Routable<T>>,
67}
68
69impl CapabilityBound for Router<crate::Connector> {
70    fn debug_typename() -> &'static str {
71        "ConnectorRouter"
72    }
73
74    #[cfg(target_os = "fuchsia")]
75    fn try_into_directory_entry(
76        self: Arc<Self>,
77        scope: vfs::execution_scope::ExecutionScope,
78        token: Arc<crate::WeakInstanceToken>,
79    ) -> Result<Arc<dyn vfs::directory::entry::DirectoryEntry>, crate::ConversionError> {
80        Ok(self.into_directory_entry(fidl_fuchsia_io::DirentType::Service, scope, token))
81    }
82}
83impl CapabilityBound for Router<crate::Data> {
84    fn debug_typename() -> &'static str {
85        "DataRouter"
86    }
87
88    #[cfg(target_os = "fuchsia")]
89    fn try_into_directory_entry(
90        self: Arc<Self>,
91        scope: vfs::execution_scope::ExecutionScope,
92        token: Arc<crate::WeakInstanceToken>,
93    ) -> Result<Arc<dyn vfs::directory::entry::DirectoryEntry>, crate::ConversionError> {
94        Ok(self.into_directory_entry(fidl_fuchsia_io::DirentType::Service, scope, token))
95    }
96}
97impl CapabilityBound for Router<crate::Dictionary> {
98    fn debug_typename() -> &'static str {
99        "DictionaryRouter"
100    }
101
102    #[cfg(target_os = "fuchsia")]
103    fn try_into_directory_entry(
104        self: Arc<Self>,
105        scope: vfs::execution_scope::ExecutionScope,
106        token: Arc<crate::WeakInstanceToken>,
107    ) -> Result<Arc<dyn vfs::directory::entry::DirectoryEntry>, crate::ConversionError> {
108        Ok(self.into_directory_entry(fidl_fuchsia_io::DirentType::Service, scope, token))
109    }
110}
111
112impl CapabilityBound for Router<crate::DirConnector> {
113    fn debug_typename() -> &'static str {
114        "DirConnectorRouter"
115    }
116
117    #[cfg(target_os = "fuchsia")]
118    fn try_into_directory_entry(
119        self: Arc<Self>,
120        scope: vfs::execution_scope::ExecutionScope,
121        token: Arc<crate::WeakInstanceToken>,
122    ) -> Result<Arc<dyn vfs::directory::entry::DirectoryEntry>, crate::ConversionError> {
123        Ok(self.into_directory_entry(fidl_fuchsia_io::DirentType::Service, scope, token))
124    }
125}
126
127impl<T: CapabilityBound> fmt::Debug for Router<T> {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        // TODO(https://fxbug.dev/329680070): Require `Debug` on `Routable` trait.
130        f.debug_struct("Router").field("routable", &"[some routable object]").finish()
131    }
132}
133
134#[async_trait]
135impl<T: CapabilityBound> Routable<T> for Router<T> {
136    async fn route(
137        &self,
138        request: RouteRequest,
139        target: Arc<WeakInstanceToken>,
140    ) -> Result<Option<Arc<T>>, RouterError> {
141        Router::route(self, request, target).await
142    }
143
144    async fn route_debug(
145        &self,
146        request: RouteRequest,
147        target: Arc<WeakInstanceToken>,
148    ) -> Result<CapabilitySource, RouterError> {
149        Router::route_debug(self, request, target).await
150    }
151
152    fn error_info(&self) -> Option<RouterErrorInfo> {
153        self.routable.error_info()
154    }
155}
156
157impl<T: CapabilityBound> Router<T> {
158    /// Package a [`Routable`] object into a [`Router`].
159    pub fn new(routable: impl Routable<T> + 'static) -> Arc<Self> {
160        Arc::new(Self { routable: Box::new(routable) })
161    }
162
163    /// Creates a router that will always fail a request with the provided error.
164    pub fn new_error(error: impl Into<RouterError>) -> Arc<Self> {
165        let v: RouterError = error.into();
166        Self::new(ErrRouter { v })
167    }
168
169    /// Creates a router that will always return the given debug info.
170    pub fn new_debug(source: CapabilitySource) -> Arc<Self> {
171        Self::new(DebugRouter { source })
172    }
173
174    /// Obtain a capability from this router, following the description in `request`.
175    pub async fn route(
176        &self,
177        request: RouteRequest,
178        target: Arc<WeakInstanceToken>,
179    ) -> Result<Option<Arc<T>>, RouterError> {
180        self.routable.route(request, target).await
181    }
182
183    /// Obtain a CapabilitySource from this router, following the description in `request`.
184    pub async fn route_debug(
185        &self,
186        request: RouteRequest,
187        target: Arc<WeakInstanceToken>,
188    ) -> Result<CapabilitySource, RouterError> {
189        self.routable.route_debug(request, target).await
190    }
191
192    /// Returns diagnostic data about the capability being routed.
193    pub fn error_info(&self) -> Option<RouterErrorInfo> {
194        self.routable.error_info()
195    }
196}
197
198#[derive(Clone)]
199struct DebugRouter {
200    source: CapabilitySource,
201}
202
203#[async_trait]
204impl<T: CapabilityBound> Routable<T> for DebugRouter {
205    async fn route(
206        &self,
207        _request: RouteRequest,
208        _target: Arc<WeakInstanceToken>,
209    ) -> Result<Option<Arc<T>>, RouterError> {
210        panic!("DebugRouter does not handle non-debug routes");
211    }
212
213    async fn route_debug(
214        &self,
215        _request: RouteRequest,
216        _target: Arc<WeakInstanceToken>,
217    ) -> Result<CapabilitySource, RouterError> {
218        Ok(self.source.clone())
219    }
220}
221
222#[derive(Clone)]
223struct ErrRouter {
224    v: RouterError,
225}
226
227#[async_trait]
228impl<T: CapabilityBound> Routable<T> for ErrRouter {
229    async fn route(
230        &self,
231        _request: RouteRequest,
232        _target: Arc<WeakInstanceToken>,
233    ) -> Result<Option<Arc<T>>, RouterError> {
234        Err(self.v.clone())
235    }
236
237    async fn route_debug(
238        &self,
239        _request: RouteRequest,
240        _target: Arc<WeakInstanceToken>,
241    ) -> Result<CapabilitySource, RouterError> {
242        Err(self.v.clone())
243    }
244}