Fixes #124756 by removing the concept of `Version.unknown`. `Version` fields that needed the ability to represent an unknown version have been made nullable. Assigning `null` to them represents an unknown version.
159 lines
4.7 KiB
Dart
159 lines
4.7 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:meta/meta.dart';
|
|
|
|
/// Represents the version of some piece of software.
|
|
///
|
|
/// While a [Version] object has fields resembling semver, it does not
|
|
/// necessarily represent a semver version.
|
|
@immutable
|
|
class Version implements Comparable<Version> {
|
|
/// Creates a new [Version] object.
|
|
///
|
|
/// A null [minor] or [patch] version is logically equivalent to 0. Using null
|
|
/// for these parameters only affects the generation of [text], if no value
|
|
/// for it is provided.
|
|
factory Version(int major, int? minor, int? patch, {String? text}) {
|
|
if (text == null) {
|
|
text = '$major';
|
|
if (minor != null) {
|
|
text = '$text.$minor';
|
|
}
|
|
if (patch != null) {
|
|
text = '$text.$patch';
|
|
}
|
|
}
|
|
|
|
return Version._(major, minor ?? 0, patch ?? 0, text);
|
|
}
|
|
|
|
/// Public constant constructor when all fields are non-null, without default value fallbacks.
|
|
const Version.withText(this.major, this.minor, this.patch, this._text);
|
|
|
|
Version._(this.major, this.minor, this.patch, this._text) {
|
|
if (major < 0) {
|
|
throw ArgumentError('Major version must be non-negative.');
|
|
}
|
|
if (minor < 0) {
|
|
throw ArgumentError('Minor version must be non-negative.');
|
|
}
|
|
if (patch < 0) {
|
|
throw ArgumentError('Patch version must be non-negative.');
|
|
}
|
|
}
|
|
|
|
/// Creates a new [Version] by parsing [text].
|
|
static Version? parse(String? text) {
|
|
final Match? match = versionPattern.firstMatch(text ?? '');
|
|
if (match == null) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
final int major = int.parse(match[1] ?? '0');
|
|
final int minor = int.parse(match[3] ?? '0');
|
|
final int patch = int.parse(match[5] ?? '0');
|
|
return Version._(major, minor, patch, text ?? '');
|
|
} on FormatException {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Returns the primary version out of a list of candidates.
|
|
///
|
|
/// This is the highest-numbered stable version.
|
|
static Version? primary(List<Version> versions) {
|
|
Version? primary;
|
|
for (final Version version in versions) {
|
|
if (primary == null || (version > primary)) {
|
|
primary = version;
|
|
}
|
|
}
|
|
return primary;
|
|
}
|
|
|
|
/// The major version number: "1" in "1.2.3".
|
|
final int major;
|
|
|
|
/// The minor version number: "2" in "1.2.3".
|
|
final int minor;
|
|
|
|
/// The patch version number: "3" in "1.2.3".
|
|
final int patch;
|
|
|
|
/// 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;
|
|
|
|
static final RegExp versionPattern =
|
|
RegExp(r'^(\d+)(\.(\d+)(\.(\d+))?)?');
|
|
|
|
/// Two [Version]s are equal if their version numbers are. The version text
|
|
/// is ignored.
|
|
@override
|
|
bool operator ==(Object other) {
|
|
return other is Version
|
|
&& other.major == major
|
|
&& other.minor == minor
|
|
&& other.patch == patch;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => Object.hash(major, minor, patch);
|
|
|
|
bool operator <(Version other) => compareTo(other) < 0;
|
|
bool operator >(Version other) => compareTo(other) > 0;
|
|
bool operator <=(Version other) => compareTo(other) <= 0;
|
|
bool operator >=(Version other) => compareTo(other) >= 0;
|
|
|
|
@override
|
|
int compareTo(Version other) {
|
|
if (major != other.major) {
|
|
return major.compareTo(other.major);
|
|
}
|
|
if (minor != other.minor) {
|
|
return minor.compareTo(other.minor);
|
|
}
|
|
return patch.compareTo(other.patch);
|
|
}
|
|
|
|
@override
|
|
String toString() => _text;
|
|
}
|
|
|
|
/// Returns true if [targetVersion] is within the range [min] and [max]
|
|
/// inclusive by default.
|
|
///
|
|
/// [min] and [max] are evaluated by [Version.parse(text)].
|
|
///
|
|
/// Pass [inclusiveMin] = false for greater than and not equal to min.
|
|
/// Pass [inclusiveMax] = false for less than and not equal to max.
|
|
bool isWithinVersionRange(
|
|
String targetVersion, {
|
|
required String min,
|
|
required String max,
|
|
bool inclusiveMax = true,
|
|
bool inclusiveMin = true,
|
|
}) {
|
|
final Version? parsedTargetVersion = Version.parse(targetVersion);
|
|
final Version? minVersion = Version.parse(min);
|
|
final Version? maxVersion = Version.parse(max);
|
|
|
|
final bool withinMin = minVersion != null &&
|
|
parsedTargetVersion != null &&
|
|
(inclusiveMin
|
|
? parsedTargetVersion >= minVersion
|
|
: parsedTargetVersion > minVersion);
|
|
|
|
final bool withinMax = maxVersion != null &&
|
|
parsedTargetVersion != null &&
|
|
(inclusiveMax
|
|
? parsedTargetVersion <= maxVersion
|
|
: parsedTargetVersion < maxVersion);
|
|
return withinMin && withinMax;
|
|
}
|