Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 1 | // 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 | |
| 15 | use indexmap::set::IndexSet as HashSet; |
| 16 | |
| 17 | use crate::{ |
| 18 | conversion::{api::ApiName, convert_error::ErrorContext, ConvertError}, |
| 19 | types::QualifiedName, |
| 20 | }; |
| 21 | |
| 22 | use 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. |
| 45 | pub(crate) struct ApiVec<P: AnalysisPhase> { |
| 46 | apis: Vec<Api<P>>, |
| 47 | names: HashSet<QualifiedName>, |
| 48 | } |
| 49 | |
| 50 | impl<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 | |
| 119 | impl<P: AnalysisPhase> Default for ApiVec<P> { |
| 120 | fn default() -> Self { |
| 121 | Self { |
| 122 | apis: Default::default(), |
| 123 | names: Default::default(), |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | impl<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 | } |