• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2024 The Android Open Source Project
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 //      http://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 std::{borrow::Borrow, collections::BTreeSet};
16 
17 use crates_index::{Dependency, Version};
18 
19 // A reference to feature. Either an explicit feature or an optional dependency.
20 #[derive(Debug, Clone, PartialEq, Eq)]
21 pub enum FeatureRef<'a> {
22     Feature(&'a str),
23     OptionalDep(&'a Dependency),
24 }
25 
26 impl<'a> FeatureRef<'a> {
name(&self) -> &str27     pub fn name(&self) -> &str {
28         match self {
29             FeatureRef::Feature(name) => name,
30             FeatureRef::OptionalDep(dep) => dep.name(),
31         }
32     }
33 }
34 
35 // Traits that let us use FeatureRef as an element of a set.
36 impl<'a> PartialOrd for FeatureRef<'a> {
partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>37     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
38         Some(self.cmp(other))
39     }
40 }
41 impl<'a> Ord for FeatureRef<'a> {
cmp(&self, other: &Self) -> std::cmp::Ordering42     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
43         self.name().cmp(other.name())
44     }
45 }
46 
47 // Lets us retrieve a set element by name.
48 impl<'a> Borrow<str> for FeatureRef<'a> {
borrow(&self) -> &str49     fn borrow(&self) -> &str {
50         self.name()
51     }
52 }
53 
54 pub type TypedFeatures<'a> = BTreeSet<FeatureRef<'a>>;
55 
56 pub trait FeaturesAndOptionalDeps {
features_and_optional_deps(&self) -> TypedFeatures57     fn features_and_optional_deps(&self) -> TypedFeatures;
58 }
59 
60 impl FeaturesAndOptionalDeps for Version {
features_and_optional_deps(&self) -> TypedFeatures61     fn features_and_optional_deps(&self) -> TypedFeatures {
62         let explicit_deps = self
63             .features()
64             .values()
65             .flat_map(|dep| dep.iter().filter_map(|d| d.strip_prefix("dep:")))
66             .collect::<BTreeSet<_>>();
67         self.features()
68             .keys()
69             .map(|f| FeatureRef::Feature(f.as_str()))
70             .chain(self.dependencies().iter().filter_map(|d| {
71                 if d.is_optional() && !explicit_deps.contains(d.name()) {
72                     Some(FeatureRef::OptionalDep(d))
73                 } else {
74                     None
75                 }
76             }))
77             .collect::<TypedFeatures>()
78     }
79 }
80 
81 #[cfg(test)]
82 mod tests {
83     use super::*;
84 
85     use itertools::assert_equal;
86 
87     #[test]
test_features()88     fn test_features() {
89         let hashbrown_0_12_3: Version =
90             serde_json::from_str(include_str!("testdata/hashbrown-0.12.3"))
91                 .expect("Failed to parse JSON testdata");
92         let features = hashbrown_0_12_3.features_and_optional_deps();
93         assert_equal(
94             features.iter().map(|f| f.name()),
95             [
96                 "ahash",
97                 "ahash-compile-time-rng",
98                 "alloc",
99                 "bumpalo",
100                 "compiler_builtins",
101                 "core",
102                 "default",
103                 "inline-more",
104                 "nightly",
105                 "raw",
106                 "rayon",
107                 "rustc-dep-of-std",
108                 "rustc-internal-api",
109                 "serde",
110             ],
111         );
112         assert_eq!(
113             features.get("ahash-compile-time-rng"),
114             Some(&FeatureRef::Feature("ahash-compile-time-rng"))
115         );
116         assert_eq!(
117             features.get("ahash"),
118             Some(&FeatureRef::OptionalDep(&hashbrown_0_12_3.dependencies()[0]))
119         );
120         assert_eq!(
121             features.get("alloc"),
122             Some(&FeatureRef::OptionalDep(&hashbrown_0_12_3.dependencies()[1]))
123         );
124         assert_eq!(
125             features.get("bumpalo"),
126             Some(&FeatureRef::OptionalDep(&hashbrown_0_12_3.dependencies()[2]))
127         );
128 
129         let hashbrown_0_14_5: Version =
130             serde_json::from_str(include_str!("testdata/hashbrown-0.14.5"))
131                 .expect("Failed to parse JSON testdata");
132         let features = hashbrown_0_14_5.features_and_optional_deps();
133         assert_equal(
134             features.iter().map(|f| f.name()),
135             [
136                 "ahash",
137                 "alloc",
138                 "allocator-api2",
139                 "compiler_builtins",
140                 "core",
141                 "default",
142                 "equivalent",
143                 "inline-more",
144                 "nightly",
145                 "raw",
146                 "rayon",
147                 "rkyv",
148                 "rustc-dep-of-std",
149                 "rustc-internal-api",
150                 "serde",
151             ],
152         );
153 
154         let winnow_0_5_37: Version = serde_json::from_str(include_str!("testdata/winnow-0.5.37"))
155             .expect("Failed to parse JSON testdata");
156         let features = winnow_0_5_37.features_and_optional_deps();
157         assert_equal(
158             features.iter().map(|f| f.name()),
159             ["alloc", "debug", "default", "simd", "std", "unstable-doc", "unstable-recover"],
160         );
161 
162         let cfg_if_1_0_0: Version = serde_json::from_str(include_str!("testdata/cfg-if-1.0.0"))
163             .expect("Failed to parse JSON testdata");
164         let features = cfg_if_1_0_0.features_and_optional_deps();
165         assert_equal(
166             features.iter().map(|f| f.name()),
167             ["compiler_builtins", "core", "rustc-dep-of-std"],
168         );
169     }
170 }
171