Chromium Code Reviews

Unified Diff: sdk/lib/_internal/pub/lib/src/version.dart

Issue 62103010: Properly handle leading zeros in pub version numbers. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
« no previous file with comments | « no previous file | sdk/lib/_internal/pub/test/version_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/_internal/pub/lib/src/version.dart
diff --git a/sdk/lib/_internal/pub/lib/src/version.dart b/sdk/lib/_internal/pub/lib/src/version.dart
index 969d268039b972fece4d1b6d858d46f3203bb7ca..dcba9d447b5459c3f3a8d36c2620ece0028a50a9 100644
--- a/sdk/lib/_internal/pub/lib/src/version.dart
+++ b/sdk/lib/_internal/pub/lib/src/version.dart
@@ -9,6 +9,8 @@ library pub.version;
import 'dart:math';
+import 'package:collection_helpers/equality.dart';
+
/// Regex that matches a version number at the beginning of a string.
final _START_VERSION = new RegExp(
r'^' // Start at beginning.
@@ -23,10 +25,14 @@ final _COMPLETE_VERSION = new RegExp("${_START_VERSION.pattern}\$");
/// a string.
final _START_COMPARISON = new RegExp(r"^[<>]=?");
+/// The equality operator to use for comparing version components.
+final _equality = const IterableEquality();
+
/// A parsed semantic version number.
class Version implements Comparable<Version>, VersionConstraint {
/// No released version: i.e. "0.0.0".
static Version get none => new Version(0, 0, 0);
+
/// The major version number: "1" in "1.2.3".
final int major;
@@ -36,15 +42,30 @@ class Version implements Comparable<Version>, VersionConstraint {
/// The patch version number: "3" in "1.2.3".
final int patch;
- /// The pre-release identifier: "foo" in "1.2.3-foo". May be `null`.
- final String preRelease;
+ /// The pre-release identifier: "foo" in "1.2.3-foo".
+ ///
+ /// This is split into a list of components, each of which may be either a
+ /// string or a non-negative integer. It may also be empty, indicating that
+ /// this version has no pre-release identifier.
+ final List preRelease;
- /// The build identifier: "foo" in "1.2.3+foo". May be `null`.
- final String build;
+ /// The build identifier: "foo" in "1.2.3+foo".
+ ///
+ /// This is split into a list of components, each of which may be either a
+ /// string or a non-negative integer. It may also be empty, indicating that
+ /// this version has no build identifier.
+ final List build;
- /// Creates a new [Version] object.
- Version(this.major, this.minor, this.patch, {String pre, this.build})
- : preRelease = pre {
+ /// The original string representation of the version number.
+ ///
+ /// This preserves textual artifacts like leading zeros that may be left out
+ /// of the parsed version.
+ final String _text;
+
+ Version._(this.major, this.minor, this.patch, String preRelease, String build,
+ this._text)
+ : preRelease = preRelease == null ? [] : _splitParts(preRelease),
+ build = build == null ? [] : _splitParts(build) {
if (major < 0) throw new ArgumentError(
'Major version must be non-negative.');
if (minor < 0) throw new ArgumentError(
@@ -53,6 +74,15 @@ class Version implements Comparable<Version>, VersionConstraint {
'Patch version must be non-negative.');
}
+ /// Creates a new [Version] object.
+ factory Version(int major, int minor, int patch, {String pre, String build}) {
+ var text = "$major.$minor.$patch";
+ if (pre != null) text += "-$pre";
+ if (build != null) text += "+$build";
+
+ return new Version._(major, minor, patch, pre, build, text);
+ }
+
/// Creates a new [Version] by parsing [text].
factory Version.parse(String text) {
final match = _COMPLETE_VERSION.firstMatch(text);
@@ -68,7 +98,7 @@ class Version implements Comparable<Version>, VersionConstraint {
String preRelease = match[5];
String build = match[8];
- return new Version(major, minor, patch, pre: preRelease, build: build);
+ return new Version._(major, minor, patch, preRelease, build, text);
} on FormatException catch (ex) {
throw new FormatException('Could not parse "$text".');
}
@@ -88,11 +118,31 @@ class Version implements Comparable<Version>, VersionConstraint {
return primary;
}
+ /// Splits a string of dot-delimited identifiers into their component parts.
+ ///
+ /// Identifiers that are numeric are converted to numbers.
+ static List _splitParts(String text) {
+ return text.split('.').map((part) {
+ try {
+ return int.parse(part);
+ } on FormatException catch (ex) {
+ // Not a number.
+ return part;
+ }
+ }).toList();
+ }
+
bool operator ==(other) {
if (other is! Version) return false;
- return compareTo(other) == 0;
+ return major == other.major && minor == other.minor &&
+ patch == other.patch &&
+ _equality.equals(preRelease, other.preRelease) &&
+ _equality.equals(build, other.build);
}
+ int get hashCode => major ^ minor ^ patch ^ _equality.hash(preRelease) ^
+ _equality.hash(build);
+
bool operator <(Version other) => compareTo(other) < 0;
bool operator >(Version other) => compareTo(other) > 0;
bool operator <=(Version other) => compareTo(other) <= 0;
@@ -102,7 +152,7 @@ class Version implements Comparable<Version>, VersionConstraint {
bool get isEmpty => false;
/// Whether or not this is a pre-release version.
- bool get isPreRelease => preRelease != null;
+ bool get isPreRelease => preRelease.isNotEmpty;
/// Tests if [other] matches this version exactly.
bool allows(Version other) => this == other;
@@ -127,83 +177,57 @@ class Version implements Comparable<Version>, VersionConstraint {
if (minor != other.minor) return minor.compareTo(other.minor);
if (patch != other.patch) return patch.compareTo(other.patch);
- if (preRelease != other.preRelease) {
- // Pre-releases always come before no pre-release string.
- if (preRelease == null) return 1;
- if (other.preRelease == null) return -1;
+ // Pre-releases always come before no pre-release string.
+ if (!isPreRelease && other.isPreRelease) return 1;
+ if (!other.isPreRelease && isPreRelease) return -1;
- return _compareStrings(preRelease, other.preRelease);
- }
-
- if (build != other.build) {
- // Builds always come after no build string.
- if (build == null) return -1;
- if (other.build == null) return 1;
-
- return _compareStrings(build, other.build);
- }
+ var comparison = _compareLists(preRelease, other.preRelease);
+ if (comparison != 0) return comparison;
- return 0;
+ // Builds always come after no build string.
+ if (build.isEmpty && other.build.isNotEmpty) return -1;
+ if (other.build.isEmpty && build.isNotEmpty) return 1;
+ return _compareLists(build, other.build);
}
- int get hashCode => toString().hashCode;
+ String toString() => _text;
- String toString() {
- var buffer = new StringBuffer();
- buffer.write('$major.$minor.$patch');
- if (preRelease != null) buffer.write('-$preRelease');
- if (build != null) buffer.write('+$build');
- return buffer.toString();
- }
-
- /// Compares the string part of two versions. This is used for the pre-release
- /// and build version parts. This follows Rule 12. of the Semantic Versioning
- /// spec.
- int _compareStrings(String a, String b) {
- var aParts = _splitParts(a);
- var bParts = _splitParts(b);
-
- for (int i = 0; i < max(aParts.length, bParts.length); i++) {
- var aPart = (i < aParts.length) ? aParts[i] : null;
- var bPart = (i < bParts.length) ? bParts[i] : null;
-
- if (aPart != bPart) {
- // Missing parts come before present ones.
- if (aPart == null) return -1;
- if (bPart == null) return 1;
-
- if (aPart is num) {
- if (bPart is num) {
- // Compare two numbers.
- return aPart.compareTo(bPart);
- } else {
- // Numbers come before strings.
- return -1;
- }
+ /// Compares a dot-separated component of two versions.
+ ///
+ /// This is used for the pre-release and build version parts. This follows
+ /// Rule 12 of the Semantic Versioning spec (v2.0.0-rc.1).
+ int _compareLists(List a, List b) {
+ for (var i = 0; i < max(a.length, b.length); i++) {
+ var aPart = (i < a.length) ? a[i] : null;
+ var bPart = (i < b.length) ? b[i] : null;
+
+ if (aPart == bPart) continue;
+
+ // Missing parts come before present ones.
+ if (aPart == null) return -1;
+ if (bPart == null) return 1;
+
+ if (aPart is num) {
+ if (bPart is num) {
+ // Compare two numbers.
+ return aPart.compareTo(bPart);
+ } else {
+ // Numbers come before strings.
+ return -1;
+ }
+ } else {
+ if (bPart is num) {
+ // Strings come after numbers.
+ return 1;
} else {
- if (bPart is num) {
- // Strings come after numbers.
- return 1;
- } else {
- // Compare two strings.
- return aPart.compareTo(bPart);
- }
+ // Compare two strings.
+ return aPart.compareTo(bPart);
}
}
}
- }
- /// Splits a string of dot-delimited identifiers into their component parts.
- /// Identifiers that are numeric are converted to numbers.
- List _splitParts(String text) {
- return text.split('.').map((part) {
- try {
- return int.parse(part);
- } on FormatException catch (ex) {
- // Not a number.
- return part;
- }
- }).toList();
+ // The lists are entirely equal.
+ return 0;
}
}
« no previous file with comments | « no previous file | sdk/lib/_internal/pub/test/version_test.dart » ('j') | no next file with comments »

Powered by Google App Engine