|
| 1 | +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 | +// for details. All rights reserved. Use of this source code is governed by a |
| 3 | +// BSD-style license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +import '../package_name.dart'; |
| 6 | +import 'term.dart'; |
| 7 | + |
| 8 | +/// A set of mutually-incompatible terms. |
| 9 | +/// |
| 10 | +/// See https://github.com/dart-lang/pub/tree/master/doc/solver.md#incompatibility. |
| 11 | +class Incompatibility { |
| 12 | + /// The mutually-incompatibile terms. |
| 13 | + final List<Term> terms; |
| 14 | + |
| 15 | + /// Creates an incompatibility with [terms]. |
| 16 | + /// |
| 17 | + /// This normalized [terms] so that each package has at most one term |
| 18 | + /// referring to it. |
| 19 | + factory Incompatibility(List<Term> terms) { |
| 20 | + if (terms.length == 1 || |
| 21 | + // Short-circuit in the common case of a two-term incompatibility with |
| 22 | + // two different packages (for example, a dependency). |
| 23 | + (terms.length == 2 && |
| 24 | + terms.first.package.name != terms.last.package.name)) { |
| 25 | + return new Incompatibility._(terms); |
| 26 | + } |
| 27 | + |
| 28 | + // Coalesce multiple terms about the same package if possible. |
| 29 | + var byName = <String, Map<PackageRef, Term>>{}; |
| 30 | + for (var term in terms) { |
| 31 | + var byRef = byName.putIfAbsent(term.package.name, () => {}); |
| 32 | + var ref = term.package.toRef(); |
| 33 | + if (byRef.containsKey(ref)) { |
| 34 | + byRef[ref] = byRef[ref].intersect(term); |
| 35 | + |
| 36 | + // If we have two terms that refer to the same package but have a null |
| 37 | + // intersection, they're mutually exclusive, making this incompatibility |
| 38 | + // irrelevant, since we already know that mutually exclusive version |
| 39 | + // ranges are incompatible. We should never derive an irrelevant |
| 40 | + // incompatibility. |
| 41 | + assert(byRef[ref] != null); |
| 42 | + } else { |
| 43 | + byRef[ref] = term; |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + return new Incompatibility._(byName.values.expand((byRef) { |
| 48 | + // If there are any positive terms for a given package, we can discard |
| 49 | + // any negative terms. |
| 50 | + var positiveTerms = |
| 51 | + byRef.values.where((term) => term.isPositive).toList(); |
| 52 | + if (positiveTerms.isNotEmpty) return positiveTerms; |
| 53 | + |
| 54 | + return byRef.values; |
| 55 | + }).toList()); |
| 56 | + } |
| 57 | + |
| 58 | + Incompatibility._(this.terms); |
| 59 | + |
| 60 | + String toString() { |
| 61 | + if (terms.length == 1) { |
| 62 | + var term = terms.single; |
| 63 | + return "${term.package.toTerseString()} is " |
| 64 | + "${term.isPositive ? 'forbidden' : 'required'}"; |
| 65 | + } |
| 66 | + |
| 67 | + if (terms.length == 2) { |
| 68 | + var term1 = terms.first; |
| 69 | + var term2 = terms.last; |
| 70 | + if (term1.isPositive != term2.isPositive) { |
| 71 | + var positive = (term1.isPositive ? term1 : term2).package; |
| 72 | + var negative = (term1.isPositive ? term2 : term1).package; |
| 73 | + return "if ${positive.toTerseString()} then ${negative.toTerseString()}"; |
| 74 | + } else if (term1.isPositive) { |
| 75 | + return "${term1.package.toTerseString()} is incompatible with " |
| 76 | + "${term2.package.toTerseString()}"; |
| 77 | + } else { |
| 78 | + return "either ${term1.package.toTerseString()} or " |
| 79 | + "${term2.package.toTerseString()}"; |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + var positive = <String>[]; |
| 84 | + var negative = <String>[]; |
| 85 | + for (var term in terms) { |
| 86 | + (term.isPositive ? positive : negative).add(term.package.toTerseString()); |
| 87 | + } |
| 88 | + |
| 89 | + if (positive.isNotEmpty && negative.isNotEmpty) { |
| 90 | + return "if ${positive.join(' and ')} then ${negative.join(' and ')}"; |
| 91 | + } else if (positive.isNotEmpty) { |
| 92 | + return "one of ${positive.join(' or ')} must be false"; |
| 93 | + } else { |
| 94 | + return "one of ${negative.join(' or ')} must be true"; |
| 95 | + } |
| 96 | + } |
| 97 | +} |
0 commit comments