1use crate::bedrock::request_metadata::Metadata;
6use crate::error::RoutingError;
7use async_trait::async_trait;
8use cm_rust::CapabilityTypeName;
9use cm_types::{IterablePath, RelativePath};
10use fidl_fuchsia_component_sandbox as fsandbox;
11use moniker::ExtendedMoniker;
12use router_error::RouterError;
13use sandbox::{
14 Capability, CapabilityBound, Connector, Data, Dict, DirConnector, DirEntry, Request, Routable,
15 Router, RouterResponse, WeakInstanceToken,
16};
17use std::fmt::Debug;
18
19#[async_trait]
20pub trait DictExt {
21 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability>;
23
24 fn get_router_or_not_found<T>(
33 &self,
34 path: &impl IterablePath,
35 not_found_error: RoutingError,
36 ) -> Router<T>
37 where
38 T: CapabilityBound,
39 Router<T>: TryFrom<Capability>;
40
41 fn insert_capability(
43 &self,
44 path: &impl IterablePath,
45 capability: Capability,
46 ) -> Result<(), fsandbox::CapabilityStoreError>;
47
48 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability>;
50
51 async fn get_with_request<'a>(
60 &self,
61 moniker: &ExtendedMoniker,
62 path: &'a impl IterablePath,
63 request: Option<Request>,
64 debug: bool,
65 target: WeakInstanceToken,
66 ) -> Result<Option<GenericRouterResponse>, RouterError>;
67}
68
69#[derive(Debug)]
72pub enum GenericRouterResponse {
73 Capability(Capability),
75
76 Unavailable,
78
79 Debug(Data),
81}
82
83impl<T: CapabilityBound> TryFrom<GenericRouterResponse> for RouterResponse<T> {
84 type Error = &'static str;
86
87 fn try_from(r: GenericRouterResponse) -> Result<Self, Self::Error> {
88 let r = match r {
89 GenericRouterResponse::Capability(c) => {
90 let debug_name = c.debug_typename();
91 RouterResponse::<T>::Capability(c.try_into().map_err(|_| debug_name)?)
92 }
93 GenericRouterResponse::Unavailable => RouterResponse::<T>::Unavailable,
94 GenericRouterResponse::Debug(d) => RouterResponse::<T>::Debug(d),
95 };
96 Ok(r)
97 }
98}
99
100#[async_trait]
101impl DictExt for Dict {
102 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability> {
103 let mut segments = path.iter_segments();
104 let Some(mut current_name) = segments.next() else { return Some(self.clone().into()) };
105 let mut current_dict = self.clone();
106 loop {
107 match segments.next() {
108 Some(next_name) => {
109 let sub_dict = current_dict
110 .get(current_name)
111 .ok()
112 .flatten()
113 .and_then(|value| value.to_dictionary())?;
114 current_dict = sub_dict;
115
116 current_name = next_name;
117 }
118 None => return current_dict.get(current_name).ok().flatten(),
119 }
120 }
121 }
122
123 fn get_router_or_not_found<T>(
124 &self,
125 path: &impl IterablePath,
126 not_found_error: RoutingError,
127 ) -> Router<T>
128 where
129 T: CapabilityBound,
130 Router<T>: TryFrom<Capability>,
131 {
132 let mut segments = path.iter_segments();
133 let root = segments.next().expect("path must be nonempty");
134
135 #[derive(Debug)]
136 struct ErrorRouter {
137 not_found_error: RouterError,
138 }
139
140 #[async_trait]
141 impl<T: CapabilityBound> Routable<T> for ErrorRouter {
142 async fn route(
143 &self,
144 _request: Option<Request>,
145 _debug: bool,
146 _target: WeakInstanceToken,
147 ) -> Result<RouterResponse<T>, RouterError> {
148 Err(self.not_found_error.clone())
149 }
150 }
151
152 #[derive(Debug)]
156 struct ScopedDictRouter<P: IterablePath + Debug + 'static> {
157 router: Router<Dict>,
158 path: P,
159 not_found_error: RoutingError,
160 }
161
162 #[async_trait]
163 impl<P: IterablePath + Debug + 'static, T: CapabilityBound> Routable<T> for ScopedDictRouter<P> {
164 async fn route(
165 &self,
166 request: Option<Request>,
167 debug: bool,
168 target: WeakInstanceToken,
169 ) -> Result<RouterResponse<T>, RouterError> {
170 let get_init_request = || request_with_dictionary_replacement(request.as_ref());
171
172 let init_request = (get_init_request)()?;
177 match self.router.route(init_request, false, target.clone()).await? {
178 RouterResponse::<Dict>::Capability(dict) => {
179 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
180 let resp = dict
181 .get_with_request(&moniker, &self.path, request, debug, target)
182 .await?;
183 let resp =
184 resp.ok_or_else(|| RouterError::from(self.not_found_error.clone()))?;
185 let resp = resp.try_into().map_err(|debug_name: &'static str| {
186 RoutingError::BedrockWrongCapabilityType {
187 expected: T::debug_typename().into(),
188 actual: debug_name.into(),
189 moniker,
190 }
191 })?;
192 Ok(resp)
193 }
194 RouterResponse::<Dict>::Debug(data) => Ok(RouterResponse::<T>::Debug(data)),
195 RouterResponse::<Dict>::Unavailable => {
196 if !debug {
197 Ok(RouterResponse::<T>::Unavailable)
198 } else {
199 let init_request = (get_init_request)()?;
204 match self.router.route(init_request, true, target).await? {
205 RouterResponse::<Dict>::Debug(d) => {
206 Ok(RouterResponse::<T>::Debug(d))
207 }
208 _ => {
209 let moniker = self.not_found_error.clone().into();
211 Err(RoutingError::BedrockWrongCapabilityType {
212 expected: "RouterResponse::Debug".into(),
213 actual: "not RouterResponse::Debug".into(),
214 moniker,
215 }
216 .into())
217 }
218 }
219 }
220 }
221 }
222 }
223 }
224
225 if segments.next().is_none() {
226 let Some(router) =
228 self.get(root).ok().flatten().and_then(|cap| Router::<T>::try_from(cap).ok())
229 else {
230 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
231 };
232 return router;
233 }
234
235 let Some(cap) = self.get(root).ok().flatten() else {
236 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
237 };
238 let router = match cap {
239 Capability::Dictionary(d) => Router::<Dict>::new_ok(d),
240 Capability::DictionaryRouter(r) => r,
241 _ => {
242 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
243 }
244 };
245
246 let mut segments = path.iter_segments();
247 let _ = segments.next().unwrap();
248 let path = RelativePath::from(segments.collect::<Vec<_>>());
249
250 Router::<T>::new(ScopedDictRouter { router, path, not_found_error: not_found_error.into() })
251 }
252
253 fn insert_capability(
254 &self,
255 path: &impl IterablePath,
256 capability: Capability,
257 ) -> Result<(), fsandbox::CapabilityStoreError> {
258 let mut segments = path.iter_segments();
259 let mut current_name = segments.next().expect("path must be non-empty");
260 let mut current_dict = self.clone();
261 loop {
262 match segments.next() {
263 Some(next_name) => {
264 let sub_dict = {
265 match current_dict.get(current_name) {
266 Ok(Some(cap)) => cap
267 .to_dictionary()
268 .ok_or(fsandbox::CapabilityStoreError::ItemNotFound)?,
269 Ok(None) => {
270 let dict = Dict::new();
271 current_dict.insert(
272 current_name.into(),
273 Capability::Dictionary(dict.clone()),
274 )?;
275 dict
276 }
277 Err(_) => return Err(fsandbox::CapabilityStoreError::ItemNotFound),
278 }
279 };
280 current_dict = sub_dict;
281
282 current_name = next_name;
283 }
284 None => {
285 return current_dict.insert(current_name.into(), capability);
286 }
287 }
288 }
289 }
290
291 fn remove_capability(&self, path: &impl IterablePath) -> Option<Capability> {
292 let mut segments = path.iter_segments();
293 let mut current_name = segments.next().expect("path must be non-empty");
294 let mut current_dict = self.clone();
295 loop {
296 match segments.next() {
297 Some(next_name) => {
298 let sub_dict = current_dict
299 .get(current_name)
300 .ok()
301 .flatten()
302 .and_then(|value| value.to_dictionary());
303 if sub_dict.is_none() {
304 return None;
306 }
307 current_dict = sub_dict.unwrap();
308 current_name = next_name;
309 }
310 None => {
311 return current_dict.remove(current_name);
312 }
313 }
314 }
315 }
316
317 async fn get_with_request<'a>(
318 &self,
319 moniker: &ExtendedMoniker,
320 path: &'a impl IterablePath,
321 request: Option<Request>,
322 debug: bool,
323 target: WeakInstanceToken,
324 ) -> Result<Option<GenericRouterResponse>, RouterError> {
325 let mut current_dict = self.clone();
326 let num_segments = path.iter_segments().count();
327 for (next_idx, next_name) in path.iter_segments().enumerate() {
328 let capability = current_dict
330 .get(next_name)
331 .map_err(|_| RoutingError::BedrockNotCloneable { moniker: moniker.clone() })?;
332
333 let Some(capability) = capability else {
335 return Ok(None);
336 };
337
338 if next_idx < num_segments - 1 {
339 let dict_request = request_with_dictionary_replacement(request.as_ref())?;
342 match capability {
343 Capability::Dictionary(d) => {
344 current_dict = d;
345 }
346 Capability::DictionaryRouter(r) => {
347 match r.route(dict_request, false, target.clone()).await? {
348 RouterResponse::<Dict>::Capability(d) => {
349 current_dict = d;
350 }
351 RouterResponse::<Dict>::Debug(d) => {
352 return Ok(Some(GenericRouterResponse::Debug(d)));
355 }
356 RouterResponse::<Dict>::Unavailable => {
357 if !debug {
358 return Ok(Some(GenericRouterResponse::Unavailable));
359 } else {
360 let dict_request =
366 request_with_dictionary_replacement(request.as_ref())?;
367 match r.route(dict_request, true, target).await? {
368 RouterResponse::<Dict>::Debug(d) => {
369 return Ok(Some(GenericRouterResponse::Debug(d)));
370 }
371 _ => {
372 return Err(RoutingError::BedrockWrongCapabilityType {
374 expected: "RouterResponse::Debug".into(),
375 actual: "not RouterResponse::Debug".into(),
376 moniker: moniker.clone(),
377 }
378 .into());
379 }
380 }
381 }
382 }
383 }
384 }
385 _ => {
386 return Err(RoutingError::BedrockWrongCapabilityType {
387 expected: Dict::debug_typename().into(),
388 actual: capability.debug_typename().into(),
389 moniker: moniker.clone(),
390 }
391 .into());
392 }
393 }
394 } else {
395 let request = request.as_ref().map(|r| r.try_clone()).transpose()?;
401 let capability: Capability = match capability {
402 Capability::DictionaryRouter(r) => {
403 match r.route(request, debug, target).await? {
404 RouterResponse::<Dict>::Capability(c) => c.into(),
405 RouterResponse::<Dict>::Unavailable => {
406 return Ok(Some(GenericRouterResponse::Unavailable));
407 }
408 RouterResponse::<Dict>::Debug(d) => {
409 return Ok(Some(GenericRouterResponse::Debug(d)));
410 }
411 }
412 }
413 Capability::ConnectorRouter(r) => {
414 match r.route(request, debug, target).await? {
415 RouterResponse::<Connector>::Capability(c) => c.into(),
416 RouterResponse::<Connector>::Unavailable => {
417 return Ok(Some(GenericRouterResponse::Unavailable));
418 }
419 RouterResponse::<Connector>::Debug(d) => {
420 return Ok(Some(GenericRouterResponse::Debug(d)));
421 }
422 }
423 }
424 Capability::DataRouter(r) => match r.route(request, debug, target).await? {
425 RouterResponse::<Data>::Capability(c) => c.into(),
426 RouterResponse::<Data>::Unavailable => {
427 return Ok(Some(GenericRouterResponse::Unavailable));
428 }
429 RouterResponse::<Data>::Debug(d) => {
430 return Ok(Some(GenericRouterResponse::Debug(d)));
431 }
432 },
433 Capability::DirEntryRouter(r) => match r.route(request, debug, target).await? {
434 RouterResponse::<DirEntry>::Capability(c) => c.into(),
435 RouterResponse::<DirEntry>::Unavailable => {
436 return Ok(Some(GenericRouterResponse::Unavailable));
437 }
438 RouterResponse::<DirEntry>::Debug(d) => {
439 return Ok(Some(GenericRouterResponse::Debug(d)));
440 }
441 },
442 Capability::DirConnectorRouter(r) => {
443 match r.route(request, debug, target).await? {
444 RouterResponse::<DirConnector>::Capability(c) => c.into(),
445 RouterResponse::<DirConnector>::Unavailable => {
446 return Ok(Some(GenericRouterResponse::Unavailable));
447 }
448 RouterResponse::<DirConnector>::Debug(d) => {
449 return Ok(Some(GenericRouterResponse::Debug(d)));
450 }
451 }
452 }
453 other => other,
454 };
455 return Ok(Some(GenericRouterResponse::Capability(capability)));
456 }
457 }
458 unreachable!("get_with_request: All cases are handled in the loop");
459 }
460}
461
462pub(super) fn request_with_dictionary_replacement(
468 request: Option<&Request>,
469) -> Result<Option<Request>, RoutingError> {
470 Ok(request.as_ref().map(|r| r.try_clone()).transpose()?.map(|r| {
471 let _ = r.metadata.set_metadata(CapabilityTypeName::Dictionary);
472 r
473 }))
474}