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
198impl<T: CapabilityBound> Router<T> {
199    /// Creates a router that will always resolve with the provided capability.
200    // TODO: Should this require debug info?
201    pub fn new_ok(c: impl Into<Arc<T>>) -> Arc<Self> {
202        let v: Arc<T> = c.into();
203        Self::new(OkRouter { v })
204    }
205}
206
207#[derive(Clone)]
208struct OkRouter<T: CapabilityBound> {
209    v: Arc<T>,
210}
211
212#[async_trait]
213impl<T: CapabilityBound> Routable<T> for OkRouter<T> {
214    async fn route(
215        &self,
216        _request: RouteRequest,
217        _target: Arc<WeakInstanceToken>,
218    ) -> Result<Option<Arc<T>>, RouterError> {
219        Ok(Some(self.v.clone()))
220    }
221
222    async fn route_debug(
223        &self,
224        _request: RouteRequest,
225        _target: Arc<WeakInstanceToken>,
226    ) -> Result<CapabilitySource, RouterError> {
227        panic!("OkRouter does not handle debug routes");
228    }
229}
230
231#[derive(Clone)]
232struct DebugRouter {
233    source: CapabilitySource,
234}
235
236#[async_trait]
237impl<T: CapabilityBound> Routable<T> for DebugRouter {
238    async fn route(
239        &self,
240        _request: RouteRequest,
241        _target: Arc<WeakInstanceToken>,
242    ) -> Result<Option<Arc<T>>, RouterError> {
243        panic!("DebugRouter does not handle non-debug routes");
244    }
245
246    async fn route_debug(
247        &self,
248        _request: RouteRequest,
249        _target: Arc<WeakInstanceToken>,
250    ) -> Result<CapabilitySource, RouterError> {
251        Ok(self.source.clone())
252    }
253}
254
255#[derive(Clone)]
256struct ErrRouter {
257    v: RouterError,
258}
259
260#[async_trait]
261impl<T: CapabilityBound> Routable<T> for ErrRouter {
262    async fn route(
263        &self,
264        _request: RouteRequest,
265        _target: Arc<WeakInstanceToken>,
266    ) -> Result<Option<Arc<T>>, RouterError> {
267        Err(self.v.clone())
268    }
269
270    async fn route_debug(
271        &self,
272        _request: RouteRequest,
273        _target: Arc<WeakInstanceToken>,
274    ) -> Result<CapabilitySource, RouterError> {
275        Err(self.v.clone())
276    }
277}