OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, 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 library pub_semver.src.version_constraint; |
| 6 |
| 7 import 'patterns.dart'; |
| 8 import 'version.dart'; |
| 9 import 'version_range.dart'; |
| 10 |
| 11 /// A [VersionConstraint] is a predicate that can determine whether a given |
| 12 /// version is valid or not. |
| 13 /// |
| 14 /// For example, a ">= 2.0.0" constraint allows any version that is "2.0.0" or |
| 15 /// greater. Version objects themselves implement this to match a specific |
| 16 /// version. |
| 17 abstract class VersionConstraint { |
| 18 /// A [VersionConstraint] that allows all versions. |
| 19 static VersionConstraint any = new VersionRange(); |
| 20 |
| 21 /// A [VersionConstraint] that allows no versions -- the empty set. |
| 22 static VersionConstraint empty = const _EmptyVersion(); |
| 23 |
| 24 /// Parses a version constraint. |
| 25 /// |
| 26 /// This string is either "any" or a series of version parts. Each part can |
| 27 /// be one of: |
| 28 /// |
| 29 /// * A version string like `1.2.3`. In other words, anything that can be |
| 30 /// parsed by [Version.parse()]. |
| 31 /// * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a version |
| 32 /// string. |
| 33 /// |
| 34 /// Whitespace is ignored. |
| 35 /// |
| 36 /// Examples: |
| 37 /// |
| 38 /// any |
| 39 /// 1.2.3-alpha |
| 40 /// <=5.1.4 |
| 41 /// >2.0.4 <= 2.4.6 |
| 42 factory VersionConstraint.parse(String text) { |
| 43 // Handle the "any" constraint. |
| 44 if (text.trim() == "any") return new VersionRange(); |
| 45 |
| 46 var originalText = text; |
| 47 var constraints = <VersionConstraint>[]; |
| 48 |
| 49 skipWhitespace() { |
| 50 text = text.trim(); |
| 51 } |
| 52 |
| 53 // Try to parse and consume a version number. |
| 54 matchVersion() { |
| 55 var version = START_VERSION.firstMatch(text); |
| 56 if (version == null) return null; |
| 57 |
| 58 text = text.substring(version.end); |
| 59 return new Version.parse(version[0]); |
| 60 } |
| 61 |
| 62 // Try to parse and consume a comparison operator followed by a version. |
| 63 matchComparison() { |
| 64 var comparison = START_COMPARISON.firstMatch(text); |
| 65 if (comparison == null) return null; |
| 66 |
| 67 var op = comparison[0]; |
| 68 text = text.substring(comparison.end); |
| 69 skipWhitespace(); |
| 70 |
| 71 var version = matchVersion(); |
| 72 if (version == null) { |
| 73 throw new FormatException('Expected version number after "$op" in ' |
| 74 '"$originalText", got "$text".'); |
| 75 } |
| 76 |
| 77 switch (op) { |
| 78 case '<=': return new VersionRange(max: version, includeMax: true); |
| 79 case '<': return new VersionRange(max: version, includeMax: false); |
| 80 case '>=': return new VersionRange(min: version, includeMin: true); |
| 81 case '>': return new VersionRange(min: version, includeMin: false); |
| 82 } |
| 83 throw "Unreachable."; |
| 84 } |
| 85 |
| 86 while (true) { |
| 87 skipWhitespace(); |
| 88 if (text.isEmpty) break; |
| 89 |
| 90 var version = matchVersion(); |
| 91 if (version != null) { |
| 92 constraints.add(version); |
| 93 continue; |
| 94 } |
| 95 |
| 96 var comparison = matchComparison(); |
| 97 if (comparison != null) { |
| 98 constraints.add(comparison); |
| 99 continue; |
| 100 } |
| 101 |
| 102 // If we got here, we couldn't parse the remaining string. |
| 103 throw new FormatException('Could not parse version "$originalText". ' |
| 104 'Unknown text at "$text".'); |
| 105 } |
| 106 |
| 107 if (constraints.isEmpty) { |
| 108 throw new FormatException('Cannot parse an empty string.'); |
| 109 } |
| 110 |
| 111 return new VersionConstraint.intersection(constraints); |
| 112 } |
| 113 |
| 114 /// Creates a new version constraint that is the intersection of |
| 115 /// [constraints]. |
| 116 /// |
| 117 /// It only allows versions that all of those constraints allow. If |
| 118 /// constraints is empty, then it returns a VersionConstraint that allows |
| 119 /// all versions. |
| 120 factory VersionConstraint.intersection( |
| 121 Iterable<VersionConstraint> constraints) { |
| 122 var constraint = new VersionRange(); |
| 123 for (var other in constraints) { |
| 124 constraint = constraint.intersect(other); |
| 125 } |
| 126 return constraint; |
| 127 } |
| 128 |
| 129 /// Returns `true` if this constraint allows no versions. |
| 130 bool get isEmpty; |
| 131 |
| 132 /// Returns `true` if this constraint allows all versions. |
| 133 bool get isAny; |
| 134 |
| 135 /// Returns `true` if this constraint allows [version]. |
| 136 bool allows(Version version); |
| 137 |
| 138 /// Creates a new [VersionConstraint] that only allows [Version]s allowed by |
| 139 /// both this and [other]. |
| 140 VersionConstraint intersect(VersionConstraint other); |
| 141 } |
| 142 |
| 143 class _EmptyVersion implements VersionConstraint { |
| 144 const _EmptyVersion(); |
| 145 |
| 146 bool get isEmpty => true; |
| 147 bool get isAny => false; |
| 148 bool allows(Version other) => false; |
| 149 VersionConstraint intersect(VersionConstraint other) => this; |
| 150 String toString() => '<empty>'; |
| 151 } |
OLD | NEW |