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: WeakInstanceToken,
25    ) -> Result<Option<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: 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.
44#[derive(Clone)]
45pub struct Router<T: CapabilityBound> {
46    routable: Arc<dyn Routable<T>>,
47}
48
49impl CapabilityBound for Router<crate::Connector> {
50    fn debug_typename() -> &'static str {
51        "ConnectorRouter"
52    }
53}
54impl CapabilityBound for Router<crate::Data> {
55    fn debug_typename() -> &'static str {
56        "DataRouter"
57    }
58}
59impl CapabilityBound for Router<crate::Dictionary> {
60    fn debug_typename() -> &'static str {
61        "DictionaryRouter"
62    }
63}
64
65impl CapabilityBound for Router<crate::DirConnector> {
66    fn debug_typename() -> &'static str {
67        "DirConnectorRouter"
68    }
69}
70
71impl<T: CapabilityBound> fmt::Debug for Router<T> {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        // TODO(https://fxbug.dev/329680070): Require `Debug` on `Routable` trait.
74        f.debug_struct("Router").field("routable", &"[some routable object]").finish()
75    }
76}
77
78#[async_trait]
79impl<T: CapabilityBound> Routable<T> for Router<T> {
80    async fn route(
81        &self,
82        request: RouteRequest,
83        target: WeakInstanceToken,
84    ) -> Result<Option<T>, RouterError> {
85        Router::route(self, request, target).await
86    }
87
88    async fn route_debug(
89        &self,
90        request: RouteRequest,
91        target: WeakInstanceToken,
92    ) -> Result<CapabilitySource, RouterError> {
93        Router::route_debug(self, request, target).await
94    }
95}
96
97impl<T: CapabilityBound> Router<T> {
98    /// Package a [`Routable`] object into a [`Router`].
99    pub fn new(routable: impl Routable<T> + 'static) -> Self {
100        Self { routable: Arc::new(routable) }
101    }
102
103    /// Creates a router that will always fail a request with the provided error.
104    pub fn new_error(error: impl Into<RouterError>) -> Self {
105        let v: RouterError = error.into();
106        Self::new(ErrRouter { v })
107    }
108
109    /// Creates a router that will always return the given debug info.
110    pub fn new_debug(source: CapabilitySource) -> Self {
111        Self::new(DebugRouter { source })
112    }
113
114    /// Obtain a capability from this router, following the description in `request`.
115    pub async fn route(
116        &self,
117        request: RouteRequest,
118        target: WeakInstanceToken,
119    ) -> Result<Option<T>, RouterError> {
120        self.routable.route(request, target).await
121    }
122
123    /// Obtain a CapabilitySource from this router, following the description in `request`.
124    pub async fn route_debug(
125        &self,
126        request: RouteRequest,
127        target: WeakInstanceToken,
128    ) -> Result<CapabilitySource, RouterError> {
129        self.routable.route_debug(request, target).await
130    }
131}
132
133impl<T: Clone + CapabilityBound> Router<T> {
134    /// Creates a router that will always resolve with the provided capability.
135    // TODO: Should this require debug info?
136    pub fn new_ok(c: impl Into<T>) -> Self {
137        let v: T = c.into();
138        Self::new(OkRouter { v })
139    }
140}
141
142#[derive(Clone)]
143struct OkRouter<T: Clone + CapabilityBound> {
144    v: T,
145}
146
147#[async_trait]
148impl<T: Clone + CapabilityBound> Routable<T> for OkRouter<T> {
149    async fn route(
150        &self,
151        _request: RouteRequest,
152        _target: WeakInstanceToken,
153    ) -> Result<Option<T>, RouterError> {
154        Ok(Some(self.v.clone()))
155    }
156
157    async fn route_debug(
158        &self,
159        _request: RouteRequest,
160        _target: WeakInstanceToken,
161    ) -> Result<CapabilitySource, RouterError> {
162        panic!("OkRouter does not handle debug routes");
163    }
164}
165
166#[derive(Clone)]
167struct DebugRouter {
168    source: CapabilitySource,
169}
170
171#[async_trait]
172impl<T: CapabilityBound> Routable<T> for DebugRouter {
173    async fn route(
174        &self,
175        _request: RouteRequest,
176        _target: WeakInstanceToken,
177    ) -> Result<Option<T>, RouterError> {
178        panic!("DebugRouter does not handle non-debug routes");
179    }
180
181    async fn route_debug(
182        &self,
183        _request: RouteRequest,
184        _target: WeakInstanceToken,
185    ) -> Result<CapabilitySource, RouterError> {
186        Ok(self.source.clone())
187    }
188}
189
190#[derive(Clone)]
191struct ErrRouter {
192    v: RouterError,
193}
194
195#[async_trait]
196impl<T: CapabilityBound> Routable<T> for ErrRouter {
197    async fn route(
198        &self,
199        _request: RouteRequest,
200        _target: WeakInstanceToken,
201    ) -> Result<Option<T>, RouterError> {
202        Err(self.v.clone())
203    }
204
205    async fn route_debug(
206        &self,
207        _request: RouteRequest,
208        _target: WeakInstanceToken,
209    ) -> Result<CapabilitySource, RouterError> {
210        Err(self.v.clone())
211    }
212}