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) {
248 if let Some(collections) = decl.collections.as_ref() {
249 for collection in collections {
250 if let Some(env) = collection.environment.as_ref() {
251 if let Some(name) = collection.name.as_ref() {
252 let source = DependencyNode::Environment(env.as_str());
253 let target = DependencyNode::Collection(name.as_str());
254 strong_dependencies.add_edge(source, target);
255 }
256 }
257 }
258 }
259}
260
261fn find_offer_node<'a>(
262 offer: &'a fdecl::Offer,
263 source: Option<&'a fdecl::Ref>,
264 source_name: &'a Option<String>,
265 _dictionary: Option<&'a String>,
266) -> Option<DependencyNode<'a>> {
267 if source.is_none() {
268 return None;
269 }
270
271 match source? {
272 fdecl::Ref::Child(fdecl::ChildRef { name, collection }) => {
273 Some(DependencyNode::Child(name, collection.as_ref().map(|s| s.as_str())))
274 }
275 #[cfg(fuchsia_api_level_at_least = "25")]
276 fdecl::Ref::Self_(_) if _dictionary.is_some() => {
277 let root_dict = _dictionary.unwrap().split('/').next().unwrap();
278 return Some(DependencyNode::Capability(root_dict));
279 }
280 fdecl::Ref::Self_(_) => {
281 if let Some(source_name) = source_name {
282 #[cfg(fuchsia_api_level_at_least = "25")]
283 if matches!(offer, fdecl::Offer::Dictionary(_)) {
284 return Some(DependencyNode::Capability(source_name));
285 }
286 if matches!(offer, fdecl::Offer::Storage(_)) {
287 return Some(DependencyNode::Capability(source_name));
288 }
289 }
290
291 Some(DependencyNode::Self_)
292 }
293 fdecl::Ref::Collection(fdecl::CollectionRef { name }) => {
294 Some(DependencyNode::Collection(name))
295 }
296 fdecl::Ref::Capability(fdecl::CapabilityRef { name }) => {
297 Some(DependencyNode::Capability(name))
298 }
299 fdecl::Ref::Parent(_) | fdecl::Ref::Framework(_) | fdecl::Ref::VoidType(_) => None,
300 _ => None,
301 }
302}
303
304fn dynamic_children_in_collection<'a>(
305 dynamic_children: &Vec<(&'a str, &'a str)>,
306 collection: &'a str,
307) -> Vec<&'a str> {
308 dynamic_children
309 .iter()
310 .filter_map(|(n, c)| if *c == collection { Some(*n) } else { None })
311 .collect()
312}
313
314fn add_offer_edges<'a>(
315 source_node: Option<DependencyNode<'a>>,
316 target_node: Option<DependencyNode<'a>>,
317 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
318 dynamic_children: &Vec<(&'a str, &'a str)>,
319) {
320 if source_node.is_none() {
321 return;
322 }
323
324 let source = source_node.unwrap();
325
326 if let DependencyNode::Collection(name) = source {
327 for child_name in dynamic_children_in_collection(dynamic_children, &name) {
328 strong_dependencies.add_edge(
329 DependencyNode::Child(&child_name, Some(&name)),
330 DependencyNode::Collection(name),
331 );
332 }
333 }
334
335 if target_node.is_none() {
336 return;
337 }
338
339 let target = target_node.unwrap();
340
341 strong_dependencies.add_edge(source, target);
342
343 if let DependencyNode::Collection(name) = target {
344 for child_name in dynamic_children_in_collection(dynamic_children, &name) {
345 strong_dependencies.add_edge(source, DependencyNode::Child(child_name, Some(&name)));
346 }
347 }
348}
349
350fn get_dependencies_from_offers<'a>(
352 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
353 decl: &'a fdecl::Component,
354 dynamic_children: &Vec<(&'a str, &'a str)>,
355 dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
356) {
357 let mut all_offers: Vec<&fdecl::Offer> = vec![];
358
359 if let Some(dynamic_offers) = dynamic_offers.as_ref() {
360 for dynamic_offer in dynamic_offers.iter() {
361 all_offers.push(dynamic_offer);
362 }
363 }
364
365 if let Some(offers) = decl.offers.as_ref() {
366 for offer in offers.iter() {
367 all_offers.push(offer);
368 }
369 }
370
371 for offer in all_offers {
372 let (source_node, target_node) = match offer {
373 fdecl::Offer::Protocol(o) => {
374 let source_node = find_offer_node(
375 offer,
376 o.source.as_ref(),
377 &o.source_name,
378 get_source_dictionary!(o),
379 );
380
381 if let Some(fdecl::DependencyType::Strong) = o.dependency_type.as_ref() {
382 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
383
384 (source_node, target_node)
385 } else {
386 continue;
387 }
388 }
389 #[cfg(fuchsia_api_level_at_least = "25")]
390 fdecl::Offer::Dictionary(o) => {
391 let source_node = find_offer_node(
392 offer,
393 o.source.as_ref(),
394 &o.source_name,
395 get_source_dictionary!(o),
396 );
397
398 if let Some(fdecl::DependencyType::Strong) = o.dependency_type.as_ref() {
399 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
400
401 (source_node, target_node)
402 } else {
403 continue;
404 }
405 }
406 fdecl::Offer::Directory(o) => {
407 let source_node = find_offer_node(
408 offer,
409 o.source.as_ref(),
410 &o.source_name,
411 get_source_dictionary!(o),
412 );
413 if let Some(fdecl::DependencyType::Strong) = o.dependency_type.as_ref() {
414 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
415
416 (source_node, target_node)
417 } else {
418 continue;
419 }
420 }
421 fdecl::Offer::Service(o) => {
422 let source_node = find_offer_node(
423 offer,
424 o.source.as_ref(),
425 &o.source_name,
426 get_source_dictionary!(o),
427 );
428
429 #[cfg(fuchsia_api_level_at_least = "HEAD")]
430 {
431 if &fdecl::DependencyType::Strong
432 == o.dependency_type.as_ref().unwrap_or(&fdecl::DependencyType::Strong)
433 {
434 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
435
436 (source_node, target_node)
437 } else {
438 continue;
439 }
440 }
441
442 #[cfg(fuchsia_api_level_less_than = "HEAD")]
443 {
444 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
445
446 (source_node, target_node)
447 }
448 }
449 fdecl::Offer::Storage(o) => {
450 let source_node = find_offer_node(offer, o.source.as_ref(), &o.source_name, None);
451
452 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
453
454 (source_node, target_node)
455 }
456 fdecl::Offer::Runner(o) => {
457 let source_node = find_offer_node(
458 offer,
459 o.source.as_ref(),
460 &o.source_name,
461 get_source_dictionary!(o),
462 );
463
464 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
465
466 (source_node, target_node)
467 }
468 fdecl::Offer::Resolver(o) => {
469 let source_node = find_offer_node(
470 offer,
471 o.source.as_ref(),
472 &o.source_name,
473 get_source_dictionary!(o),
474 );
475
476 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
477
478 (source_node, target_node)
479 }
480 fdecl::Offer::Config(o) => {
481 let source_node = find_offer_node(
482 offer,
483 o.source.as_ref(),
484 &o.source_name,
485 get_source_dictionary!(o),
486 );
487
488 let target_node = find_offer_node(offer, o.target.as_ref(), &None, None);
489
490 (source_node, target_node)
491 }
492 _ => continue,
493 };
494
495 add_offer_edges(source_node, target_node, strong_dependencies, dynamic_children);
496 }
497}
498
499pub fn generate_dependency_graph<'a>(
501 strong_dependencies: &mut DirectedGraph<DependencyNode<'a>>,
502 decl: &'a fdecl::Component,
503 dynamic_children: &Vec<(&'a str, &'a str)>,
504 dynamic_offers: Option<&'a Vec<fdecl::Offer>>,
505) {
506 get_dependencies_from_uses(strong_dependencies, decl);
507 get_dependencies_from_offers(strong_dependencies, decl, dynamic_children, dynamic_offers);
508 get_dependencies_from_capabilities(strong_dependencies, decl);
509 get_dependencies_from_environments(strong_dependencies, decl);
510 get_dependencies_from_children(strong_dependencies, decl);
511 get_dependencies_from_collections(strong_dependencies, decl);
512}