version.dart 4.46 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'package:meta/meta.dart';

7
// TODO(reidbaker): Investigate using pub_semver instead of this class.
8
@immutable
9 10
class Version implements Comparable<Version> {
  /// Creates a new [Version] object.
11
  factory Version(int? major, int? minor, int? patch, {String? text}) {
12 13
    if (text == null) {
      text = major == null ? '0' : '$major';
14
      if (minor != null) {
15
        text = '$text.$minor';
16 17
      }
      if (patch != null) {
18
        text = '$text.$patch';
19
      }
20 21
    }

22
    return Version._(major ?? 0, minor ?? 0, patch ?? 0, text);
23 24
  }

25 26 27
  /// Public constant constructor when all fields are non-null, without default value fallbacks.
  const Version.withText(this.major, this.minor, this.patch, this._text);

28
  Version._(this.major, this.minor, this.patch, this._text) {
29
    if (major < 0) {
30
      throw ArgumentError('Major version must be non-negative.');
31 32
    }
    if (minor < 0) {
33
      throw ArgumentError('Minor version must be non-negative.');
34 35
    }
    if (patch < 0) {
36
      throw ArgumentError('Patch version must be non-negative.');
37
    }
38 39 40
  }

  /// Creates a new [Version] by parsing [text].
41 42
  static Version? parse(String? text) {
    final Match? match = versionPattern.firstMatch(text ?? '');
43
    if (match == null) {
44
      return null;
45 46 47
    }

    try {
48 49 50
      final int major = int.parse(match[1] ?? '0');
      final int minor = int.parse(match[3] ?? '0');
      final int patch = int.parse(match[5] ?? '0');
51
      return Version._(major, minor, patch, text ?? '');
52
    } on FormatException {
53
      return null;
54 55 56
    }
  }

57 58 59
  /// Returns the primary version out of a list of candidates.
  ///
  /// This is the highest-numbered stable version.
60 61
  static Version? primary(List<Version> versions) {
    Version? primary;
62
    for (final Version version in versions) {
63 64 65 66 67 68 69 70
      if (primary == null || (version > primary)) {
        primary = version;
      }
    }
    return primary;
  }


71
  static Version get unknown => Version(0, 0, 0, text: 'unknown');
72

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
  /// 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+))?)?');

91 92 93
  /// Two [Version]s are equal if their version numbers are. The version text
  /// is ignored.
  @override
94 95 96 97 98
  bool operator ==(Object other) {
    return other is Version
        && other.major == major
        && other.minor == minor
        && other.patch == patch;
99 100 101
  }

  @override
102
  int get hashCode => Object.hash(major, minor, patch);
103 104 105 106 107 108 109 110

  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) {
111
    if (major != other.major) {
112
      return major.compareTo(other.major);
113 114
    }
    if (minor != other.minor) {
115
      return minor.compareTo(other.minor);
116
    }
117 118 119 120 121 122
    return patch.compareTo(other.patch);
  }

  @override
  String toString() => _text;
}
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

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