1use crate::errors::UtilsError;
6use fidl_fuchsia_component_sandbox::{
7 self as fsandbox, AggregateSource, Capability, CapabilityId, DictionaryItem, DirConnector,
8 DirConnectorRouterRouteResponse, RouteRequest,
9};
10use std::cell::Cell;
11use std::collections::HashMap;
12
13pub struct DictionaryUtil {
14 capability_store: fsandbox::CapabilityStoreProxy,
15 cap_id: Cell<CapabilityId>,
16}
17
18impl DictionaryUtil {
19 pub fn new(capability_store: fsandbox::CapabilityStoreProxy) -> Self {
20 Self { capability_store, cap_id: Cell::new(0) }
21 }
22}
23
24impl DictionaryUtil {
25 pub async fn import_dictionary(
26 &self,
27 dictionary: fsandbox::DictionaryRef,
28 ) -> Result<CapabilityId, UtilsError> {
29 let dest_id = self.next_cap_id();
30 let capability = Capability::Dictionary(dictionary);
31 self.capability_store.import(dest_id, capability).await??;
32 Ok(dest_id)
33 }
34
35 pub async fn copy_export_dictionary(
36 &self,
37 dictionary_id: CapabilityId,
38 ) -> Result<fsandbox::DictionaryRef, UtilsError> {
39 let dest_id = self.next_cap_id();
40 self.capability_store.dictionary_copy(dictionary_id, dest_id).await??;
41 let exported = self.capability_store.export(dest_id).await??;
42 if let Capability::Dictionary(d) = exported {
43 Ok(d)
44 } else {
45 Err(UtilsError::UnexpectedCapabilityType(
46 "Dictionary".to_string(),
47 format!("{:?}", exported),
48 ))
49 }
50 }
51
52 pub async fn dictionary_dir_connector_route(
53 &self,
54 dictionary_id: CapabilityId,
55 service_name: &str,
56 ) -> Result<DirConnector, UtilsError> {
57 let dest_id = self.next_cap_id();
58 self.capability_store.dictionary_get(dictionary_id, service_name, dest_id).await??;
59 let exported = self.capability_store.export(dest_id).await??;
60 let Capability::DirConnectorRouter(router) = exported else {
61 return Err(UtilsError::UnexpectedCapabilityType(
62 "DirConnectorRouter".to_string(),
63 format!("{:?}", exported),
64 ));
65 };
66
67 let routed = router.into_proxy().route(RouteRequest { ..Default::default() }).await??;
68 let DirConnectorRouterRouteResponse::DirConnector(connector) = routed else {
69 return Err(UtilsError::UnexpectedCapabilityType(
70 "DirConnector".to_string(),
71 format!("{:?}", routed),
72 ));
73 };
74
75 Ok(connector)
76 }
77
78 pub async fn create_aggregate_dictionary(
79 &self,
80 sources: HashMap<String, Vec<AggregateSource>>,
81 ) -> Result<CapabilityId, UtilsError> {
82 let dest_id = self.next_cap_id();
83 self.capability_store.dictionary_create(dest_id).await??;
84
85 for (service_name, sources) in sources.into_iter() {
86 let aggregate = self.capability_store.create_service_aggregate(sources).await??;
87
88 let imported = self.next_cap_id();
89 self.capability_store.import(imported, Capability::DirConnector(aggregate)).await??;
90
91 self.capability_store
92 .dictionary_insert(dest_id, &DictionaryItem { key: service_name, value: imported })
93 .await??;
94 }
95
96 Ok(dest_id)
97 }
98
99 fn next_cap_id(&self) -> CapabilityId {
100 self.cap_id.set(self.cap_id.get() + 1);
101 self.cap_id.get()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use fidl::endpoints::{Proxy, create_proxy_and_stream};
109 use futures::StreamExt;
110
111 #[fuchsia::test]
112 async fn test_import_dictionary() {
113 let (proxy, mut stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
114 let util = DictionaryUtil::new(proxy);
115
116 let task = fuchsia_async::Task::local(async move {
117 if let Some(Ok(request)) = stream.next().await {
118 match request {
119 fsandbox::CapabilityStoreRequest::Import { id, capability, responder } => {
120 assert_eq!(id, 1);
121 assert!(matches!(capability, Capability::Dictionary(_)));
122 responder.send(Ok(())).unwrap();
123 }
124 _ => panic!("Unexpected request"),
125 }
126 }
127 });
128
129 let dictionary = fsandbox::DictionaryRef { token: fidl::EventPair::create().0 };
130 let id = util.import_dictionary(dictionary).await.expect("Import failed");
131 assert_eq!(id, 1);
132 task.await;
133 }
134
135 #[fuchsia::test]
136 async fn test_copy_export_dictionary() {
137 let (proxy, mut stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
138 let util = DictionaryUtil::new(proxy);
139
140 let task = fuchsia_async::Task::local(async move {
141 if let Some(Ok(fsandbox::CapabilityStoreRequest::DictionaryCopy {
143 id,
144 dest_id,
145 responder,
146 })) = stream.next().await
147 {
148 assert_eq!(id, 100);
149 assert_eq!(dest_id, 1);
150 responder.send(Ok(())).unwrap();
151 } else {
152 panic!("Expected DictionaryCopy");
153 }
154
155 if let Some(Ok(fsandbox::CapabilityStoreRequest::Export { id, responder })) =
157 stream.next().await
158 {
159 assert_eq!(id, 1);
160 responder
161 .send(Ok(Capability::Dictionary(fsandbox::DictionaryRef {
162 token: fidl::EventPair::create().0,
163 })))
164 .unwrap();
165 } else {
166 panic!("Expected Export");
167 }
168 });
169
170 let _ = util.copy_export_dictionary(100).await.expect("Copy export failed");
171 task.await;
172 }
173
174 #[fuchsia::test]
175 async fn test_dictionary_dir_connector_route() {
176 let (proxy, mut stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
177 let util = DictionaryUtil::new(proxy);
178
179 let task = fuchsia_async::Task::local(async move {
180 if let Some(Ok(fsandbox::CapabilityStoreRequest::DictionaryGet {
182 id,
183 key,
184 dest_id,
185 responder,
186 })) = stream.next().await
187 {
188 assert_eq!(id, 100);
189 assert_eq!(key, "service");
190 assert_eq!(dest_id, 1);
191 responder.send(Ok(())).unwrap();
192 } else {
193 panic!("Expected DictionaryGet");
194 }
195
196 if let Some(Ok(fsandbox::CapabilityStoreRequest::Export { id, responder })) =
198 stream.next().await
199 {
200 assert_eq!(id, 1);
201
202 let (router_client, mut router_stream) =
203 create_proxy_and_stream::<fsandbox::DirConnectorRouterMarker>();
204 fuchsia_async::Task::local(async move {
205 if let Some(Ok(fsandbox::DirConnectorRouterRequest::Route {
206 payload: _,
207 responder,
208 })) = router_stream.next().await
209 {
210 let connector =
211 fsandbox::DirConnector { token: fidl::EventPair::create().0 };
212 responder
213 .send(Ok(DirConnectorRouterRouteResponse::DirConnector(connector)))
214 .unwrap();
215 }
216 })
217 .detach();
218
219 responder
220 .send(Ok(Capability::DirConnectorRouter(
221 router_client.into_client_end().unwrap(),
222 )))
223 .unwrap();
224 } else {
225 panic!("Expected Export");
226 }
227 });
228
229 let _ = util.dictionary_dir_connector_route(100, "service").await.expect("Route failed");
230 task.await;
231 }
232
233 #[fuchsia::test]
234 async fn test_create_aggregate_dictionary() {
235 let (proxy, mut stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
236 let util = DictionaryUtil::new(proxy);
237
238 let task = fuchsia_async::Task::local(async move {
239 if let Some(Ok(fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder })) =
241 stream.next().await
242 {
243 assert_eq!(id, 1);
244 responder.send(Ok(())).unwrap();
245 } else {
246 panic!("Expected DictionaryCreate");
247 }
248
249 if let Some(Ok(fsandbox::CapabilityStoreRequest::CreateServiceAggregate {
251 sources: _,
252 responder,
253 })) = stream.next().await
254 {
255 let connector = fsandbox::DirConnector { token: fidl::EventPair::create().0 };
256 responder.send(Ok(connector)).unwrap();
257 } else {
258 panic!("Expected CreateServiceAggregate");
259 }
260
261 if let Some(Ok(fsandbox::CapabilityStoreRequest::Import {
263 id,
264 capability,
265 responder,
266 })) = stream.next().await
267 {
268 assert_eq!(id, 2);
269 assert!(matches!(capability, Capability::DirConnector(_)));
270 responder.send(Ok(())).unwrap();
271 } else {
272 panic!("Expected Import");
273 }
274
275 if let Some(Ok(fsandbox::CapabilityStoreRequest::DictionaryInsert {
277 id,
278 item,
279 responder,
280 })) = stream.next().await
281 {
282 assert_eq!(id, 1);
283 assert_eq!(item.key, "service");
284 assert_eq!(item.value, 2);
285 responder.send(Ok(())).unwrap();
286 } else {
287 panic!("Expected DictionaryInsert");
288 }
289 });
290
291 let mut sources = HashMap::new();
292 sources.insert(
293 "service".to_string(),
294 vec![fsandbox::AggregateSource {
295 dir_connector: Some(fsandbox::DirConnector { token: fidl::EventPair::create().0 }),
296 ..Default::default()
297 }],
298 );
299 let id = util.create_aggregate_dictionary(sources).await.expect("Create aggregate failed");
300 assert_eq!(id, 1);
301 task.await;
302 }
303}