diff --git a/pkgs/package_config/CHANGELOG.md b/pkgs/package_config/CHANGELOG.md index 2c070c25f8..ac3be04497 100644 --- a/pkgs/package_config/CHANGELOG.md +++ b/pkgs/package_config/CHANGELOG.md @@ -1,4 +1,7 @@ -## 2.1.2-wip +## 2.2.0-wip + +- Add relational operators to `LanguageVersion` with extension methods + exported under `LanguageVersionRelationalOperators`. - Include correct parameter names in errors when validating the `major` and `minor` versions in the `LanguageVersion.new` constructor. diff --git a/pkgs/package_config/lib/package_config_types.dart b/pkgs/package_config/lib/package_config_types.dart index 825f7acec9..5c2c3413c4 100644 --- a/pkgs/package_config/lib/package_config_types.dart +++ b/pkgs/package_config/lib/package_config_types.dart @@ -14,4 +14,9 @@ library; export 'src/errors.dart' show PackageConfigError; export 'src/package_config.dart' - show InvalidLanguageVersion, LanguageVersion, Package, PackageConfig; + show + InvalidLanguageVersion, + LanguageVersion, + LanguageVersionRelationalOperators, + Package, + PackageConfig; diff --git a/pkgs/package_config/lib/src/package_config.dart b/pkgs/package_config/lib/src/package_config.dart index 1f7562ae23..707e85703e 100644 --- a/pkgs/package_config/lib/src/package_config.dart +++ b/pkgs/package_config/lib/src/package_config.dart @@ -355,7 +355,7 @@ abstract class LanguageVersion implements Comparable { /// Two language versions are considered equal if they have the /// same major and minor version numbers. /// - /// A language version is greater then another if the former's major version + /// A language version is greater than another if the former's major version /// is greater than the latter's major version, or if they have /// the same major version and the former's minor version is greater than /// the latter's. @@ -406,3 +406,90 @@ abstract class InvalidLanguageVersion implements LanguageVersion { @override String toString(); } + +/// Relational operators for [LanguageVersion] that +/// compare valid versions with [LanguageVersion.compareTo]. +/// +/// If either operand is an [InvalidLanguageVersion], a [StateError] is thrown. +/// Versions should be verified as valid after parsing and before using them. +extension LanguageVersionRelationalOperators on LanguageVersion { + /// Whether this language version is less than [other]. + /// + /// If either version being compared is an [InvalidLanguageVersion], + /// a [StateError] is thrown. Verify versions are valid before comparing them. + /// + /// For details on how valid language versions are compared, + /// check out [LanguageVersion.compareTo]. + bool operator <(LanguageVersion other) { + // Throw an error if comparing as or with an invalid language version. + if (this is InvalidLanguageVersion) { + _throwThisInvalid(); + } else if (other is InvalidLanguageVersion) { + _throwOtherInvalid(); + } + + return compareTo(other) < 0; + } + + /// Whether this language version is less than or equal to [other]. + /// + /// If either version being compared is an [InvalidLanguageVersion], + /// a [StateError] is thrown. Verify versions are valid before comparing them. + /// + /// For details on how valid language versions are compared, + /// check out [LanguageVersion.compareTo]. + bool operator <=(LanguageVersion other) { + // Throw an error if comparing as or with an invalid language version. + if (this is InvalidLanguageVersion) { + _throwThisInvalid(); + } else if (other is InvalidLanguageVersion) { + _throwOtherInvalid(); + } + + return compareTo(other) <= 0; + } + + /// Whether this language version is greater than [other]. + /// + /// If either version being compared is an [InvalidLanguageVersion], + /// a [StateError] is thrown. Verify versions are valid before comparing them. + /// + /// For details on how valid language versions are compared, + /// check out [LanguageVersion.compareTo]. + bool operator >(LanguageVersion other) { + // Throw an error if comparing as or with an invalid language version. + if (this is InvalidLanguageVersion) { + _throwThisInvalid(); + } else if (other is InvalidLanguageVersion) { + _throwOtherInvalid(); + } + + return compareTo(other) > 0; + } + + /// Whether this language version is greater than or equal to [other]. + /// + /// If either version being compared is an [InvalidLanguageVersion], + /// a [StateError] is thrown. Verify versions are valid before comparing them. + /// + /// For details on how valid language versions are compared, + /// check out [LanguageVersion.compareTo]. + bool operator >=(LanguageVersion other) { + // Throw an error if comparing as or with an invalid language version. + if (this is InvalidLanguageVersion) { + _throwThisInvalid(); + } else if (other is InvalidLanguageVersion) { + _throwOtherInvalid(); + } + + return compareTo(other) >= 0; + } + + static Never _throwThisInvalid() => throw StateError( + 'Can\'t compare an invalid language version to another language version. ' + 'Verify language versions are valid after parsing.'); + + static Never _throwOtherInvalid() => throw StateError( + 'Can\'t compare a language version to an invalid language version. ' + 'Verify language versions are valid after parsing.'); +} diff --git a/pkgs/package_config/pubspec.yaml b/pkgs/package_config/pubspec.yaml index 1ac63bfc85..d3a1c7d382 100644 --- a/pkgs/package_config/pubspec.yaml +++ b/pkgs/package_config/pubspec.yaml @@ -1,5 +1,5 @@ name: package_config -version: 2.1.2-wip +version: 2.2.0-wip description: Support for reading and writing Dart Package Configuration files. repository: https://github.com/dart-lang/tools/tree/main/pkgs/package_config issue_tracker: https://github.com/dart-lang/tools/labels/package%3Apackage_config diff --git a/pkgs/package_config/test/package_config_impl_test.dart b/pkgs/package_config/test/package_config_impl_test.dart index fe1c98c905..fff82fa5e4 100644 --- a/pkgs/package_config/test/package_config_impl_test.dart +++ b/pkgs/package_config/test/package_config_impl_test.dart @@ -69,6 +69,86 @@ void main() { failParse('WhiteSpace 2', '1 .1'); failParse('WhiteSpace 3', '1. 1'); failParse('WhiteSpace 4', '1.1 '); + + test('compareTo valid', () { + var version = LanguageVersion(3, 5); + + for (var (otherVersion, matcher) in [ + (version, isZero), // Identical. + (LanguageVersion(3, 5), isZero), // Same major, same minor. + (LanguageVersion(3, 4), isPositive), // Same major, lower minor. + (LanguageVersion(3, 6), isNegative), // Same major, greater minor. + (LanguageVersion(2, 5), isPositive), // Lower major, same minor. + (LanguageVersion(2, 4), isPositive), // Lower major, lower minor. + (LanguageVersion(2, 6), isPositive), // Lower major, greater minor. + (LanguageVersion(4, 5), isNegative), // Greater major, same minor. + (LanguageVersion(4, 4), isNegative), // Greater major, lower minor. + (LanguageVersion(4, 6), isNegative), // Greater major, greater minor. + ]) { + expect(version.compareTo(otherVersion), matcher); + } + }); + + test('compareTo invalid', () { + var validVersion = LanguageVersion(3, 5); + var invalidVersion = LanguageVersion.parse('', onError: (_) {}); + + expect(validVersion.compareTo(invalidVersion), isPositive); + expect(invalidVersion.compareTo(validVersion), isNegative); + }); + + test('relational valid', () { + /// Test that the relational comparisons between two valid versions + /// match the results of `compareTo`. + void testComparisons( + LanguageVersion version, LanguageVersion otherVersion) { + expect(version == otherVersion, version.compareTo(otherVersion) == 0); + + expect(version < otherVersion, version.compareTo(otherVersion) < 0); + expect(version <= otherVersion, version.compareTo(otherVersion) <= 0); + + expect(version > otherVersion, version.compareTo(otherVersion) > 0); + expect(version >= otherVersion, version.compareTo(otherVersion) >= 0); + } + + var version = LanguageVersion(3, 5); + + // Check relational comparisons of a version to itself. + testComparisons(version, version); + + // Check relational comparisons of a version to versions with all + // possible combinations of minor and major versions that are + // the same, lower, and greater. + for (final major in [2, 3, 4]) { + for (final minor in [4, 5, 6]) { + testComparisons(version, LanguageVersion(major, minor)); + } + } + }); + + test('relational invalid', () { + void testComparisonsWithInvalid( + LanguageVersion version, + LanguageVersion otherVersion, + ) { + expect(version == otherVersion, identical(version, otherVersion)); + + expect(() => version < otherVersion, throwsA(isA())); + expect(() => version <= otherVersion, throwsA(isA())); + + expect(() => version > otherVersion, throwsA(isA())); + expect(() => version >= otherVersion, throwsA(isA())); + } + + var validVersion = LanguageVersion(3, 5); + var invalidVersion = LanguageVersion.parse('', onError: (_) {}); + var differentInvalidVersion = LanguageVersion.parse('-', onError: (_) {}); + + testComparisonsWithInvalid(validVersion, invalidVersion); + testComparisonsWithInvalid(invalidVersion, validVersion); + testComparisonsWithInvalid(invalidVersion, invalidVersion); + testComparisonsWithInvalid(invalidVersion, differentInvalidVersion); + }); }); group('Package', () {