| // 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 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 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; | |
| } |