Index: mojo/public/dart/third_party/pub_semver/lib/src/version_range.dart |
diff --git a/mojo/public/dart/third_party/pub_semver/lib/src/version_range.dart b/mojo/public/dart/third_party/pub_semver/lib/src/version_range.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5c53834c67267206a1184baf7853e489d72aaa94 |
--- /dev/null |
+++ b/mojo/public/dart/third_party/pub_semver/lib/src/version_range.dart |
@@ -0,0 +1,337 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library pub_semver.src.version_range; |
+ |
+import 'version.dart'; |
+import 'version_constraint.dart'; |
+import 'version_union.dart'; |
+ |
+/// Constrains versions to a fall within a given range. |
+/// |
+/// If there is a minimum, then this only allows versions that are at that |
+/// minimum or greater. If there is a maximum, then only versions less than |
+/// that are allowed. In other words, this allows `>= min, < max`. |
+class VersionRange implements VersionConstraint { |
+ /// The minimum end of the range. |
+ /// |
+ /// If [includeMin] is `true`, this will be the minimum allowed version. |
+ /// Otherwise, it will be the highest version below the range that is not |
+ /// allowed. |
+ /// |
+ /// This may be `null` in which case the range has no minimum end and allows |
+ /// any version less than the maximum. |
+ final Version min; |
+ |
+ /// The maximum end of the range. |
+ /// |
+ /// If [includeMax] is `true`, this will be the maximum allowed version. |
+ /// Otherwise, it will be the lowest version above the range that is not |
+ /// allowed. |
+ /// |
+ /// This may be `null` in which case the range has no maximum end and allows |
+ /// any version greater than the minimum. |
+ final Version max; |
+ |
+ /// If `true` then [min] is allowed by the range. |
+ final bool includeMin; |
+ |
+ /// If `true`, then [max] is allowed by the range. |
+ final bool includeMax; |
+ |
+ /// Creates a new version range from [min] to [max], either inclusive or |
+ /// exclusive. |
+ /// |
+ /// If it is an error if [min] is greater than [max]. |
+ /// |
+ /// Either [max] or [min] may be omitted to not clamp the range at that end. |
+ /// If both are omitted, the range allows all versions. |
+ /// |
+ /// If [includeMin] is `true`, then the minimum end of the range is inclusive. |
+ /// Likewise, passing [includeMax] as `true` makes the upper end inclusive. |
+ VersionRange({this.min, this.max, |
+ this.includeMin: false, this.includeMax: false}) { |
+ if (min != null && max != null && min > max) { |
+ throw new ArgumentError( |
+ '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; |
+ } |
+ |
+ int get hashCode => min.hashCode ^ (max.hashCode * 3) ^ |
+ (includeMin.hashCode * 5) ^ (includeMax.hashCode * 7); |
+ |
+ bool get isEmpty => false; |
+ |
+ bool get isAny => min == null && max == null; |
+ |
+ /// Tests if [other] falls within this version range. |
+ bool allows(Version other) { |
+ if (min != null) { |
+ if (other < min) return false; |
+ if (!includeMin && other == min) return false; |
+ } |
+ |
+ if (max != null) { |
+ if (other > max) return false; |
+ if (!includeMax && other == max) return false; |
+ |
+ |
+ // Disallow pre-release versions that have the same major, minor, and |
+ // patch version as the max, but only if neither the max nor the min is a |
+ // pre-release of that version. This ensures that "^1.2.3" doesn't include |
+ // "2.0.0-pre", while also allowing both ">=2.0.0-pre.2 <2.0.0" and |
+ // ">=1.2.3 <2.0.0-pre.7" to match "2.0.0-pre.5". |
+ // |
+ // It's worth noting that this is different than [NPM's semantics][]. NPM |
+ // disallows **all** pre-release versions unless their major, minor, and |
+ // patch numbers match those of a prerelease min or max. This ensures that |
+ // no prerelease versions will ever be selected if the user doesn't |
+ // explicitly allow them. |
+ // |
+ // [NPM's semantics]: https://www.npmjs.org/doc/misc/semver.html#prerelease-tags |
+ // |
+ // Instead, we ensure that release versions will always be preferred over |
+ // prerelease versions by ordering the release versions first in |
+ // [Version.prioritize]. This means that constraints like "any" or |
+ // ">1.2.3" can still match prerelease versions if they're the only things |
+ // available. |
+ var maxIsReleaseOfOther = !includeMax && |
+ !max.isPreRelease && other.isPreRelease && |
+ _equalsWithoutPreRelease(other, max); |
+ var minIsPreReleaseOfOther = min != null && min.isPreRelease && |
+ _equalsWithoutPreRelease(other, min); |
+ if (maxIsReleaseOfOther && !minIsPreReleaseOfOther) return false; |
+ } |
+ |
+ return true; |
+ } |
+ |
+ bool _equalsWithoutPreRelease(Version version1, Version version2) => |
+ version1.major == version2.major && |
+ version1.minor == version2.minor && |
+ version1.patch == version2.patch; |
+ |
+ bool allowsAll(VersionConstraint other) { |
+ if (other.isEmpty) return true; |
+ if (other is Version) return allows(other); |
+ |
+ if (other is VersionUnion) { |
+ return other.constraints.every((constraint) => allowsAll(constraint)); |
+ } |
+ |
+ if (other is VersionRange) { |
+ if (min != null) { |
+ if (other.min == null) return false; |
+ if (min > other.min) return false; |
+ if (min == other.min && !includeMin && other.includeMin) return false; |
+ } |
+ |
+ if (max != null) { |
+ if (other.max == null) return false; |
+ if (max < other.max) return false; |
+ if (max == other.max && !includeMax && other.includeMax) return false; |
+ } |
+ |
+ return true; |
+ } |
+ |
+ throw new ArgumentError('Unknown VersionConstraint type $other.'); |
+ } |
+ |
+ bool allowsAny(VersionConstraint other) { |
+ if (other.isEmpty) return false; |
+ if (other is Version) return allows(other); |
+ |
+ if (other is VersionUnion) { |
+ return other.constraints.any((constraint) => allowsAny(constraint)); |
+ } |
+ |
+ if (other is VersionRange) { |
+ // If neither range has a minimum, they'll overlap at some point. |
+ // |
+ // ... this ] |
+ // ... other ] |
+ if (min == null && other.min == null) return true; |
+ |
+ // If this range has a lower minimum than the other range, it overlaps as |
+ // long as its maximum is higher than or the same as the other range's |
+ // minimum. |
+ // |
+ // [ this ] [ this ] |
+ // [ other ] [ other ] |
+ if (min == null || (other.min != null && min < other.min)) { |
+ if (max == null) return true; |
+ if (max > other.min) return true; |
+ if (max < other.min) return false; |
+ assert(max == other.min); |
+ return includeMax && other.includeMin; |
+ } |
+ |
+ // If this range has a higher minimum than the other range, it overlaps as |
+ // long as its minimum is lower than or the same as the other range's |
+ // maximum. |
+ // |
+ // [ this ] [ this ] |
+ // [ other ] [ other ] |
+ if (other.max == null) return true; |
+ if (min < other.max) return true; |
+ if (min > other.max) return false; |
+ assert(min == other.max); |
+ return includeMin && other.includeMax; |
+ } |
+ |
+ throw new ArgumentError('Unknown VersionConstraint type $other.'); |
+ } |
+ |
+ VersionConstraint intersect(VersionConstraint other) { |
+ if (other.isEmpty) return other; |
+ if (other is VersionUnion) return other.intersect(this); |
+ |
+ // A range and a Version just yields the version if it's in the range. |
+ if (other is Version) { |
+ return allows(other) ? other : VersionConstraint.empty; |
+ } |
+ |
+ 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 VersionConstraint.empty; |
+ } |
+ |
+ if (intersectMin != null && intersectMax != null && |
+ intersectMin > intersectMax) { |
+ // Non-overlapping ranges, so empty. |
+ return VersionConstraint.empty; |
+ } |
+ |
+ // If we got here, there is an actual range. |
+ return new VersionRange(min: intersectMin, max: intersectMax, |
+ includeMin: intersectIncludeMin, includeMax: intersectIncludeMax); |
+ } |
+ |
+ throw new ArgumentError('Unknown VersionConstraint type $other.'); |
+ } |
+ |
+ VersionConstraint union(VersionConstraint other) { |
+ if (other is Version) { |
+ if (allows(other)) return this; |
+ |
+ if (other == min) { |
+ return new VersionRange( |
+ min: this.min, max: this.max, |
+ includeMin: true, includeMax: this.includeMax); |
+ } |
+ |
+ if (other == max) { |
+ return new VersionRange( |
+ min: this.min, max: this.max, |
+ includeMin: this.includeMin, includeMax: true); |
+ } |
+ |
+ return new VersionConstraint.unionOf([this, other]); |
+ } |
+ |
+ if (other is VersionRange) { |
+ // If the two ranges don't overlap, we won't be able to create a single |
+ // VersionRange for both of them. |
+ var edgesTouch = (max == other.min && (includeMax || other.includeMin)) || |
+ (min == other.max && (includeMin || other.includeMax)); |
+ if (!edgesTouch && !allowsAny(other)) { |
+ return new VersionConstraint.unionOf([this, other]); |
+ } |
+ |
+ var unionMin = min; |
+ var unionIncludeMin = includeMin; |
+ var unionMax = max; |
+ var unionIncludeMax = includeMax; |
+ |
+ if (unionMin == null) { |
+ // Do nothing. |
+ } else if (other.min == null || other.min < min) { |
+ unionMin = other.min; |
+ unionIncludeMin = other.includeMin; |
+ } else if (min == other.min && other.includeMin) { |
+ // If the edges are the same but one is inclusive, make it inclusive. |
+ unionIncludeMin = true; |
+ } |
+ |
+ if (unionMax == null) { |
+ // Do nothing. |
+ } else if (other.max == null || other.max > max) { |
+ unionMax = other.max; |
+ unionIncludeMax = other.includeMax; |
+ } else if (max == other.max && other.includeMax) { |
+ // If the edges are the same but one is inclusive, make it inclusive. |
+ unionIncludeMax = true; |
+ } |
+ |
+ return new VersionRange(min: unionMin, max: unionMax, |
+ includeMin: unionIncludeMin, includeMax: unionIncludeMax); |
+ } |
+ |
+ return new VersionConstraint.unionOf([this, other]); |
+ } |
+ |
+ String toString() { |
+ var buffer = new StringBuffer(); |
+ |
+ if (min != null) { |
+ buffer.write(includeMin ? '>=' : '>'); |
+ buffer.write(min); |
+ } |
+ |
+ if (max != null) { |
+ if (min != null) buffer.write(' '); |
+ buffer.write(includeMax ? '<=' : '<'); |
+ buffer.write(max); |
+ } |
+ |
+ if (min == null && max == null) buffer.write('any'); |
+ return buffer.toString(); |
+ } |
+} |