1use directed_graph::DirectedGraph;
6use fidl_fuchsia_component_decl as fdecl;
7use std::fmt;
8
9#[cfg(fuchsia_api_level_at_least = "25")]
10macro_rules! get_source_dictionary {
11 ($decl:ident) => {
12 $decl.source_dictionary.as_ref()
13 };
14}
15#[cfg(fuchsia_api_level_less_than = "25")]
16macro_rules! get_source_dictionary {
17 ($decl:ident) => {
18 None
19 };
20}
21
22#[derive(Copy, Clone, Hash, Ord, Debug, PartialOrd, PartialEq, Eq)]
25pub enum DependencyNode<'a> {
26 Self_,
27 Child(&'a str, Option<&'a str>),
28 Collection(&'a str),
29 Environment(&'a str),
30 Capability(&'a str),
31}
32
33impl<'a> fmt::Display for DependencyNode<'a> {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 DependencyNode::Self_ => write!(f, "self"),
37 DependencyNode::Child(name, None) => write!(f, "child {}", name),
38 DependencyNode::Child(name, Some(collection)) => {
39 write!(f, "child {}:{}", collection, name)
40 }
41 DependencyNode::Collection(name) => write!(f, "collection {}", name),
42 DependencyNode::Environment(name) => write!(f, "environment {}", name),
43 DependencyNode::Capability(name) => write!(f, "capability {}", name),
44 }
45 }
46}
47
48fn ref_to_dependency_node<'a>(ref_: Option<&'a fdecl::Ref>) -> Option<DependencyNode<'a>> {
49 match ref_? {
50 fdecl::Ref::Self_(_) => Some(DependencyNode::Self_),
51 fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
52 Some(DependencyNode::Child(name, collection.as_ref().map(|s| s.as_str())))
53 }
54 fdecl::Ref::Collection(fdecl::CollectionRef { name }) => {
55 Some(DependencyNode::Collection(name))
56 }
57 fdecl::Ref::Capability(fdecl::CapabilityRef { name }) => {
58 Some(DependencyNode::Capability(name))
59 }
60 fdecl::Ref::Framework(_)
61 | fdecl::Ref::Parent(_)
62 | fdecl::Ref::Debug(_)
63 | fdecl::Ref::VoidType(_) => None,
64 #[cfg(fuchsia_api_level_at_least = "HEAD")]
65 fdecl::Ref::Environment(_) => None,
66 _ => None,
67 }
68}
69
70fn get_dependencies_from_uses<'a>(
72 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
73 decl: &'a fdecl::Component,
74) {
75 if let Some(uses) = decl.uses.as_ref() {
76 for use_ in uses.iter() {
77 #[allow(unused_variables)]
78 let (dependency_type, source, source_name, dict) = match use_ {
79 fdecl::Use::Service(u) => {
80 (u.dependency_type, &u.source, &u.source_name, get_source_dictionary!(u))
81 }
82 fdecl::Use::Protocol(u) => {
83 (u.dependency_type, &u.source, &u.source_name, get_source_dictionary!(u))
84 }
85 fdecl::Use::Directory(u) => {
86 (u.dependency_type, &u.source, &u.source_name, get_source_dictionary!(u))
87 }
88 fdecl::Use::EventStream(u) => (
89 Some(fdecl::DependencyType::Strong),
90 &u.source,
91 &u.source_name,
92 None::<&String>,
93 ),
94 #[cfg(fuchsia_api_level_at_least = "HEAD")]
95 fdecl::Use::Runner(u) => (
96 Some(fdecl::DependencyType::Strong),
97 &u.source,
98 &u.source_name,
99 get_source_dictionary!(u),
100 ),
101 #[cfg(fuchsia_api_level_at_least = "HEAD")]
102 fdecl::Use::Config(u) => (
103 Some(fdecl::DependencyType::Strong),
104 &u.source,
105 &u.source_name,
106 get_source_dictionary!(u),
107 ),
108 fdecl::Use::Storage(_) => continue,
110 _ => continue,
111 };
112 if dependency_type != Some(fdecl::DependencyType::Strong) {
113 continue;
114 }
115
116 let dependency_nodes = match &source {
117 Some(fdecl::Ref::Child(fdecl::ChildRef { name, collection })) => {
118 vec![DependencyNode::Child(name, collection.as_ref().map(|s| s.as_str()))]
119 }
120 Some(fdecl::Ref::Self_(_)) => {
121 #[cfg(fuchsia_api_level_at_least = "25")]
122 if dict.as_ref().is_some() {
123 if let Some(source_name) = source_name.as_ref() {
124 vec![DependencyNode::Capability(source_name)]
125 } else {
126 vec![]
127 }
128 } else {
129 vec![]
130 }
131
132 #[cfg(fuchsia_api_level_less_than = "25")]
133 vec![]
134 }
135 Some(fdecl::Ref::Collection(fdecl::CollectionRef { name })) => {
136 let mut nodes = vec![];
137 if let Some(children) = decl.children.as_ref() {
138 for child in children {
139 if let Some(child_name) = child.name.as_ref() {
140 nodes.push(DependencyNode::Child(child_name, Some(name)));
141 }
142 }
143 }
144 nodes
145 }
146 _ => vec![],
147 };
148
149 for source_node in dependency_nodes {
150 strong_dependencies.add_edge(source_node, DependencyNode::Self_);
151 }
152 }
153 }
154}
155
156fn get_dependencies_from_capabilities<'a>(
157 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
158 decl: &'a fdecl::Component,
159) {
160 if let Some(capabilities) = decl.capabilities.as_ref() {
161 for cap in capabilities {
162 match cap {
163 #[cfg(fuchsia_api_level_at_least = "25")]
164 fdecl::Capability::Dictionary(dictionary) => {
165 if dictionary.source_path.as_ref().is_some() {
166 if let Some(name) = dictionary.name.as_ref() {
167 strong_dependencies
170 .add_edge(DependencyNode::Self_, DependencyNode::Capability(name));
171 }
172 }
173 }
174 fdecl::Capability::Storage(storage) => {
175 if let (Some(name), Some(_backing_dir)) =
176 (storage.name.as_ref(), storage.backing_dir.as_ref())
177 {
178 if let Some(source_node) = ref_to_dependency_node(storage.source.as_ref()) {
179 strong_dependencies
180 .add_edge(source_node, DependencyNode::Capability(name));
181 }
182 }
183 }
184 _ => continue,
185 }
186 }
187 }
188}
189
190fn get_dependencies_from_environments<'a>(
191 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
192 decl: &'a fdecl::Component,
193) {
194 if let Some(environment) = decl.environments.as_ref() {
195 for environment in environment {
196 if let Some(name) = &environment.name {
197 let target = DependencyNode::Environment(name);
198 if let Some(debugs) = environment.debug_capabilities.as_ref() {
199 for debug in debugs {
200 if let fdecl::DebugRegistration::Protocol(o) = debug {
201 if let Some(source_node) = ref_to_dependency_node(o.source.as_ref()) {
202 strong_dependencies.add_edge(source_node, target);
203 }
204 }
205 }
206 }
207 if let Some(runners) = environment.runners.as_ref() {
208 for runner in runners {
209 if let Some(source_node) = ref_to_dependency_node(runner.source.as_ref()) {
210 strong_dependencies.add_edge(source_node, target);
211 }
212 }
213 }
214 if let Some(resolvers) = environment.resolvers.as_ref() {
215 for resolver in resolvers {
216 if let Some(source_node) = ref_to_dependency_node(resolver.source.as_ref())
217 {
218 strong_dependencies.add_edge(source_node, target);
219 }
220 }
221 }
222 }
223 }
224 }
225}
226
227fn get_dependencies_from_children<'a>(
228 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
229 decl: &'a fdecl::Component,
230) {
231 if let Some(children) = decl.children.as_ref() {
232 for child in children {
233 if let Some(name) = child.name.as_ref() {
234 if let Some(env) = child.environment.as_ref() {
235 let source = DependencyNode::Environment(env.as_str());
236 let target = DependencyNode::Child(name, None);
237 strong_dependencies.add_edge(source, target);
238 }
239 }
240 }
241 }
242}
243
244fn get_dependencies_from_collections<'a>(
245 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
246 decl: &'a fdecl::Component,
247 dynamic_children: &Vec<(&'a str, &'a str)>,
248) {
249 if let Some(collections) = decl.collections.as_ref() {
250 for collection in collections {
251 if let Some(env) = collection.environment.as_ref() {
252 if let Some(name) = collection.name.as_ref() {
253 let source = DependencyNode::Environment(env.as_str());
254 let target = DependencyNode::Collection(name.as_str());
255 strong_dependencies.add_edge(source, target);
256
257 for child_name in dynamic_children_in_collection(dynamic_children, &name) {
258 strong_dependencies
259 .add_edge(source, DependencyNode::Child(child_name, Some(&name)));
260 }
261 }
262 }
263 }
264 }
265}
266
267fn find_offer_node<'a>(
268 offer: &'a fdecl::Offer,
269 source: Option<&'a fdecl::Ref>,
270 source_name: &'a Option<String>,
271 _dictionary: Option<&'a String>,
272) -> Option<DependencyNode<'a>> {
273 if source.is_none() {
274 return None;
275 }
276
277 match source? {
278 fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
279 Some(DependencyNode::Child(name, collection.as_ref().map(|s| s.as_str())))
280 }
281 #[cfg(fuchsia_api_level_at_least = "25")]
282 fdecl::Ref::Self_(_) if _dictionary.is_some() => {
283 let root_dict = _dictionary.unwrap().split('/').next().unwrap();
284 return Some(DependencyNode::Capability(root_dict));
285 }
286 fdecl::Ref::Self_(_) => {
287 if let Some(source_name) = source_name {
288 #[cfg(fuchsia_api_level_at_least = "25")]
289 if matches!(offer, fdecl::Offer::Dictionary(_)) {
290 return Some(DependencyNode::Capability(source_name));
291 }
292 if matches!(offer, fdecl::Offer::Storage(_)) {
293 return Some(DependencyNode::Capability(source_name));
294 }
295 }
296
297 Some(DependencyNode::Self_)
298 }
299 fdecl::Ref::Collection(fdecl::CollectionRef { name }) => {
300 Some(DependencyNode::Collection(name))
301 }
302 fdecl::Ref::Capability(fdecl::CapabilityRef { name }) => {
303 Some(DependencyNode::Capability(name))
304 }
305 fdecl::Ref::Parent(_) | fdecl::Ref::Framework(_) | fdecl::Ref::VoidType(_) => None,
306 _ => None,
307 }
308}
309
310fn dynamic_children_in_collection<'a>(
311 dynamic_children: &Vec<(&'a str, &'a str)>,
312 collection: &'a str,
313) -> Vec<&'a str> {
314 dynamic_children
315 .iter()
316 .filter_map(|(n, c)| if *c == collection { Some(*n) } else { None })
317 .collect()
318}
319
320fn add_offer_edges<'a>(
321 source_node: Option<DependencyNode<'a>>,
322 target_node: Option<DependencyNode<'a>>,
323 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
324 dynamic_children: &Vec<(&'a str, &'a str)>,
325) {
326 if source_node.is_none() {
327 return;
328 }
329
330 let source = source_node.unwrap();
331
332 if let DependencyNode::Collection(name) = source {
333 for child_name in dynamic_children_in_collection(dynamic_children, &name) {
334 strong_dependencies.add_edge(
335 DependencyNode::Child(&child_name, Some(&name)),
336 DependencyNode::Collection(name),
337 );
338 }
339 }
340
341 if target_node.is_none() {
342 return;
343 }
344
345 let target = target_node.unwrap();
346
347 strong_dependencies.add_edge(source, target);
348
349 if let DependencyNode::Collection(name) = target {
350 for child_name in dynamic_children_in_collection(dynamic_children, &name) {
351 strong_dependencies.add_edge(source, DependencyNode::Child(child_name, Some(&name)));
352 }
353 }
354}
355
356fn get_dependencies_from_offers<'a>(
358 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
359 decl: &'a fdecl::Component,
360 dynamic_children: &Vec<(&'a str, &'a str)>,
361 dynamic_offers: &'a Vec<fdecl::Offer>,
362) {
363 let mut all_offers: Vec<&fdecl::Offer> = vec![];
364
365 for dynamic_offer in dynamic_offers.iter() {
366 all_offers.push(dynamic_offer);
367 }
368
369 if let Some(offers) = decl.offers.as_ref() {
370 for offer in offers.iter() {
371 all_offers.push(offer);
372 }
373 }
374
375 for offer in all_offers {
376 let (source_node, target_node) = match offer {
377 fdecl::Offer::Protocol(o) => {
378 let source_node = find_offer_node(
379 offer,
380 o.source.as_ref(),
381 &o.source_name,
382 get_source_dictionary!(o),
383 );
384
385 if let Some(fdecl::DependencyType::Strong) = o.dependency_type.as_ref() {
386 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
387
388 (source_node, target_node)
389 } else {
390 continue;
391 }
392 }
393 #[cfg(fuchsia_api_level_at_least = "25")]
394 fdecl::Offer::Dictionary(o) => {
395 let source_node = find_offer_node(
396 offer,
397 o.source.as_ref(),
398 &o.source_name,
399 get_source_dictionary!(o),
400 );
401
402 if let Some(fdecl::DependencyType::Strong) = o.dependency_type.as_ref() {
403 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
404
405 (source_node, target_node)
406 } else {
407 continue;
408 }
409 }
410 fdecl::Offer::Directory(o) => {
411 let source_node = find_offer_node(
412 offer,
413 o.source.as_ref(),
414 &o.source_name,
415 get_source_dictionary!(o),
416 );
417 if let Some(fdecl::DependencyType::Strong) = o.dependency_type.as_ref() {
418 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
419
420 (source_node, target_node)
421 } else {
422 continue;
423 }
424 }
425 fdecl::Offer::Service(o) => {
426 let source_node = find_offer_node(
427 offer,
428 o.source.as_ref(),
429 &o.source_name,
430 get_source_dictionary!(o),
431 );
432
433 #[cfg(fuchsia_api_level_at_least = "HEAD")]
434 {
435 if &fdecl::DependencyType::Strong
436 == o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)
437 {
438 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
439
440 (source_node, target_node)
441 } else {
442 continue;
443 }
444 }
445
446 #[cfg(fuchsia_api_level_less_than = "HEAD")]
447 {
448 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
449
450 (source_node, target_node)
451 }
452 }
453 fdecl::Offer::Storage(o) => {
454 let source_node = find_offer_node(offer, o.source.as_ref(), &o.source_name, None);
455
456 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
457
458 (source_node, target_node)
459 }
460 fdecl::Offer::Runner(o) => {
461 let source_node = find_offer_node(
462 offer,
463 o.source.as_ref(),
464 &o.source_name,
465 get_source_dictionary!(o),
466 );
467
468 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
469
470 (source_node, target_node)
471 }
472 fdecl::Offer::Resolver(o) => {
473 let source_node = find_offer_node(
474 offer,
475 o.source.as_ref(),
476 &o.source_name,
477 get_source_dictionary!(o),
478 );
479
480 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
481
482 (source_node, target_node)
483 }
484 fdecl::Offer::Config(o) => {
485 let source_node = find_offer_node(
486 offer,
487 o.source.as_ref(),
488 &o.source_name,
489 get_source_dictionary!(o),
490 );
491
492 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
493
494 (source_node, target_node)
495 }
496 _ => continue,
497 };
498
499 add_offer_edges(source_node, target_node, strong_dependencies, dynamic_children);
500 }
501}
502
503pub fn generate_dependency_graph<'a>(
505 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
506 decl: &'a fdecl::Component,
507 dynamic_children: &Vec<(&'a str, &'a str)>,
508 dynamic_offers: &'a Vec<fdecl::Offer>,
509) {
510 get_dependencies_from_uses(strong_dependencies, decl);
511 get_dependencies_from_offers(strong_dependencies, decl, dynamic_children, dynamic_offers);
512 get_dependencies_from_capabilities(strong_dependencies, decl);
513 get_dependencies_from_environments(strong_dependencies, decl);
514 get_dependencies_from_children(strong_dependencies, decl);
515 get_dependencies_from_collections(strong_dependencies, decl, dynamic_children);
516}