blob: baadbd7b205d1b429602e6ee557cebd9dc4ee67f [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use indexmap::set::IndexSet as HashSet;
10use std::collections::VecDeque;
11use std::fmt::Debug;
12
13use itertools::Itertools;
14
15use crate::types::QualifiedName;
16
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070017/// A little like `HasDependencies` but accounts for only direct fiele
18/// and bases.
19pub(crate) trait HasFieldsAndBases {
20 fn name(&self) -> &QualifiedName;
21 /// Return field and base class dependencies of this item.
22 /// This should only include those items where a definition is required,
23 /// not merely a declaration. So if the field type is
24 /// `std::unique_ptr<A>`, this should only return `std::unique_ptr`.
25 fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
26}
Brian Silverman4e662aa2022-05-11 23:10:19 -070027
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070028/// Iterate through APIs in an order such that later APIs have no fields or bases
29/// other than those whose types have already been processed.
30pub(super) fn fields_and_bases_first<'a, T: HasFieldsAndBases + Debug + 'a>(
Brian Silverman4e662aa2022-05-11 23:10:19 -070031 inputs: impl Iterator<Item = &'a T> + 'a,
32) -> impl Iterator<Item = &'a T> {
33 let queue: VecDeque<_> = inputs.collect();
34 let yet_to_do = queue.iter().map(|api| api.name()).collect();
35 DepthFirstIter { queue, yet_to_do }
36}
37
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070038struct DepthFirstIter<'a, T: HasFieldsAndBases + Debug> {
Brian Silverman4e662aa2022-05-11 23:10:19 -070039 queue: VecDeque<&'a T>,
40 yet_to_do: HashSet<&'a QualifiedName>,
41}
42
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070043impl<'a, T: HasFieldsAndBases + Debug> Iterator for DepthFirstIter<'a, T> {
Brian Silverman4e662aa2022-05-11 23:10:19 -070044 type Item = &'a T;
45
46 fn next(&mut self) -> Option<Self::Item> {
47 let first_candidate = self.queue.get(0).map(|api| api.name());
48 while let Some(candidate) = self.queue.pop_front() {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070049 if !candidate
50 .field_and_base_deps()
51 .any(|d| self.yet_to_do.contains(&d))
52 {
Brian Silverman4e662aa2022-05-11 23:10:19 -070053 self.yet_to_do.remove(candidate.name());
54 return Some(candidate);
55 }
56 self.queue.push_back(candidate);
57 if self.queue.get(0).map(|api| api.name()) == first_candidate {
58 panic!(
59 "Failed to find a candidate; there must be a circular dependency. Queue is {}",
60 self.queue
61 .iter()
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070062 .map(|item| format!(
63 "{}: {}",
64 item.name(),
65 item.field_and_base_deps().join(",")
66 ))
Brian Silverman4e662aa2022-05-11 23:10:19 -070067 .join("\n")
68 );
69 }
70 }
71 None
72 }
73}
74
75#[cfg(test)]
76mod test {
77 use crate::types::QualifiedName;
78
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070079 use super::{fields_and_bases_first, HasFieldsAndBases};
Brian Silverman4e662aa2022-05-11 23:10:19 -070080
81 #[derive(Debug)]
82 struct Thing(QualifiedName, Vec<QualifiedName>);
83
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070084 impl HasFieldsAndBases for Thing {
Brian Silverman4e662aa2022-05-11 23:10:19 -070085 fn name(&self) -> &QualifiedName {
86 &self.0
87 }
88
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070089 fn field_and_base_deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
Brian Silverman4e662aa2022-05-11 23:10:19 -070090 Box::new(self.1.iter())
91 }
92 }
93
94 #[test]
95 fn test() {
96 let a = Thing(QualifiedName::new_from_cpp_name("a"), vec![]);
97 let b = Thing(
98 QualifiedName::new_from_cpp_name("b"),
99 vec![
100 QualifiedName::new_from_cpp_name("a"),
101 QualifiedName::new_from_cpp_name("c"),
102 ],
103 );
104 let c = Thing(
105 QualifiedName::new_from_cpp_name("c"),
106 vec![QualifiedName::new_from_cpp_name("a")],
107 );
108 let api_list = vec![a, b, c];
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700109 let mut it = fields_and_bases_first(api_list.iter());
Brian Silverman4e662aa2022-05-11 23:10:19 -0700110 assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("a"));
111 assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("c"));
112 assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("b"));
113 assert!(it.next().is_none());
114 }
115}