// 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'; /// 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(int.parse).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(int.parse).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(int.parse).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.'); break; 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); assert(nextM != null); nextM += 1; nextN = 0; break; case 'n': // Hotfix to internal roll. 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'; } return null; // For analyzer } }