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

Side by Side Diff: utils/pub/version.dart

Issue 10383203: Add base Version class to start support versioning in pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix ==. Created 8 years, 7 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | utils/tests/pub/test_pub.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 /**
6 * Handles version numbers, following the [Semantic Versioning][semver] spec.
7 *
8 * [semver]: http://semver.org/
9 */
10 #library('pub_version');
11
12 /** A parsed semantic version number. */
13 class Version implements Comparable, VersionConstraint {
14 static final _PARSE_REGEX = const RegExp(
15 @'^' // Start at beginning.
16 @'(\d+).(\d+).(\d+)' // Version number.
17 @'(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Pre-release.
18 @'(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Build.
19 @'$'); // Consume entire string.
20
21 /** The major version number: "1" in "1.2.3". */
22 final int major;
23
24 /** The minor version number: "2" in "1.2.3". */
25 final int minor;
26
27 /** The patch version number: "3" in "1.2.3". */
28 final int patch;
29
30 /** The pre-release identifier: "foo" in "1.2.3-foo". May be `null`. */
31 final String preRelease;
32
33 /** The build identifier: "foo" in "1.2.3+foo". May be `null`. */
34 final String build;
35
36 /** Creates a new [Version] object. */
37 Version(this.major, this.minor, this.patch, [String pre, this.build])
38 : preRelease = pre {
39 if (major < 0) throw new IllegalArgumentException(
40 'Major version must be non-negative.');
41 if (minor < 0) throw new IllegalArgumentException(
42 'Minor version must be non-negative.');
43 if (patch < 0) throw new IllegalArgumentException(
44 'Patch version must be non-negative.');
45 }
46
47 /**
48 * Creates a new [Version] by parsing [text].
49 */
50 factory Version.parse(String text) {
51 final match = _PARSE_REGEX.firstMatch(text);
52 if (match == null) {
53 throw new FormatException('Could not parse "$text".');
54 }
55
56 try {
57 int major = Math.parseInt(match[1]);
58 int minor = Math.parseInt(match[2]);
59 int patch = Math.parseInt(match[3]);
60
61 String preRelease = match[5];
62 String build = match[8];
63
64 return new Version(major, minor, patch, preRelease, build);
65 } catch (BadNumberFormatException ex) {
66 throw new FormatException('Could not parse "$text".');
67 }
68 }
69
70 bool operator ==(Version other) {
71 if (other is! Version) return false;
72 return compareTo(other) == 0;
73 }
74
75 bool operator <(Version other) => compareTo(other) < 0;
76 bool operator >(Version other) => compareTo(other) > 0;
77 bool operator <=(Version other) => compareTo(other) <= 0;
78 bool operator >=(Version other) => compareTo(other) >= 0;
79
80 /** Tests if [other] matches this version exactly. */
81 bool allows(Version other) => this == other;
82
83 int compareTo(Version other) {
84 if (major != other.major) return major.compareTo(other.major);
85 if (minor != other.minor) return minor.compareTo(other.minor);
86 if (patch != other.patch) return patch.compareTo(other.patch);
87
88 if (preRelease != other.preRelease) {
89 // Pre-releases always come before no pre-release string.
90 if (preRelease == null) return 1;
91 if (other.preRelease == null) return -1;
92
93 return _compareStrings(preRelease, other.preRelease);
94 }
95
96 if (build != other.build) {
97 // Builds always come after no build string.
98 if (build == null) return -1;
99 if (other.build == null) return 1;
100
101 return _compareStrings(build, other.build);
102 }
103
104 return 0;
105 }
106
107 String toString() {
108 var buffer = new StringBuffer();
109 buffer.add('$major.$minor.$patch');
110 if (preRelease != null) buffer.add('-$preRelease');
111 if (build != null) buffer.add('+$build');
112 return buffer.toString();
113 }
114
115 /**
116 * Compares the string part of two versions. This is used for the pre-release
117 * and build version parts. This follows Rule 12. of the Semantic Versioning
118 * spec.
119 */
120 int _compareStrings(String a, String b) {
121 var aParts = _splitParts(a);
122 var bParts = _splitParts(b);
123
124 for (int i = 0; i < Math.max(aParts.length, bParts.length); i++) {
125 var aPart = (i < aParts.length) ? aParts[i] : null;
126 var bPart = (i < bParts.length) ? bParts[i] : null;
127
128 if (aPart != bPart) {
129 // Missing parts come before present ones.
130 if (aPart == null) return -1;
131 if (bPart == null) return 1;
132
133 if (aPart is int) {
134 if (bPart is int) {
135 // Compare two numbers.
136 return aPart.compareTo(bPart);
137 } else {
138 // Numbers come before strings.
139 return -1;
140 }
141 } else {
142 if (bPart is int) {
143 // Strings come after numbers.
144 return 1;
145 } else {
146 // Compare two strings.
147 return aPart.compareTo(bPart);
148 }
149 }
150 }
151 }
152 }
153
154 /**
155 * Splits a string of dot-delimited identifiers into their component parts.
156 * Identifiers that are numeric are converted to numbers.
157 */
158 List _splitParts(String text) {
159 return text.split('.').map((part) {
160 try {
161 return Math.parseInt(part);
162 } catch (BadNumberFormatException ex) {
163 // Not a number.
164 return part;
165 }
166 });
167 }
168 }
169
170 /**
171 * A [VersionConstraint] is a predicate that can determine whether a given
172 * version is valid or not. For example, a ">= 2.0.0" constraint allows any
173 * version that is "2.0.0" or greater. Version objects themselves implement
174 * this to match a specific version.
175 */
176 interface VersionConstraint {
Sean Eagan 2012/05/17 15:35:39 couldn't this just be: typedef bool VersionConstr
Bob Nystrom 2012/05/17 16:48:30 It could be right now, but later patches will be a
177 bool allows(Version version);
178 }
179
180 /**
181 * Constrains versions to a fall within a given range. If there is a minimum,
182 * then this only allows versions that are at that minimum or greater. If there
183 * is a maximum, then only versions less than that are allowed. In other words,
184 * this allows `>= min, < max`.
185 */
186 class VersionRange implements VersionConstraint {
Sean Eagan 2012/05/17 15:35:39 There should be a generic interface for intervals
Bob Nystrom 2012/05/17 16:48:30 I like this idea, but sometimes I think you can be
187 final Version min;
188 final Version max;
189
190 VersionRange([this.min, this.max]) {
Sean Eagan 2012/05/17 15:35:39 As in the above linked gist, you can determine the
Bob Nystrom 2012/05/17 16:48:30 At least in the use case of versions, my hunch is
191 if (min != null && max != null && min > max) {
192 throw new IllegalArgumentException(
193 'Maximum version ("$max") must be less than minimum ("$min").');
194 }
195 }
196
197 /** Tests if [other] matches falls within this version range. */
198 bool allows(Version other) {
199 if (min != null && other < min) return false;
200 if (max != null && other >= max) return false;
201 return true;
202 }
203 }
204
205 /** Thrown by [Version.parse()] if the argument isn't a valid version string. */
206 class FormatException implements Exception {
207 final String message;
208
209 FormatException(this.message);
210
211 String toString() => message;
212 }
OLDNEW
« no previous file with comments | « no previous file | utils/tests/pub/test_pub.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698