blob: 6a93beae44e6a6ae66a429acf430fe0d8eac2022 [file] [log] [blame]
// 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.
/// Possible string formats that `flutter --version` can return.
enum VersionType {
/// A stable flutter release.
///
/// Example: '1.2.3'
stable,
/// A pre-stable flutter release.
///
/// Example: '1.2.3-4.5.pre'
development,
/// A master channel flutter version.
///
/// Example: '1.2.3-4.0.pre.10'
///
/// The last number is the number of commits past the last tagged version.
latest,
}
final Map<VersionType, RegExp> versionPatterns = <VersionType, RegExp>{
VersionType.stable: RegExp(r'^(\d+)\.(\d+)\.(\d+)$'),
VersionType.development: RegExp(r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre$'),
VersionType.latest: RegExp(r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre\.(\d+)$'),
};
class Version {
Version({
required this.x,
required this.y,
required this.z,
this.m,
this.n,
this.commits,
required this.type,
}) {
switch (type) {
case VersionType.stable:
assert(m == null);
assert(n == null);
assert(commits == null);
break;
case VersionType.development:
assert(m != null);
assert(n != null);
assert(commits == null);
break;
case VersionType.latest:
assert(m != null);
assert(n != null);
assert(commits != null);
break;
}
}
/// Create a new [Version] from a version string.
///
/// It is expected that [versionString] will be generated by
/// `flutter --version` and match one of `stablePattern`, `developmentPattern`
/// and `latestPattern`.
factory Version.fromString(String versionString) {
assert(versionString != null);
versionString = versionString.trim();
// stable tag
Match? match = versionPatterns[VersionType.stable]!.firstMatch(versionString);
if (match != null) {
// parse stable
final List<int> parts = match
.groups(<int>[1, 2, 3])
.map((String? s) => int.parse(s!))
.toList();
return Version(
x: parts[0],
y: parts[1],
z: parts[2],
type: VersionType.stable,
);
}
// development tag
match = versionPatterns[VersionType.development]!.firstMatch(versionString);
if (match != null) {
// parse development
final List<int> parts =
match.groups(<int>[1, 2, 3, 4, 5]).map((String? s) => int.parse(s!)).toList();
return Version(
x: parts[0],
y: parts[1],
z: parts[2],
m: parts[3],
n: parts[4],
type: VersionType.development,
);
}
// latest tag
match = versionPatterns[VersionType.latest]!.firstMatch(versionString);
if (match != null) {
// parse latest
final List<int> parts = match.groups(
<int>[1, 2, 3, 4, 5, 6],
).map(
(String? s) => int.parse(s!),
).toList();
return Version(
x: parts[0],
y: parts[1],
z: parts[2],
m: parts[3],
n: parts[4],
commits: parts[5],
type: VersionType.latest,
);
}
throw Exception('${versionString.trim()} cannot be parsed');
}
// Returns a new version with the given [increment] part incremented.
// NOTE new version must be of same type as previousVersion.
factory Version.increment(
Version previousVersion,
String increment, {
VersionType? nextVersionType,
}) {
final int nextX = previousVersion.x;
int nextY = previousVersion.y;
int nextZ = previousVersion.z;
int? nextM = previousVersion.m;
int? nextN = previousVersion.n;
if (nextVersionType == null) {
if (previousVersion.type == VersionType.latest) {
nextVersionType = VersionType.development;
} else {
nextVersionType = previousVersion.type;
}
}
switch (increment) {
case 'x':
// This was probably a mistake.
throw Exception('Incrementing x is not supported by this tool.');
case 'y':
// Dev release following a beta release.
nextY += 1;
nextZ = 0;
if (previousVersion.type != VersionType.stable) {
nextM = 0;
nextN = 0;
}
break;
case 'z':
// Hotfix to stable release.
assert(previousVersion.type == VersionType.stable);
nextZ += 1;
break;
case 'm':
// Regular dev release.
assert(previousVersion.type == VersionType.development);
nextM = nextM! + 1;
nextN = 0;
break;
case 'n':
// Hotfix to internal roll.
nextN = nextN! + 1;
break;
default:
throw Exception('Unknown increment level $increment.');
}
return Version(
x: nextX,
y: nextY,
z: nextZ,
m: nextM,
n: nextN,
type: nextVersionType,
);
}
/// Major version.
final int x;
/// Zero-indexed count of beta releases after a major release.
final int y;
/// Number of hotfix releases after a stable release.
final int z;
/// Zero-indexed count of dev releases after a beta release.
final int? m;
/// Number of hotfixes required to make a dev release.
final int? n;
/// Number of commits past last tagged dev release.
final int? commits;
final VersionType type;
@override
String toString() {
switch (type) {
case VersionType.stable:
return '$x.$y.$z';
case VersionType.development:
return '$x.$y.$z-$m.$n.pre';
case VersionType.latest:
return '$x.$y.$z-$m.$n.pre.$commits';
}
}
}