Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Unified Diff: utils/pub/version.dart

Issue 10540151: First pass at version constraint solver. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Request all versions from the source, and not just the best. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « utils/pub/source.dart ('k') | utils/pub/version_solver.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: utils/pub/version.dart
diff --git a/utils/pub/version.dart b/utils/pub/version.dart
index ee5aca0cc461c22d4feddf28a8214299bf8bf124..bf638bc5e464f6cf47594e45d83f6eb9b7def32f 100644
--- a/utils/pub/version.dart
+++ b/utils/pub/version.dart
@@ -13,7 +13,8 @@
/** A parsed semantic version number. */
class Version implements Comparable, Hashable, VersionConstraint {
- static get none() => new Version(0, 0, 0);
+ /** No released version: i.e. "0.0.0". */
+ static Version get none() => new Version(0, 0, 0);
static final _PARSE_REGEX = const RegExp(
@'^' // Start at beginning.
@@ -71,7 +72,7 @@ class Version implements Comparable, Hashable, VersionConstraint {
}
}
- bool operator ==(Version other) {
+ bool operator ==(other) {
if (other is! Version) return false;
return compareTo(other) == 0;
}
@@ -81,9 +82,24 @@ class Version implements Comparable, Hashable, VersionConstraint {
bool operator <=(Version other) => compareTo(other) <= 0;
bool operator >=(Version other) => compareTo(other) >= 0;
+ bool get isEmpty() => false;
+
/** Tests if [other] matches this version exactly. */
bool allows(Version other) => this == other;
+ VersionConstraint intersect(VersionConstraint other) {
+ if (other.isEmpty) return other;
+
+ // Intersect a version and a range.
+ if (other is VersionRange) return other.intersect(this);
+
+ // Intersecting two versions only works if they are the same.
+ if (other is Version) return this == other ? this : const _EmptyVersion();
+
+ throw new IllegalArgumentException(
+ 'Unknown VersionConstraint type $other.');
+ }
+
int compareTo(Version other) {
if (major != other.major) return major.compareTo(other.major);
if (minor != other.minor) return minor.compareTo(other.minor);
@@ -179,8 +195,52 @@ class Version implements Comparable, Hashable, VersionConstraint {
* version that is "2.0.0" or greater. Version objects themselves implement
* this to match a specific version.
*/
-interface VersionConstraint {
+interface VersionConstraint default _VersionConstraintFactory {
+ /**
+ * A [VersionConstraint] that allows no versions: i.e. the empty set.
+ */
+ VersionConstraint.empty();
+
+ /**
+ * Parses a version constraint. This string is a space-separated series of
+ * version parts. Each part can be one of:
+ *
+ * * A version string like `1.2.3`. In other words, anything that can be
+ * parsed by [Version.parse()].
+ * * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a version
+ * string. There cannot be a space between the operator and the version.
+ *
+ * Examples:
+ *
+ * 1.2.3-alpha
+ * <=5.1.4
+ * >2.0.4 <=2.4.6
+ */
+ VersionConstraint.parse(String text);
+
+ /**
+ * Creates a new version constraint that is the intersection of [constraints].
+ * It will only allow versions that all of those constraints allow. If
+ * constraints is empty, then it returns a VersionConstraint that allows all
+ * versions.
+ */
+ VersionConstraint.intersect(Collection<VersionConstraint> constraints);
+
+ /**
+ * Returns `true` if this constraint allows no versions.
+ */
+ bool get isEmpty();
+
+ /**
+ * Returns `true` if this constraint allows [version].
+ */
bool allows(Version version);
+
+ /**
+ * Creates a new [VersionConstraint] that only allows [Version]s allowed by
+ * both this and [other].
+ */
+ VersionConstraint intersect(VersionConstraint other);
}
/**
@@ -192,18 +252,176 @@ interface VersionConstraint {
class VersionRange implements VersionConstraint {
final Version min;
final Version max;
+ final bool includeMin;
+ final bool includeMax;
- VersionRange([this.min, this.max]) {
+ VersionRange([this.min, this.max,
+ this.includeMin = false, this.includeMax = false]) {
if (min != null && max != null && min > max) {
throw new IllegalArgumentException(
- 'Maximum version ("$max") must be less than minimum ("$min").');
+ 'Minimum version ("$min") must be less than maximum ("$max").');
}
}
+ bool operator ==(other) {
+ if (other is! VersionRange) return false;
+
+ return min == other.min &&
+ max == other.max &&
+ includeMin == other.includeMin &&
+ includeMax == other.includeMax;
+ }
+
+ bool get isEmpty() => false;
+
/** Tests if [other] matches falls within this version range. */
bool allows(Version other) {
if (min != null && other < min) return false;
- if (max != null && other >= max) return false;
+ if (min != null && !includeMin && other == min) return false;
+ if (max != null && other > max) return false;
+ if (max != null && !includeMax && other == max) return false;
return true;
}
+
+ VersionConstraint intersect(VersionConstraint other) {
+ if (other.isEmpty) return other;
+
+ // A range and a Version just yields the version if it's in the range.
+ if (other is Version) return allows(other) ? other : const _EmptyVersion();
+
+ if (other is VersionRange) {
+ // Intersect the two ranges.
+ var intersectMin = min;
+ var intersectIncludeMin = includeMin;
+ var intersectMax = max;
+ var intersectIncludeMax = includeMax;
+
+ if (other.min == null) {
+ // Do nothing.
+ } else if (intersectMin == null || intersectMin < other.min) {
+ intersectMin = other.min;
+ intersectIncludeMin = other.includeMin;
+ } else if (intersectMin == other.min && !other.includeMin) {
+ // The edges are the same, but one is exclusive, make it exclusive.
+ intersectIncludeMin = false;
+ }
+
+ if (other.max == null) {
+ // Do nothing.
+ } else if (intersectMax == null || intersectMax > other.max) {
+ intersectMax = other.max;
+ intersectIncludeMax = other.includeMax;
+ } else if (intersectMax == other.max && !other.includeMax) {
+ // The edges are the same, but one is exclusive, make it exclusive.
+ intersectIncludeMax = false;
+ }
+
+ if (intersectMin == null && intersectMax == null) {
+ // Open range.
+ return new VersionRange();
+ }
+
+ // If the range is just a single version.
+ if (intersectMin == intersectMax) {
+ // If both ends are inclusive, allow that version.
+ if (intersectIncludeMin && intersectIncludeMax) return intersectMin;
+
+ // Otherwise, no versions.
+ return const _EmptyVersion();
+ }
+
+ if (intersectMin != null && intersectMax != null &&
+ intersectMin > intersectMax) {
+ // Non-overlapping ranges, so empty.
+ return const _EmptyVersion();
+ }
+
+ // If we got here, there is an actual range.
+ return new VersionRange(intersectMin, intersectMax,
+ intersectIncludeMin, intersectIncludeMax);
+ }
+
+ throw new IllegalArgumentException(
+ 'Unknown VersionConstraint type $other.');
+ }
+
+ String toString() {
+ var buffer = new StringBuffer();
+
+ if (min != null) {
+ buffer.add(includeMin ? '>=' : '>');
+ buffer.add(min);
+ }
+
+ if (max != null) {
+ if (min != null) buffer.add(' ');
+ buffer.add(includeMax ? '<=' : '<');
+ buffer.add(max);
+ }
+
+ if (min == null && max == null) buffer.add('any');
+ return buffer.toString();
+ }
+}
+
+class _EmptyVersion implements VersionConstraint {
+ const _EmptyVersion();
+
+ bool get isEmpty() => true;
+ bool allows(Version other) => false;
+ VersionConstraint intersect(VersionConstraint other) => this;
+ String toString() => '<empty>';
+}
+
+class _VersionConstraintFactory {
+ factory VersionConstraint.empty() => const _EmptyVersion();
+
+ factory VersionConstraint.parse(String text) {
+ if (text.trim() == '') {
+ throw new FormatException('Cannot parse an empty string.');
+ }
+
+ // Split it into space-separated parts.
+ var constraints = <VersionConstraint>[];
+ for (var part in text.split(' ')) {
+ constraints.add(parseSingleConstraint(part));
+ }
+
+ return new VersionConstraint.intersect(constraints);
+ }
+
+ factory VersionConstraint.intersect(
+ Collection<VersionConstraint> constraints) {
+ var constraint = new VersionRange();
+ for (var other in constraints) {
+ constraint = constraint.intersect(other);
+ }
+ return constraint;
+ }
+
+ static VersionConstraint parseSingleConstraint(String text) {
+ if (text == 'any') {
+ return new VersionRange();
+ }
+
+ // TODO(rnystrom): Consider other syntaxes for version constraints. This
+ // one is whitespace sensitive (you can't do "< 1.2.3") and "<" is
+ // unfortunately meaningful in YAML, requiring it to be quoted in a
+ // pubspec.
+ // See if it's a comparison operator followed by a version, like ">1.2.3".
+ var match = const RegExp(@"^([<>]=?)?(.*)$").firstMatch(text);
+ if (match != null) {
+ var comparison = match[1];
+ var version = new Version.parse(match[2]);
+ switch (match[1]) {
+ case '<=': return new VersionRange(max: version, includeMax: true);
+ case '<': return new VersionRange(max: version, includeMax: false);
+ case '>=': return new VersionRange(min: version, includeMin: true);
+ case '>': return new VersionRange(min: version, includeMin: false);
+ }
+ }
+
+ // Otherwise, it must be an explicit version.
+ return new Version.parse(text);
+ }
}
« no previous file with comments | « utils/pub/source.dart ('k') | utils/pub/version_solver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698