1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 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;
}