routing/bedrock/
with_porcelain_type.rs1use crate::bedrock::request_metadata::METADATA_KEY_TYPE;
6use crate::error::RoutingError;
7use async_trait::async_trait;
8use cm_rust::CapabilityTypeName;
9use moniker::ExtendedMoniker;
10use router_error::RouterError;
11use sandbox::{Capability, CapabilityBound, Data, Request, Routable, Router, RouterResponse};
12
13pub fn is_supported(porcelain_type: &CapabilityTypeName) -> bool {
14 matches!(porcelain_type, CapabilityTypeName::Protocol | CapabilityTypeName::Config)
15}
16
17pub trait WithPorcelainType {
18 fn with_porcelain_type(
22 self,
23 porcelain_type: CapabilityTypeName,
24 moniker: impl Into<ExtendedMoniker>,
25 ) -> Self;
26}
27
28#[derive(Debug, Clone)]
29struct RouterWithPorcelainType<T: CapabilityBound> {
30 router: Router<T>,
31 porcelain_type: CapabilityTypeName,
32 moniker: ExtendedMoniker,
33}
34
35#[async_trait]
36impl<T: CapabilityBound> Routable<T> for RouterWithPorcelainType<T> {
37 async fn route(
38 &self,
39 request: Option<Request>,
40 debug: bool,
41 ) -> Result<RouterResponse<T>, RouterError> {
42 let request = request.ok_or_else(|| RouterError::InvalidArgs)?;
43 let RouterWithPorcelainType { router, porcelain_type, moniker } = self;
44 let Capability::Data(Data::String(capability_type)) = request
45 .metadata
46 .get(&cm_types::Name::new(METADATA_KEY_TYPE).unwrap())
47 .map_err(|()| RoutingError::BedrockNotCloneable { moniker: moniker.clone() })?
48 .unwrap_or_else(|| {
49 panic!("missing capability type {porcelain_type} for request: {request:?}")
50 })
51 else {
52 return Err(RoutingError::BedrockNotPresentInDictionary {
53 moniker: moniker.clone(),
54 name: String::from("type"),
55 }
56 .into());
57 };
58 let porcelain_type = porcelain_type.to_string();
59 if capability_type == porcelain_type {
60 router.route(Some(request), debug).await
61 } else {
62 Err(RoutingError::BedrockWrongCapabilityType {
63 moniker: moniker.clone(),
64 actual: capability_type,
65 expected: porcelain_type,
66 }
67 .into())
68 }
69 }
70}
71
72impl<T: CapabilityBound> WithPorcelainType for Router<T> {
73 fn with_porcelain_type(
74 self,
75 porcelain_type: CapabilityTypeName,
76 moniker: impl Into<ExtendedMoniker>,
77 ) -> Self {
78 if !is_supported(&porcelain_type) {
79 return self;
80 }
81
82 Router::<T>::new(RouterWithPorcelainType::<T> {
83 router: self,
84 porcelain_type,
85 moniker: moniker.into(),
86 })
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::bedrock::request_metadata::{protocol_metadata, Metadata, METADATA_KEY_TYPE};
94 use assert_matches::assert_matches;
95 use cm_rust::Availability;
96 use cm_types::Name;
97 use moniker::Moniker;
98 use router_error::{DowncastErrorForTest, RouterError};
99 use sandbox::{Capability, Data, Dict, WeakInstanceToken};
100 use std::sync::Arc;
101
102 #[derive(Debug)]
103 struct FakeInstanceToken {}
104
105 impl FakeInstanceToken {
106 fn new() -> WeakInstanceToken {
107 WeakInstanceToken { inner: Arc::new(FakeInstanceToken {}) }
108 }
109 }
110
111 impl sandbox::WeakInstanceTokenAny for FakeInstanceToken {
112 fn as_any(&self) -> &dyn std::any::Any {
113 self
114 }
115 }
116
117 #[fuchsia::test]
118 async fn porcelain_type_good() {
119 let source = Data::String("hello".to_string());
120 let base = Router::<Data>::new_ok(source);
121 let proxy = base.with_porcelain_type(CapabilityTypeName::Protocol, Moniker::root());
122 let metadata = protocol_metadata(Availability::Optional);
123 let capability = proxy
124 .route(Some(Request { target: FakeInstanceToken::new(), metadata }), false)
125 .await
126 .unwrap();
127 let capability = match capability {
128 RouterResponse::<Data>::Capability(d) => d,
129 c => panic!("Bad enum {:#?}", c),
130 };
131 assert_eq!(capability, Data::String("hello".to_string()));
132 }
133
134 #[fuchsia::test]
135 async fn porcelain_type_bad() {
136 let source = Data::String("hello".to_string());
137 let base = Router::<Data>::new_ok(source);
138 let proxy = base.with_porcelain_type(CapabilityTypeName::Protocol, Moniker::root());
139 let metadata = Dict::new();
140 metadata
141 .insert(
142 Name::new(METADATA_KEY_TYPE).unwrap(),
143 Capability::Data(Data::String(String::from("directory"))),
144 )
145 .unwrap();
146 metadata.set_metadata(Availability::Optional);
147 let error = proxy
148 .route(Some(Request { target: FakeInstanceToken::new(), metadata }), false)
149 .await
150 .unwrap_err();
151 assert_matches!(
152 error,
153 RouterError::NotFound(err)
154 if matches!(
155 err.downcast_for_test::<RoutingError>(),
156 RoutingError::BedrockWrongCapabilityType { actual: _, expected: _, moniker: _},
157 )
158 );
159 }
160}