blob: 1e440ccab5a5f48a11f0b7c5133e2e9fdebf0964 [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 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use indexmap::set::IndexSet as HashSet;
16
17use crate::{
18 conversion::{api::ApiName, convert_error::ErrorContext, ConvertError},
19 types::QualifiedName,
20};
21
22use super::api::{AnalysisPhase, Api};
23
24/// Newtype wrapper for a list of APIs, which enforces the invariant
25/// that each API has a unique name.
26///
27/// Specifically, each API should have a unique [`QualifiedName`] which is kept
28/// within an [`ApiName`]. The [`QualifiedName`] is used to refer to this API
29/// from others, e.g. to represent edges in the graph used for garbage collection,
30/// so that's why this uniqueness is so important.
31///
32/// At present, this type also refuses to allow mutation of an API once it
33/// has been added to a set. This is because the autocxx engine is
34/// fundamentally organized into lots of analysis phases, each one _adding_
35/// fields rather than mutating earlier fields. The idea here is that it's
36/// impossible for stupid future maintainers (i.e. me) to make errors by
37/// referring to fields before they're filled in. If a field exists, it's
38/// correct.
39///
40/// While this is currently the case, it's possible that in future we could
41/// see legitimate reasons to break this latter invariant and allow mutation
42/// of APIs within an existing `ApiVec`. But it's extremely important that
43/// the naming-uniqueness-invariant remains, so any such mutation should
44/// allow mutation only of other fields, not the name.
45pub(crate) struct ApiVec<P: AnalysisPhase> {
46 apis: Vec<Api<P>>,
47 names: HashSet<QualifiedName>,
48}
49
50impl<P: AnalysisPhase> ApiVec<P> {
51 pub(crate) fn push(&mut self, api: Api<P>) {
52 let name = api.name();
53 let already_contains = self.already_contains(name);
54 if already_contains {
55 if api.discard_duplicates() {
56 // This is already an IgnoredItem or something else where
57 // we can silently drop it.
58 log::info!("Discarding duplicate API for {}", name);
59 } else {
60 log::info!(
61 "Duplicate API for {} - removing all of them and replacing with an IgnoredItem.",
62 name
63 );
64 self.retain(|api| api.name() != name);
65 self.push(Api::IgnoredItem {
66 name: ApiName::new_from_qualified_name(name.clone()),
67 err: ConvertError::DuplicateItemsFoundInParsing,
68 ctx: Some(ErrorContext::new_for_item(name.get_final_ident())),
69 })
70 }
71 } else {
72 self.names.insert(name.clone());
73 self.apis.push(api)
74 }
75 }
76
77 fn already_contains(&self, name: &QualifiedName) -> bool {
78 self.names.contains(name)
79 }
80
81 pub(crate) fn new() -> Self {
82 Self::default()
83 }
84
85 pub(crate) fn append(&mut self, more: &mut ApiVec<P>) {
86 self.extend(more.apis.drain(..))
87 }
88
89 pub(crate) fn extend(&mut self, it: impl Iterator<Item = Api<P>>) {
90 // Could be optimized in future
91 for api in it {
92 self.push(api)
93 }
94 }
95
96 pub(crate) fn iter(&self) -> impl Iterator<Item = &Api<P>> {
97 self.apis.iter()
98 }
99
100 pub(crate) fn into_iter(self) -> impl Iterator<Item = Api<P>> {
101 self.apis.into_iter()
102 }
103
104 pub(crate) fn is_empty(&self) -> bool {
105 self.apis.is_empty()
106 }
107
108 pub fn retain<F>(&mut self, f: F)
109 where
110 F: FnMut(&Api<P>) -> bool,
111 {
112 self.apis.retain(f);
113 self.names.clear();
114 self.names
115 .extend(self.apis.iter().map(|api| api.name()).cloned());
116 }
117}
118
119impl<P: AnalysisPhase> Default for ApiVec<P> {
120 fn default() -> Self {
121 Self {
122 apis: Default::default(),
123 names: Default::default(),
124 }
125 }
126}
127
128impl<P: AnalysisPhase> FromIterator<Api<P>> for ApiVec<P> {
129 fn from_iter<I: IntoIterator<Item = Api<P>>>(iter: I) -> Self {
130 let mut this = ApiVec::new();
131 for i in iter {
132 // Could be optimized in future
133 this.push(i);
134 }
135 this
136 }
137}