1
- use std:: collections:: BTreeMap ;
1
+ use std:: collections:: { BTreeMap , HashMap } ;
2
2
use std:: mem;
3
3
use std:: rc:: Rc ;
4
4
@@ -26,6 +26,7 @@ struct Inner {
26
26
features : FeatureMap ,
27
27
checksum : Option < String > ,
28
28
links : Option < InternedString > ,
29
+ namespaced_features : bool ,
29
30
}
30
31
31
32
impl Summary {
@@ -34,9 +35,10 @@ impl Summary {
34
35
dependencies : Vec < Dependency > ,
35
36
features : BTreeMap < String , Vec < String > > ,
36
37
links : Option < String > ,
38
+ namespaced_features : bool ,
37
39
) -> CargoResult < Summary > {
38
40
for dep in dependencies. iter ( ) {
39
- if features. get ( & * dep. name ( ) ) . is_some ( ) {
41
+ if !namespaced_features && features. get ( & * dep. name ( ) ) . is_some ( ) {
40
42
bail ! (
41
43
"Features and dependencies cannot have the \
42
44
same name: `{}`",
@@ -50,14 +52,15 @@ impl Summary {
50
52
)
51
53
}
52
54
}
53
- let feature_map = build_feature_map ( features, & dependencies) ?;
55
+ let feature_map = build_feature_map ( features, & dependencies, namespaced_features ) ?;
54
56
Ok ( Summary {
55
57
inner : Rc :: new ( Inner {
56
58
package_id : pkg_id,
57
59
dependencies,
58
60
features : feature_map,
59
61
checksum : None ,
60
62
links : links. map ( |l| InternedString :: new ( & l) ) ,
63
+ namespaced_features,
61
64
} ) ,
62
65
} )
63
66
}
@@ -86,6 +89,9 @@ impl Summary {
86
89
pub fn links ( & self ) -> Option < InternedString > {
87
90
self . inner . links
88
91
}
92
+ pub fn namespaced_features ( & self ) -> bool {
93
+ self . inner . namespaced_features
94
+ }
89
95
90
96
pub fn override_id ( mut self , id : PackageId ) -> Summary {
91
97
Rc :: make_mut ( & mut self . inner ) . package_id = id;
@@ -131,55 +137,179 @@ impl PartialEq for Summary {
131
137
fn build_feature_map (
132
138
features : BTreeMap < String , Vec < String > > ,
133
139
dependencies : & [ Dependency ] ,
140
+ namespaced : bool ,
134
141
) -> CargoResult < FeatureMap > {
135
142
use self :: FeatureValue :: * ;
143
+ let dep_map: HashMap < _ , _ > = dependencies
144
+ . iter ( )
145
+ . map ( |d| ( d. name ( ) . as_str ( ) , d) )
146
+ . collect ( ) ;
147
+
136
148
let mut map = BTreeMap :: new ( ) ;
137
149
for ( feature, list) in features. iter ( ) {
150
+ // If namespaced features is active and the key is the same as that of an
151
+ // optional dependency, that dependency must be included in the values.
152
+ // Thus, if a `feature` is found that has the same name as a dependency, we
153
+ // (a) bail out if the dependency is non-optional, and (b) we track if the
154
+ // feature requirements include the dependency `crate:feature` in the list.
155
+ // This is done with the `dependency_found` variable, which can only be
156
+ // false if features are namespaced and the current feature key is the same
157
+ // as the name of an optional dependency. If so, it gets set to true during
158
+ // iteration over the list if the dependency is found in the list.
159
+ let mut dependency_found = if namespaced {
160
+ match dep_map. get ( feature. as_str ( ) ) {
161
+ Some ( ref dep_data) => {
162
+ if !dep_data. is_optional ( ) {
163
+ bail ! (
164
+ "Feature `{}` includes the dependency of the same name, but this is \
165
+ left implicit in the features included by this feature.\n \
166
+ Additionally, the dependency must be marked as optional to be \
167
+ included in the feature definition.\n \
168
+ Consider adding `crate:{}` to this feature's requirements \
169
+ and marking the dependency as `optional = true`",
170
+ feature,
171
+ feature
172
+ )
173
+ } else {
174
+ false
175
+ }
176
+ }
177
+ None => true ,
178
+ }
179
+ } else {
180
+ true
181
+ } ;
182
+
138
183
let mut values = vec ! [ ] ;
139
184
for dep in list {
140
- let val = FeatureValue :: build ( InternedString :: new ( dep ) , |fs| features . contains_key ( fs ) ) ;
141
- if let & Feature ( _ ) = & val {
142
- values . push ( val ) ;
143
- continue ;
144
- }
185
+ let val = FeatureValue :: build (
186
+ InternedString :: new ( dep ) ,
187
+ |fs| features . contains_key ( fs ) ,
188
+ namespaced ,
189
+ ) ;
145
190
146
191
// Find data for the referenced dependency...
147
192
let dep_data = {
148
- let dep_name = match val {
149
- Feature ( _) => "" ,
150
- Crate ( ref dep_name ) | CrateFeature ( ref dep_name, _ ) => dep_name ,
151
- } ;
152
- dependencies . iter ( ) . find ( |d| * d . name ( ) == * dep_name )
193
+ match val {
194
+ Feature ( ref dep_name ) | Crate ( ref dep_name ) | CrateFeature ( ref dep_name , _) => {
195
+ dep_map . get ( dep_name. as_str ( ) )
196
+ }
197
+ }
153
198
} ;
199
+ let is_optional_dep = dep_data. map_or ( false , |d| d. is_optional ( ) ) ;
200
+ if let FeatureValue :: Crate ( ref dep_name) = val {
201
+ // If we have a dependency value, check if this is the dependency named
202
+ // the same as the feature that we were looking for.
203
+ if !dependency_found && feature == dep_name. as_str ( ) {
204
+ dependency_found = true ;
205
+ }
206
+ }
154
207
155
- match ( & val, dep_data) {
156
- ( & Crate ( ref dep) , Some ( d) ) => {
157
- if !d. is_optional ( ) {
158
- bail ! (
159
- "Feature `{}` depends on `{}` which is not an \
160
- optional dependency.\n Consider adding \
161
- `optional = true` to the dependency",
162
- feature,
163
- dep
164
- )
208
+ match ( & val, dep_data. is_some ( ) , is_optional_dep) {
209
+ // The value is a feature. If features are namespaced, this just means
210
+ // it's not prefixed with `crate:`, so we have to check whether the
211
+ // feature actually exist. If the feature is not defined *and* an optional
212
+ // dependency of the same name exists, the feature is defined implicitly
213
+ // here by adding it to the feature map, pointing to the dependency.
214
+ // If features are not namespaced, it's been validated as a feature already
215
+ // while instantiating the `FeatureValue` in `FeatureValue::build()`, so
216
+ // we don't have to do so here.
217
+ ( & Feature ( feat) , _, true ) => {
218
+ if namespaced && !features. contains_key ( & * feat) {
219
+ map. insert ( feat. to_string ( ) , vec ! [ FeatureValue :: Crate ( feat) ] ) ;
220
+ }
221
+ }
222
+ // If features are namespaced and the value is not defined as a feature
223
+ // and there is no optional dependency of the same name, error out.
224
+ // If features are not namespaced, there must be an existing feature
225
+ // here (checked by `FeatureValue::build()`), so it will always be defined.
226
+ ( & Feature ( feat) , dep_exists, false ) => {
227
+ if namespaced && !features. contains_key ( & * feat) {
228
+ if dep_exists {
229
+ bail ! (
230
+ "Feature `{}` includes `{}` which is not defined as a feature.\n \
231
+ A non-optional dependency of the same name is defined; consider \
232
+ adding `optional = true` to its definition",
233
+ feature,
234
+ feat
235
+ )
236
+ } else {
237
+ bail ! (
238
+ "Feature `{}` includes `{}` which is not defined as a feature" ,
239
+ feature,
240
+ feat
241
+ )
242
+ }
165
243
}
166
244
}
167
- ( & CrateFeature ( ref dep_name, _) , None ) => bail ! (
245
+ // The value is a dependency. If features are namespaced, it is explicitly
246
+ // tagged as such (`crate:value`). If features are not namespaced, any value
247
+ // not recognized as a feature is pegged as a `Crate`. Here we handle the case
248
+ // where the dependency exists but is non-optional. It branches on namespaced
249
+ // just to provide the correct string for the crate dependency in the error.
250
+ ( & Crate ( ref dep) , true , false ) => if namespaced {
251
+ bail ! (
252
+ "Feature `{}` includes `crate:{}` which is not an \
253
+ optional dependency.\n Consider adding \
254
+ `optional = true` to the dependency",
255
+ feature,
256
+ dep
257
+ )
258
+ } else {
259
+ bail ! (
260
+ "Feature `{}` depends on `{}` which is not an \
261
+ optional dependency.\n Consider adding \
262
+ `optional = true` to the dependency",
263
+ feature,
264
+ dep
265
+ )
266
+ } ,
267
+ // If namespaced, the value was tagged as a dependency; if not namespaced,
268
+ // this could be anything not defined as a feature. This handles the case
269
+ // where no such dependency is actually defined; again, the branch on
270
+ // namespaced here is just to provide the correct string in the error.
271
+ ( & Crate ( ref dep) , false , _) => if namespaced {
272
+ bail ! (
273
+ "Feature `{}` includes `crate:{}` which is not a known \
274
+ dependency",
275
+ feature,
276
+ dep
277
+ )
278
+ } else {
279
+ bail ! (
280
+ "Feature `{}` includes `{}` which is neither a dependency nor \
281
+ another feature",
282
+ feature,
283
+ dep
284
+ )
285
+ } ,
286
+ ( & Crate ( _) , true , true ) => { }
287
+ // If the value is a feature for one of the dependencies, bail out if no such
288
+ // dependency is actually defined in the manifest.
289
+ ( & CrateFeature ( ref dep, _) , false , _) => bail ! (
168
290
"Feature `{}` requires a feature of `{}` which is not a \
169
291
dependency",
170
292
feature,
171
- dep_name
172
- ) ,
173
- ( & Crate ( ref dep) , None ) => bail ! (
174
- "Feature `{}` includes `{}` which is neither \
175
- a dependency nor another feature",
176
- feature,
177
293
dep
178
294
) ,
179
- ( & CrateFeature ( _, _) , Some ( _ ) ) | ( & Feature ( _ ) , _) => { }
295
+ ( & CrateFeature ( _, _) , true , _) => { }
180
296
}
181
297
values. push ( val) ;
182
298
}
299
+
300
+ if !dependency_found {
301
+ // If we have not found the dependency of the same-named feature, we should
302
+ // bail here.
303
+ bail ! (
304
+ "Feature `{}` includes the optional dependency of the \
305
+ same name, but this is left implicit in the features \
306
+ included by this feature.\n Consider adding \
307
+ `crate:{}` to this feature's requirements.",
308
+ feature,
309
+ feature
310
+ )
311
+ }
312
+
183
313
map. insert ( feature. clone ( ) , values) ;
184
314
}
185
315
Ok ( map)
@@ -200,30 +330,42 @@ pub enum FeatureValue {
200
330
}
201
331
202
332
impl FeatureValue {
203
- fn build < T > ( feature : InternedString , is_feature : T ) -> FeatureValue
333
+ fn build < T > ( feature : InternedString , is_feature : T , namespaced : bool ) -> FeatureValue
204
334
where
205
335
T : Fn ( & str ) -> bool ,
206
336
{
207
- match feature. find ( '/' ) {
208
- Some ( pos) => {
337
+ match ( feature. find ( '/' ) , namespaced ) {
338
+ ( Some ( pos) , _ ) => {
209
339
let ( dep, dep_feat) = feature. split_at ( pos) ;
210
340
let dep_feat = & dep_feat[ 1 ..] ;
211
341
FeatureValue :: CrateFeature ( InternedString :: new ( dep) , InternedString :: new ( dep_feat) )
212
342
}
213
- None if is_feature ( & feature) => FeatureValue :: Feature ( feature) ,
214
- None => FeatureValue :: Crate ( feature) ,
343
+ ( None , true ) if feature. starts_with ( "crate:" ) => {
344
+ FeatureValue :: Crate ( InternedString :: new ( & feature[ 6 ..] ) )
345
+ }
346
+ ( None , true ) => FeatureValue :: Feature ( feature) ,
347
+ ( None , false ) if is_feature ( & feature) => FeatureValue :: Feature ( feature) ,
348
+ ( None , false ) => FeatureValue :: Crate ( feature) ,
215
349
}
216
350
}
217
351
218
352
pub fn new ( feature : InternedString , s : & Summary ) -> FeatureValue {
219
- Self :: build ( feature, |fs| s. features ( ) . contains_key ( fs) )
353
+ Self :: build (
354
+ feature,
355
+ |fs| s. features ( ) . contains_key ( fs) ,
356
+ s. namespaced_features ( ) ,
357
+ )
220
358
}
221
359
222
- pub fn to_string ( & self ) -> String {
360
+ pub fn to_string ( & self , s : & Summary ) -> String {
223
361
use self :: FeatureValue :: * ;
224
362
match * self {
225
363
Feature ( ref f) => f. to_string ( ) ,
226
- Crate ( ref c) => c. to_string ( ) ,
364
+ Crate ( ref c) => if s. namespaced_features ( ) {
365
+ format ! ( "crate:{}" , & c)
366
+ } else {
367
+ c. to_string ( )
368
+ } ,
227
369
CrateFeature ( ref c, ref f) => [ c. as_ref ( ) , f. as_ref ( ) ] . join ( "/" ) ,
228
370
}
229
371
}
0 commit comments