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