@@ -251,6 +251,24 @@ impl MarkerOperator {
251
251
Self :: NotIn => None ,
252
252
}
253
253
}
254
+
255
+ /// Negates this marker operator.
256
+ ///
257
+ /// If a negation doesn't exist, which is only the case for ~=, then this
258
+ /// returns `None`.
259
+ fn negate ( self ) -> Option < MarkerOperator > {
260
+ Some ( match self {
261
+ Self :: Equal => Self :: NotEqual ,
262
+ Self :: NotEqual => Self :: Equal ,
263
+ Self :: TildeEqual => return None ,
264
+ Self :: LessThan => Self :: GreaterEqual ,
265
+ Self :: LessEqual => Self :: GreaterThan ,
266
+ Self :: GreaterThan => Self :: LessEqual ,
267
+ Self :: GreaterEqual => Self :: LessThan ,
268
+ Self :: In => Self :: NotIn ,
269
+ Self :: NotIn => Self :: In ,
270
+ } )
271
+ }
254
272
}
255
273
256
274
impl FromStr for MarkerOperator {
@@ -968,6 +986,13 @@ impl ExtraOperator {
968
986
_ => None ,
969
987
}
970
988
}
989
+
990
+ fn negate ( & self ) -> ExtraOperator {
991
+ match * self {
992
+ ExtraOperator :: Equal => ExtraOperator :: NotEqual ,
993
+ ExtraOperator :: NotEqual => ExtraOperator :: Equal ,
994
+ }
995
+ }
971
996
}
972
997
973
998
impl Display for ExtraOperator {
@@ -1285,6 +1310,118 @@ impl MarkerExpression {
1285
1310
}
1286
1311
}
1287
1312
1313
+ /// Negates this marker expression.
1314
+ ///
1315
+ /// In most cases, this returns a `MarkerTree::Expression`, but in some
1316
+ /// cases it can be more complicated than that. For example, the negation
1317
+ /// of a compatible version constraint is a disjunction.
1318
+ ///
1319
+ /// Additionally, in some cases, the negation reflects the "spirit" of what
1320
+ /// the marker expression is. For example, the negation of an "arbitrary"
1321
+ /// expression will still result in an expression that is always false.
1322
+ fn negate ( & self ) -> MarkerTree {
1323
+ match * self {
1324
+ MarkerExpression :: Version {
1325
+ ref key,
1326
+ ref specifier,
1327
+ } => {
1328
+ let ( op, version) = ( specifier. operator ( ) , specifier. version ( ) . clone ( ) ) ;
1329
+ match op. negate ( ) {
1330
+ None => negate_compatible_version ( key. clone ( ) , version) ,
1331
+ Some ( op) => {
1332
+ // OK because this can only fail with either local versions,
1333
+ // which we avoid by construction, or if the op is ~=, which
1334
+ // is never the result of negating an op.
1335
+ let specifier =
1336
+ VersionSpecifier :: from_version ( op, version. without_local ( ) ) . unwrap ( ) ;
1337
+ let expr = MarkerExpression :: Version {
1338
+ key : key. clone ( ) ,
1339
+ specifier,
1340
+ } ;
1341
+ MarkerTree :: Expression ( expr)
1342
+ }
1343
+ }
1344
+ }
1345
+ MarkerExpression :: VersionInverted {
1346
+ ref version,
1347
+ ref operator,
1348
+ ref key,
1349
+ } => {
1350
+ let version = version. clone ( ) ;
1351
+ match operator. negate ( ) {
1352
+ None => negate_compatible_version ( key. clone ( ) , version) ,
1353
+ Some ( op) => {
1354
+ let expr = MarkerExpression :: VersionInverted {
1355
+ version : version. without_local ( ) ,
1356
+ operator : op,
1357
+ key : key. clone ( ) ,
1358
+ } ;
1359
+ MarkerTree :: Expression ( expr)
1360
+ }
1361
+ }
1362
+ }
1363
+ MarkerExpression :: String {
1364
+ ref key,
1365
+ ref operator,
1366
+ ref value,
1367
+ } => {
1368
+ let expr = MarkerExpression :: String {
1369
+ key : key. clone ( ) ,
1370
+ // negating ~= doesn't make sense in this context, but
1371
+ // I believe it is technically allowed, so we just leave
1372
+ // it as-is.
1373
+ operator : operator. negate ( ) . unwrap_or ( MarkerOperator :: TildeEqual ) ,
1374
+ value : value. clone ( ) ,
1375
+ } ;
1376
+ MarkerTree :: Expression ( expr)
1377
+ }
1378
+ MarkerExpression :: StringInverted {
1379
+ ref value,
1380
+ ref operator,
1381
+ ref key,
1382
+ } => {
1383
+ let expr = MarkerExpression :: StringInverted {
1384
+ value : value. clone ( ) ,
1385
+ // negating ~= doesn't make sense in this context, but
1386
+ // I believe it is technically allowed, so we just leave
1387
+ // it as-is.
1388
+ operator : operator. negate ( ) . unwrap_or ( MarkerOperator :: TildeEqual ) ,
1389
+ key : key. clone ( ) ,
1390
+ } ;
1391
+ MarkerTree :: Expression ( expr)
1392
+ }
1393
+ MarkerExpression :: Extra {
1394
+ ref operator,
1395
+ ref name,
1396
+ } => {
1397
+ let expr = MarkerExpression :: Extra {
1398
+ operator : operator. negate ( ) ,
1399
+ name : name. clone ( ) ,
1400
+ } ;
1401
+ MarkerTree :: Expression ( expr)
1402
+ }
1403
+ // "arbitrary" expressions always return false, and while the
1404
+ // negation logically implies they should always return true, we do
1405
+ // not do that here because it violates the spirit of a meaningly
1406
+ // or "arbitrary" marker. We flip the operator but do nothing else.
1407
+ MarkerExpression :: Arbitrary {
1408
+ ref l_value,
1409
+ ref operator,
1410
+ ref r_value,
1411
+ } => {
1412
+ let expr = MarkerExpression :: Arbitrary {
1413
+ l_value : l_value. clone ( ) ,
1414
+ // negating ~= doesn't make sense in this context, but
1415
+ // I believe it is technically allowed, so we just leave
1416
+ // it as-is.
1417
+ operator : operator. negate ( ) . unwrap_or ( MarkerOperator :: TildeEqual ) ,
1418
+ r_value : r_value. clone ( ) ,
1419
+ } ;
1420
+ MarkerTree :: Expression ( expr)
1421
+ }
1422
+ }
1423
+ }
1424
+
1288
1425
/// Evaluate a <`marker_value`> <`marker_op`> <`marker_value`> expression
1289
1426
///
1290
1427
/// When `env` is `None`, all expressions that reference the environment
@@ -1858,6 +1995,28 @@ impl MarkerTree {
1858
1995
}
1859
1996
}
1860
1997
1998
+ /// Returns a new marker tree that is the negation of this one.
1999
+ #[ must_use]
2000
+ pub fn negate ( & self ) -> MarkerTree {
2001
+ match * self {
2002
+ MarkerTree :: Expression ( ref expr) => expr. negate ( ) ,
2003
+ MarkerTree :: And ( ref trees) => {
2004
+ let mut negated = MarkerTree :: Or ( Vec :: with_capacity ( trees. len ( ) ) ) ;
2005
+ for tree in trees {
2006
+ negated. or ( tree. negate ( ) ) ;
2007
+ }
2008
+ negated
2009
+ }
2010
+ MarkerTree :: Or ( ref trees) => {
2011
+ let mut negated = MarkerTree :: And ( Vec :: with_capacity ( trees. len ( ) ) ) ;
2012
+ for tree in trees {
2013
+ negated. and ( tree. negate ( ) ) ;
2014
+ }
2015
+ negated
2016
+ }
2017
+ }
2018
+ }
2019
+
1861
2020
/// Combine this marker tree with the one given via a conjunction.
1862
2021
///
1863
2022
/// This does some shallow flattening. That is, if `self` is a conjunction
@@ -1954,6 +2113,43 @@ impl Display for MarkerTree {
1954
2113
}
1955
2114
}
1956
2115
2116
+ /// Negates a compatible version marker expression, from its component parts.
2117
+ ///
2118
+ /// Here, we consider `key ~= V.N` to be equivalent to
2119
+ /// `key >= V.N and key == V.*`. So the negation returned is
2120
+ /// `key < V.N or key != V.*`.
2121
+ fn negate_compatible_version ( key : MarkerValueVersion , version : Version ) -> MarkerTree {
2122
+ assert ! (
2123
+ version. release( ) . len( ) > 1 ,
2124
+ "~= requires more than 1 release version number"
2125
+ ) ;
2126
+ // I believe we're already guaranteed that this is true,
2127
+ // because we're only here if this version was combined
2128
+ // with ~=, which cannot be used with local versions anyway.
2129
+ // But this ensures correctness and should be pretty cheap.
2130
+ let version = version. without_local ( ) ;
2131
+ let pattern = VersionPattern :: wildcard ( Version :: new (
2132
+ & version. release ( ) [ ..version. release ( ) . len ( ) - 1 ] ,
2133
+ ) ) ;
2134
+ // OK because this can only fail for local versions or when using
2135
+ // ~=, but neither is the case here.
2136
+ let disjunct1 = VersionSpecifier :: from_version ( pep440_rs:: Operator :: LessThan , version) . unwrap ( ) ;
2137
+ // And this is OK because it only fails if the above would fail
2138
+ // (which we know it doesn't) or if the operator is not compatible
2139
+ // with wildcards, but != is.
2140
+ let disjunct2 = VersionSpecifier :: from_pattern ( pep440_rs:: Operator :: NotEqual , pattern) . unwrap ( ) ;
2141
+ MarkerTree :: Or ( vec ! [
2142
+ MarkerTree :: Expression ( MarkerExpression :: Version {
2143
+ key: key. clone( ) ,
2144
+ specifier: disjunct1,
2145
+ } ) ,
2146
+ MarkerTree :: Expression ( MarkerExpression :: Version {
2147
+ key,
2148
+ specifier: disjunct2,
2149
+ } ) ,
2150
+ ] )
2151
+ }
2152
+
1957
2153
/// ```text
1958
2154
/// version_cmp = wsp* <'<=' | '<' | '!=' | '==' | '>=' | '>' | '~=' | '==='>
1959
2155
/// marker_op = version_cmp | (wsp* 'in') | (wsp* 'not' wsp+ 'in')
@@ -2265,6 +2461,62 @@ mod test {
2265
2461
}
2266
2462
}
2267
2463
2464
+ #[ test]
2465
+ fn test_marker_negation ( ) {
2466
+ let neg = |marker_string : & str | -> String {
2467
+ let tree: MarkerTree = marker_string. parse ( ) . unwrap ( ) ;
2468
+ tree. negate ( ) . to_string ( )
2469
+ } ;
2470
+
2471
+ assert_eq ! ( neg( "python_version > '3.6'" ) , "python_version <= '3.6'" ) ;
2472
+ assert_eq ! ( neg( "'3.6' < python_version" ) , "'3.6' >= python_version" ) ;
2473
+
2474
+ assert_eq ! (
2475
+ neg( "python_version == '3.6.*'" ) ,
2476
+ "python_version != '3.6.*'"
2477
+ ) ;
2478
+ assert_eq ! (
2479
+ neg( "python_version != '3.6.*'" ) ,
2480
+ "python_version == '3.6.*'"
2481
+ ) ;
2482
+
2483
+ assert_eq ! (
2484
+ neg( "python_version ~= '3.6'" ) ,
2485
+ "python_version < '3.6' or python_version != '3.*'"
2486
+ ) ;
2487
+ assert_eq ! (
2488
+ neg( "'3.6' ~= python_version" ) ,
2489
+ "python_version < '3.6' or python_version != '3.*'"
2490
+ ) ;
2491
+ assert_eq ! (
2492
+ neg( "python_version ~= '3.6.2'" ) ,
2493
+ "python_version < '3.6.2' or python_version != '3.6.*'"
2494
+ ) ;
2495
+
2496
+ assert_eq ! ( neg( "sys_platform == 'linux'" ) , "sys_platform != 'linux'" ) ;
2497
+ assert_eq ! ( neg( "'linux' == sys_platform" ) , "'linux' != sys_platform" ) ;
2498
+
2499
+ // ~= is nonsense on string markers. Evaluation always returns false
2500
+ // in this case, so technically negation would be an expression that
2501
+ // always returns true. But, as we do with "arbitrary" markers, we
2502
+ // don't let the negation of nonsense become sensible.
2503
+ assert_eq ! ( neg( "sys_platform ~= 'linux'" ) , "sys_platform ~= 'linux'" ) ;
2504
+
2505
+ // As above, arbitrary exprs remain arbitrary.
2506
+ assert_eq ! ( neg( "'foo' == 'bar'" ) , "'foo' != 'bar'" ) ;
2507
+
2508
+ // Conjunctions
2509
+ assert_eq ! (
2510
+ neg( "os_name == 'bar' and os_name == 'foo'" ) ,
2511
+ "os_name != 'bar' or os_name != 'foo'"
2512
+ ) ;
2513
+ // Disjunctions
2514
+ assert_eq ! (
2515
+ neg( "os_name == 'bar' or os_name == 'foo'" ) ,
2516
+ "os_name != 'bar' and os_name != 'foo'"
2517
+ ) ;
2518
+ }
2519
+
2268
2520
#[ test]
2269
2521
fn test_marker_evaluation ( ) {
2270
2522
let env27 = MarkerEnvironment :: try_from ( MarkerEnvironmentBuilder {
0 commit comments