Chromium Code Reviews| 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: i.e. the empty set. | |
|
nweiz
2014/09/25 22:52:02
I think we're supposed to avoid "i.e." in docs.
Bob Nystrom
2014/09/26 19:41:07
Done.
| |
| 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>[]; | |
|
nweiz
2014/09/25 22:52:02
Nit: no type annotations here or for the functions
Bob Nystrom
2014/09/26 19:41:07
Done.
nweiz
2014/09/26 19:44:23
This array still has an annotation.
Bob Nystrom
2014/11/10 22:14:06
Done.
| |
| 48 | |
| 49 void skipWhitespace() { | |
| 50 text = text.trim(); | |
| 51 } | |
| 52 | |
| 53 // Try to parse and consume a version number. | |
| 54 Version 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 VersionConstraint 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 |