1use crate::bedrock::request_metadata::METADATA_KEY_TYPE;
6use crate::error::RoutingError;
7use async_trait::async_trait;
8use cm_rust::CapabilityTypeName;
9use cm_types::{IterablePath, Name, RelativePath};
10use fidl_fuchsia_component_sandbox as fsandbox;
11use moniker::ExtendedMoniker;
12use router_error::RouterError;
13use sandbox::{
14 Capability, CapabilityBound, Connector, Data, Dict, DirEntry, Request, Routable, Router,
15 RouterResponse,
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);
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 ) -> Result<Option<GenericRouterResponse>, RouterError>;
66}
67
68#[derive(Debug)]
71pub enum GenericRouterResponse {
72 Capability(Capability),
74
75 Unavailable,
77
78 Debug(Data),
80}
81
82impl<T: CapabilityBound> TryFrom<GenericRouterResponse> for RouterResponse<T> {
83 type Error = &'static str;
85
86 fn try_from(r: GenericRouterResponse) -> Result<Self, Self::Error> {
87 let r = match r {
88 GenericRouterResponse::Capability(c) => {
89 let debug_name = c.debug_typename();
90 RouterResponse::<T>::Capability(c.try_into().map_err(|_| debug_name)?)
91 }
92 GenericRouterResponse::Unavailable => RouterResponse::<T>::Unavailable,
93 GenericRouterResponse::Debug(d) => RouterResponse::<T>::Debug(d),
94 };
95 Ok(r)
96 }
97}
98
99#[async_trait]
100impl DictExt for Dict {
101 fn get_capability(&self, path: &impl IterablePath) -> Option<Capability> {
102 let mut segments = path.iter_segments();
103 let Some(mut current_name) = segments.next() else { return Some(self.clone().into()) };
104 let mut current_dict = self.clone();
105 loop {
106 match segments.next() {
107 Some(next_name) => {
108 let sub_dict = current_dict
109 .get(current_name)
110 .ok()
111 .flatten()
112 .and_then(|value| value.to_dictionary())?;
113 current_dict = sub_dict;
114
115 current_name = next_name;
116 }
117 None => return current_dict.get(current_name).ok().flatten(),
118 }
119 }
120 }
121
122 fn get_router_or_not_found<T>(
123 &self,
124 path: &impl IterablePath,
125 not_found_error: RoutingError,
126 ) -> Router<T>
127 where
128 T: CapabilityBound,
129 Router<T>: TryFrom<Capability>,
130 {
131 let mut segments = path.iter_segments();
132 let root = segments.next().expect("path must be nonempty");
133
134 #[derive(Debug)]
135 struct ErrorRouter {
136 not_found_error: RouterError,
137 }
138
139 #[async_trait]
140 impl<T: CapabilityBound> Routable<T> for ErrorRouter {
141 async fn route(
142 &self,
143 _request: Option<Request>,
144 _debug: bool,
145 ) -> Result<RouterResponse<T>, RouterError> {
146 Err(self.not_found_error.clone())
147 }
148 }
149
150 #[derive(Debug)]
154 struct ScopedDictRouter<P: IterablePath + Debug + 'static> {
155 router: Router<Dict>,
156 path: P,
157 not_found_error: RoutingError,
158 }
159
160 #[async_trait]
161 impl<P: IterablePath + Debug + 'static, T: CapabilityBound> Routable<T> for ScopedDictRouter<P> {
162 async fn route(
163 &self,
164 request: Option<Request>,
165 debug: bool,
166 ) -> Result<RouterResponse<T>, RouterError> {
167 let init_request = request_with_dictionary_replacement(request.as_ref())?;
172 match self.router.route(init_request, false).await? {
173 RouterResponse::<Dict>::Capability(dict) => {
174 let moniker: ExtendedMoniker = self.not_found_error.clone().into();
175 let resp =
176 dict.get_with_request(&moniker, &self.path, request, debug).await?;
177 let resp =
178 resp.ok_or_else(|| RouterError::from(self.not_found_error.clone()))?;
179 let resp = resp.try_into().map_err(|debug_name: &'static str| {
180 RoutingError::BedrockWrongCapabilityType {
181 expected: T::debug_typename().into(),
182 actual: debug_name.into(),
183 moniker,
184 }
185 })?;
186 Ok(resp)
187 }
188 _ => Err(RoutingError::BedrockMemberAccessUnsupported {
189 moniker: self.not_found_error.clone().into(),
190 }
191 .into()),
192 }
193 }
194 }
195
196 if segments.next().is_none() {
197 let Some(router) =
199 self.get(root).ok().flatten().and_then(|cap| Router::<T>::try_from(cap).ok())
200 else {
201 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
202 };
203 return router;
204 }
205
206 let Some(cap) = self.get(root).ok().flatten() else {
207 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
208 };
209 let router = match cap {
210 Capability::Dictionary(d) => Router::<Dict>::new_ok(d),
211 Capability::DictionaryRouter(r) => r,
212 _ => {
213 return Router::<T>::new(ErrorRouter { not_found_error: not_found_error.into() });
214 }
215 };
216
217 let mut segments = path.iter_segments();
218 let _ = segments.next().unwrap();
219 let path = RelativePath::from(segments.collect::<Vec<_>>());
220
221 Router::<T>::new(ScopedDictRouter { router, path, not_found_error: not_found_error.into() })
222 }
223
224 fn insert_capability(
225 &self,
226 path: &impl IterablePath,
227 capability: Capability,
228 ) -> Result<(), fsandbox::CapabilityStoreError> {
229 let mut segments = path.iter_segments();
230 let mut current_name = segments.next().expect("path must be non-empty");
231 let mut current_dict = self.clone();
232 loop {
233 match segments.next() {
234 Some(next_name) => {
235 let sub_dict = {
236 match current_dict.get(current_name) {
237 Ok(Some(cap)) => cap
238 .to_dictionary()
239 .ok_or(fsandbox::CapabilityStoreError::ItemNotFound)?,
240 Ok(None) => {
241 let dict = Dict::new();
242 current_dict.insert(
243 current_name.into(),
244 Capability::Dictionary(dict.clone()),
245 )?;
246 dict
247 }
248 Err(_) => return Err(fsandbox::CapabilityStoreError::ItemNotFound),
249 }
250 };
251 current_dict = sub_dict;
252
253 current_name = next_name;
254 }
255 None => {
256 return current_dict.insert(current_name.into(), capability);
257 }
258 }
259 }
260 }
261
262 fn remove_capability(&self, path: &impl IterablePath) {
263 let mut segments = path.iter_segments();
264 let mut current_name = segments.next().expect("path must be non-empty");
265 let mut current_dict = self.clone();
266 loop {
267 match segments.next() {
268 Some(next_name) => {
269 let sub_dict = current_dict
270 .get(current_name)
271 .ok()
272 .flatten()
273 .and_then(|value| value.to_dictionary());
274 if sub_dict.is_none() {
275 return;
277 }
278 current_dict = sub_dict.unwrap();
279 current_name = next_name;
280 }
281 None => {
282 current_dict.remove(current_name);
283 return;
284 }
285 }
286 }
287 }
288
289 async fn get_with_request<'a>(
290 &self,
291 moniker: &ExtendedMoniker,
292 path: &'a impl IterablePath,
293 request: Option<Request>,
294 debug: bool,
295 ) -> Result<Option<GenericRouterResponse>, RouterError> {
296 let mut current_dict = self.clone();
297 let num_segments = path.iter_segments().count();
298 for (next_idx, next_name) in path.iter_segments().enumerate() {
299 let capability = current_dict
301 .get(next_name)
302 .map_err(|_| RoutingError::BedrockNotCloneable { moniker: moniker.clone() })?;
303
304 let Some(capability) = capability else {
306 return Ok(None);
307 };
308
309 let debug = if next_idx < num_segments - 1 {
311 false
315 } else {
316 debug
317 };
318
319 if next_idx < num_segments - 1 {
320 let request = request_with_dictionary_replacement(request.as_ref())?;
323 match capability {
324 Capability::Dictionary(d) => {
325 current_dict = d;
326 }
327 Capability::DictionaryRouter(r) => match r.route(request, false).await? {
328 RouterResponse::<Dict>::Capability(d) => {
329 current_dict = d;
330 }
331 RouterResponse::<Dict>::Unavailable => {
332 return Ok(Some(GenericRouterResponse::Unavailable));
333 }
334 RouterResponse::<Dict>::Debug(d) => {
335 return Ok(Some(GenericRouterResponse::Debug(d)));
338 }
339 },
340 _ => {
341 return Err(RoutingError::BedrockWrongCapabilityType {
342 expected: Dict::debug_typename().into(),
343 actual: capability.debug_typename().into(),
344 moniker: moniker.clone(),
345 }
346 .into());
347 }
348 }
349 } else {
350 let request = request.as_ref().map(|r| r.try_clone()).transpose()?;
356 let capability: Capability = match capability {
357 Capability::DictionaryRouter(r) => match r.route(request, debug).await? {
358 RouterResponse::<Dict>::Capability(c) => c.into(),
359 RouterResponse::<Dict>::Unavailable => {
360 return Ok(Some(GenericRouterResponse::Unavailable));
361 }
362 RouterResponse::<Dict>::Debug(d) => {
363 return Ok(Some(GenericRouterResponse::Debug(d)));
364 }
365 },
366 Capability::ConnectorRouter(r) => match r.route(request, debug).await? {
367 RouterResponse::<Connector>::Capability(c) => c.into(),
368 RouterResponse::<Connector>::Unavailable => {
369 return Ok(Some(GenericRouterResponse::Unavailable));
370 }
371 RouterResponse::<Connector>::Debug(d) => {
372 return Ok(Some(GenericRouterResponse::Debug(d)));
373 }
374 },
375 Capability::DataRouter(r) => match r.route(request, debug).await? {
376 RouterResponse::<Data>::Capability(c) => c.into(),
377 RouterResponse::<Data>::Unavailable => {
378 return Ok(Some(GenericRouterResponse::Unavailable));
379 }
380 RouterResponse::<Data>::Debug(d) => {
381 return Ok(Some(GenericRouterResponse::Debug(d)));
382 }
383 },
384 Capability::DirEntryRouter(r) => match r.route(request, debug).await? {
385 RouterResponse::<DirEntry>::Capability(c) => c.into(),
386 RouterResponse::<DirEntry>::Unavailable => {
387 return Ok(Some(GenericRouterResponse::Unavailable));
388 }
389 RouterResponse::<DirEntry>::Debug(d) => {
390 return Ok(Some(GenericRouterResponse::Debug(d)));
391 }
392 },
393 other => other,
394 };
395 return Ok(Some(GenericRouterResponse::Capability(capability)));
396 }
397 }
398 unreachable!("get_with_request: All cases are handled in the loop");
399 }
400}
401
402pub(super) fn request_with_dictionary_replacement(
408 request: Option<&Request>,
409) -> Result<Option<Request>, RoutingError> {
410 Ok(request.as_ref().map(|r| r.try_clone()).transpose()?.map(|r| {
411 let _ = r.metadata.insert(
412 Name::new(METADATA_KEY_TYPE).unwrap(),
413 Capability::Data(Data::String(CapabilityTypeName::Dictionary.to_string())),
414 );
415 r
416 }))
417}