OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * Handles version numbers, following the [Semantic Versioning][semver] spec. | 6 * Handles version numbers, following the [Semantic Versioning][semver] spec. |
7 * | 7 * |
8 * [semver]: http://semver.org/ | 8 * [semver]: http://semver.org/ |
9 */ | 9 */ |
10 library version; | 10 library version; |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
190 }); | 190 }); |
191 } | 191 } |
192 } | 192 } |
193 | 193 |
194 /** | 194 /** |
195 * A [VersionConstraint] is a predicate that can determine whether a given | 195 * A [VersionConstraint] is a predicate that can determine whether a given |
196 * version is valid or not. For example, a ">= 2.0.0" constraint allows any | 196 * version is valid or not. For example, a ">= 2.0.0" constraint allows any |
197 * version that is "2.0.0" or greater. Version objects themselves implement | 197 * version that is "2.0.0" or greater. Version objects themselves implement |
198 * this to match a specific version. | 198 * this to match a specific version. |
199 */ | 199 */ |
200 interface VersionConstraint default _VersionConstraintFactory { | 200 abstract class VersionConstraint { |
201 /** | 201 /** |
202 * A [VersionConstraint] that allows no versions: i.e. the empty set. | 202 * A [VersionConstraint] that allows no versions: i.e. the empty set. |
203 */ | 203 */ |
204 VersionConstraint.empty(); | 204 factory VersionConstraint.empty() => const _EmptyVersion(); |
205 | 205 |
206 /** | 206 /** |
207 * Parses a version constraint. This string is a space-separated series of | 207 * Parses a version constraint. This string is a space-separated series of |
208 * version parts. Each part can be one of: | 208 * version parts. Each part can be one of: |
209 * | 209 * |
210 * * A version string like `1.2.3`. In other words, anything that can be | 210 * * A version string like `1.2.3`. In other words, anything that can be |
211 * parsed by [Version.parse()]. | 211 * parsed by [Version.parse()]. |
212 * * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a version | 212 * * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a version |
213 * string. There cannot be a space between the operator and the version. | 213 * string. There cannot be a space between the operator and the version. |
214 * | 214 * |
215 * Examples: | 215 * Examples: |
216 * | 216 * |
217 * 1.2.3-alpha | 217 * 1.2.3-alpha |
218 * <=5.1.4 | 218 * <=5.1.4 |
219 * >2.0.4 <=2.4.6 | 219 * >2.0.4 <=2.4.6 |
220 */ | 220 */ |
221 VersionConstraint.parse(String text); | 221 factory VersionConstraint.parse(String text) { |
| 222 if (text.trim() == '') { |
| 223 throw new FormatException('Cannot parse an empty string.'); |
| 224 } |
| 225 |
| 226 // Split it into space-separated parts. |
| 227 var constraints = <VersionConstraint>[]; |
| 228 for (var part in text.split(' ')) { |
| 229 constraints.add(_parseSingleConstraint(part)); |
| 230 } |
| 231 |
| 232 return new VersionConstraint.intersection(constraints); |
| 233 } |
222 | 234 |
223 /** | 235 /** |
224 * Creates a new version constraint that is the intersection of [constraints]. | 236 * Creates a new version constraint that is the intersection of [constraints]. |
225 * It will only allow versions that all of those constraints allow. If | 237 * It will only allow versions that all of those constraints allow. If |
226 * constraints is empty, then it returns a VersionConstraint that allows all | 238 * constraints is empty, then it returns a VersionConstraint that allows all |
227 * versions. | 239 * versions. |
228 */ | 240 */ |
229 VersionConstraint.intersect(Collection<VersionConstraint> constraints); | 241 factory VersionConstraint.intersection( |
| 242 Collection<VersionConstraint> constraints) { |
| 243 var constraint = new VersionRange(); |
| 244 for (var other in constraints) { |
| 245 constraint = constraint.intersect(other); |
| 246 } |
| 247 return constraint; |
| 248 } |
230 | 249 |
231 /** | 250 /** |
232 * Returns `true` if this constraint allows no versions. | 251 * Returns `true` if this constraint allows no versions. |
233 */ | 252 */ |
234 bool get isEmpty; | 253 bool get isEmpty; |
235 | 254 |
236 /** | 255 /** |
237 * Returns `true` if this constraint allows [version]. | 256 * Returns `true` if this constraint allows [version]. |
238 */ | 257 */ |
239 bool allows(Version version); | 258 bool allows(Version version); |
240 | 259 |
241 /** | 260 /** |
242 * Creates a new [VersionConstraint] that only allows [Version]s allowed by | 261 * Creates a new [VersionConstraint] that only allows [Version]s allowed by |
243 * both this and [other]. | 262 * both this and [other]. |
244 */ | 263 */ |
245 VersionConstraint intersect(VersionConstraint other); | 264 VersionConstraint intersect(VersionConstraint other); |
| 265 |
| 266 static VersionConstraint _parseSingleConstraint(String text) { |
| 267 if (text == 'any') { |
| 268 return new VersionRange(); |
| 269 } |
| 270 |
| 271 // TODO(rnystrom): Consider other syntaxes for version constraints. This |
| 272 // one is whitespace sensitive (you can't do "< 1.2.3") and "<" is |
| 273 // unfortunately meaningful in YAML, requiring it to be quoted in a |
| 274 // pubspec. |
| 275 // See if it's a comparison operator followed by a version, like ">1.2.3". |
| 276 var match = new RegExp(r"^([<>]=?)?(.*)$").firstMatch(text); |
| 277 if (match != null) { |
| 278 var comparison = match[1]; |
| 279 var version = new Version.parse(match[2]); |
| 280 switch (match[1]) { |
| 281 case '<=': return new VersionRange(max: version, includeMax: true); |
| 282 case '<': return new VersionRange(max: version, includeMax: false); |
| 283 case '>=': return new VersionRange(min: version, includeMin: true); |
| 284 case '>': return new VersionRange(min: version, includeMin: false); |
| 285 } |
| 286 } |
| 287 |
| 288 // Otherwise, it must be an explicit version. |
| 289 return new Version.parse(text); |
| 290 } |
246 } | 291 } |
247 | 292 |
248 /** | 293 /** |
249 * Constrains versions to a fall within a given range. If there is a minimum, | 294 * Constrains versions to a fall within a given range. If there is a minimum, |
250 * then this only allows versions that are at that minimum or greater. If there | 295 * then this only allows versions that are at that minimum or greater. If there |
251 * is a maximum, then only versions less than that are allowed. In other words, | 296 * is a maximum, then only versions less than that are allowed. In other words, |
252 * this allows `>= min, < max`. | 297 * this allows `>= min, < max`. |
253 */ | 298 */ |
254 class VersionRange implements VersionConstraint { | 299 class VersionRange implements VersionConstraint { |
255 final Version min; | 300 final Version min; |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
367 } | 412 } |
368 | 413 |
369 class _EmptyVersion implements VersionConstraint { | 414 class _EmptyVersion implements VersionConstraint { |
370 const _EmptyVersion(); | 415 const _EmptyVersion(); |
371 | 416 |
372 bool get isEmpty => true; | 417 bool get isEmpty => true; |
373 bool allows(Version other) => false; | 418 bool allows(Version other) => false; |
374 VersionConstraint intersect(VersionConstraint other) => this; | 419 VersionConstraint intersect(VersionConstraint other) => this; |
375 String toString() => '<empty>'; | 420 String toString() => '<empty>'; |
376 } | 421 } |
377 | |
378 class _VersionConstraintFactory { | |
379 factory VersionConstraint.empty() => const _EmptyVersion(); | |
380 | |
381 factory VersionConstraint.parse(String text) { | |
382 if (text.trim() == '') { | |
383 throw new FormatException('Cannot parse an empty string.'); | |
384 } | |
385 | |
386 // Split it into space-separated parts. | |
387 var constraints = <VersionConstraint>[]; | |
388 for (var part in text.split(' ')) { | |
389 constraints.add(parseSingleConstraint(part)); | |
390 } | |
391 | |
392 return new VersionConstraint.intersect(constraints); | |
393 } | |
394 | |
395 factory VersionConstraint.intersect( | |
396 Collection<VersionConstraint> constraints) { | |
397 var constraint = new VersionRange(); | |
398 for (var other in constraints) { | |
399 constraint = constraint.intersect(other); | |
400 } | |
401 return constraint; | |
402 } | |
403 | |
404 static VersionConstraint parseSingleConstraint(String text) { | |
405 if (text == 'any') { | |
406 return new VersionRange(); | |
407 } | |
408 | |
409 // TODO(rnystrom): Consider other syntaxes for version constraints. This | |
410 // one is whitespace sensitive (you can't do "< 1.2.3") and "<" is | |
411 // unfortunately meaningful in YAML, requiring it to be quoted in a | |
412 // pubspec. | |
413 // See if it's a comparison operator followed by a version, like ">1.2.3". | |
414 var match = new RegExp(r"^([<>]=?)?(.*)$").firstMatch(text); | |
415 if (match != null) { | |
416 var comparison = match[1]; | |
417 var version = new Version.parse(match[2]); | |
418 switch (match[1]) { | |
419 case '<=': return new VersionRange(max: version, includeMax: true); | |
420 case '<': return new VersionRange(max: version, includeMax: false); | |
421 case '>=': return new VersionRange(min: version, includeMin: true); | |
422 case '>': return new VersionRange(min: version, includeMin: false); | |
423 } | |
424 } | |
425 | |
426 // Otherwise, it must be an explicit version. | |
427 return new Version.parse(text); | |
428 } | |
429 } | |
OLD | NEW |