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 import 'version_union.dart'; |
| 11 |
| 12 /// A [VersionConstraint] is a predicate that can determine whether a given |
| 13 /// version is valid or not. |
| 14 /// |
| 15 /// For example, a ">= 2.0.0" constraint allows any version that is "2.0.0" or |
| 16 /// greater. Version objects themselves implement this to match a specific |
| 17 /// version. |
| 18 abstract class VersionConstraint { |
| 19 /// A [VersionConstraint] that allows all versions. |
| 20 static VersionConstraint any = new VersionRange(); |
| 21 |
| 22 /// A [VersionConstraint] that allows no versions -- the empty set. |
| 23 static VersionConstraint empty = const _EmptyVersion(); |
| 24 |
| 25 /// Parses a version constraint. |
| 26 /// |
| 27 /// This string is one of: |
| 28 /// |
| 29 /// * "any". [any] version. |
| 30 /// * "^" followed by a version string. Versions compatible with |
| 31 /// ([VersionConstraint.compatibleWith]) the version. |
| 32 /// * a series of version parts. Each part can be one of: |
| 33 /// * A version string like `1.2.3`. In other words, anything that can be |
| 34 /// parsed by [Version.parse()]. |
| 35 /// * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a |
| 36 /// version string. |
| 37 /// |
| 38 /// Whitespace is ignored. |
| 39 /// |
| 40 /// Examples: |
| 41 /// |
| 42 /// any |
| 43 /// ^0.7.2 |
| 44 /// ^1.0.0-alpha |
| 45 /// 1.2.3-alpha |
| 46 /// <=5.1.4 |
| 47 /// >2.0.4 <= 2.4.6 |
| 48 factory VersionConstraint.parse(String text) { |
| 49 var originalText = text; |
| 50 |
| 51 skipWhitespace() { |
| 52 text = text.trim(); |
| 53 } |
| 54 |
| 55 skipWhitespace(); |
| 56 |
| 57 // Handle the "any" constraint. |
| 58 if (text == "any") return any; |
| 59 |
| 60 // Try to parse and consume a version number. |
| 61 matchVersion() { |
| 62 var version = START_VERSION.firstMatch(text); |
| 63 if (version == null) return null; |
| 64 |
| 65 text = text.substring(version.end); |
| 66 return new Version.parse(version[0]); |
| 67 } |
| 68 |
| 69 // Try to parse and consume a comparison operator followed by a version. |
| 70 matchComparison() { |
| 71 var comparison = START_COMPARISON.firstMatch(text); |
| 72 if (comparison == null) return null; |
| 73 |
| 74 var op = comparison[0]; |
| 75 text = text.substring(comparison.end); |
| 76 skipWhitespace(); |
| 77 |
| 78 var version = matchVersion(); |
| 79 if (version == null) { |
| 80 throw new FormatException('Expected version number after "$op" in ' |
| 81 '"$originalText", got "$text".'); |
| 82 } |
| 83 |
| 84 switch (op) { |
| 85 case '<=': return new VersionRange(max: version, includeMax: true); |
| 86 case '<': return new VersionRange(max: version, includeMax: false); |
| 87 case '>=': return new VersionRange(min: version, includeMin: true); |
| 88 case '>': return new VersionRange(min: version, includeMin: false); |
| 89 } |
| 90 throw "Unreachable."; |
| 91 } |
| 92 |
| 93 // Try to parse the "^" operator followed by a version. |
| 94 matchCompatibleWith() { |
| 95 if (!text.startsWith(COMPATIBLE_WITH)) return null; |
| 96 |
| 97 text = text.substring(COMPATIBLE_WITH.length); |
| 98 skipWhitespace(); |
| 99 |
| 100 var version = matchVersion(); |
| 101 if (version == null) { |
| 102 throw new FormatException('Expected version number after ' |
| 103 '"$COMPATIBLE_WITH" in "$originalText", got "$text".'); |
| 104 } |
| 105 |
| 106 if (text.isNotEmpty) { |
| 107 throw new FormatException('Cannot include other constraints with ' |
| 108 '"$COMPATIBLE_WITH" constraint in "$originalText".'); |
| 109 } |
| 110 |
| 111 return new VersionConstraint.compatibleWith(version); |
| 112 } |
| 113 |
| 114 var compatibleWith = matchCompatibleWith(); |
| 115 if (compatibleWith != null) return compatibleWith; |
| 116 |
| 117 var constraints = []; |
| 118 |
| 119 while (true) { |
| 120 skipWhitespace(); |
| 121 |
| 122 if (text.isEmpty) break; |
| 123 |
| 124 var version = matchVersion(); |
| 125 if (version != null) { |
| 126 constraints.add(version); |
| 127 continue; |
| 128 } |
| 129 |
| 130 var comparison = matchComparison(); |
| 131 if (comparison != null) { |
| 132 constraints.add(comparison); |
| 133 continue; |
| 134 } |
| 135 |
| 136 // If we got here, we couldn't parse the remaining string. |
| 137 throw new FormatException('Could not parse version "$originalText". ' |
| 138 'Unknown text at "$text".'); |
| 139 } |
| 140 |
| 141 if (constraints.isEmpty) { |
| 142 throw new FormatException('Cannot parse an empty string.'); |
| 143 } |
| 144 |
| 145 return new VersionConstraint.intersection(constraints); |
| 146 } |
| 147 |
| 148 /// Creates a version constraint which allows all versions that are |
| 149 /// backward compatible with [version]. |
| 150 /// |
| 151 /// Versions are considered backward compatible with [version] if they |
| 152 /// are greater than or equal to [version], but less than the next breaking |
| 153 /// version ([Version.nextBreaking]) of [version]. |
| 154 factory VersionConstraint.compatibleWith(Version version) => |
| 155 new _CompatibleWithVersionRange(version); |
| 156 |
| 157 /// Creates a new version constraint that is the intersection of |
| 158 /// [constraints]. |
| 159 /// |
| 160 /// It only allows versions that all of those constraints allow. If |
| 161 /// constraints is empty, then it returns a VersionConstraint that allows |
| 162 /// all versions. |
| 163 factory VersionConstraint.intersection( |
| 164 Iterable<VersionConstraint> constraints) { |
| 165 var constraint = new VersionRange(); |
| 166 for (var other in constraints) { |
| 167 constraint = constraint.intersect(other); |
| 168 } |
| 169 return constraint; |
| 170 } |
| 171 |
| 172 /// Creates a new version constraint that is the union of [constraints]. |
| 173 /// |
| 174 /// It allows any versions that any of those constraints allows. If |
| 175 /// [constraints] is empty, this returns a constraint that allows no versions. |
| 176 factory VersionConstraint.unionOf( |
| 177 Iterable<VersionConstraint> constraints) => |
| 178 VersionUnion.create(constraints); |
| 179 |
| 180 /// Returns `true` if this constraint allows no versions. |
| 181 bool get isEmpty; |
| 182 |
| 183 /// Returns `true` if this constraint allows all versions. |
| 184 bool get isAny; |
| 185 |
| 186 /// Returns `true` if this constraint allows [version]. |
| 187 bool allows(Version version); |
| 188 |
| 189 /// Returns `true` if this constraint allows all the versions that [other] |
| 190 /// allows. |
| 191 bool allowsAll(VersionConstraint other); |
| 192 |
| 193 /// Returns `true` if this constraint allows any of the versions that [other] |
| 194 /// allows. |
| 195 bool allowsAny(VersionConstraint other); |
| 196 |
| 197 /// Creates a new [VersionConstraint] that only allows [Version]s allowed by |
| 198 /// both this and [other]. |
| 199 VersionConstraint intersect(VersionConstraint other); |
| 200 |
| 201 /// Creates a new [VersionConstraint] that allows [Versions]s allowed by |
| 202 /// either this or [other]. |
| 203 VersionConstraint union(VersionConstraint other); |
| 204 } |
| 205 |
| 206 class _EmptyVersion implements VersionConstraint { |
| 207 const _EmptyVersion(); |
| 208 |
| 209 bool get isEmpty => true; |
| 210 bool get isAny => false; |
| 211 bool allows(Version other) => false; |
| 212 bool allowsAll(Version other) => other.isEmpty; |
| 213 bool allowsAny(Version other) => false; |
| 214 VersionConstraint intersect(VersionConstraint other) => this; |
| 215 VersionConstraint union(VersionConstraint other) => other; |
| 216 String toString() => '<empty>'; |
| 217 } |
| 218 |
| 219 class _CompatibleWithVersionRange extends VersionRange { |
| 220 _CompatibleWithVersionRange(Version version) : super( |
| 221 min: version, includeMin: true, |
| 222 max: version.nextBreaking, includeMax: false); |
| 223 |
| 224 String toString() => '^$min'; |
| 225 } |
OLD | NEW |