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