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