Skip to content

Commit

Permalink
Merge branch 'main' into misc/pub-semver-remove-meta-dep
Browse files Browse the repository at this point in the history
  • Loading branch information
parlough committed Mar 6, 2025
2 parents 106b9c1 + 04667d7 commit c31f839
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ jobs:
uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main
with:
write-comments: false
permissions:
id-token: write
pull-requests: write
8 changes: 8 additions & 0 deletions pkgs/package_config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 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.

## 2.1.1

- Require Dart 3.4
Expand Down
7 changes: 6 additions & 1 deletion pkgs/package_config/lib/package_config_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
97 changes: 95 additions & 2 deletions pkgs/package_config/lib/src/package_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,15 @@ abstract class Package {
abstract class LanguageVersion implements Comparable<LanguageVersion> {
/// The maximal value allowed by [major] and [minor] values;
static const int maxValue = 0x7FFFFFFF;

/// Constructs a [LanguageVersion] with the specified
/// [major] and [minor] version numbers.
///
/// Both [major] and [minor] must be greater than or equal to 0
/// and less than or equal to [maxValue].
factory LanguageVersion(int major, int minor) {
RangeError.checkValueInInterval(major, 0, maxValue, 'major');
RangeError.checkValueInInterval(minor, 0, maxValue, 'major');
RangeError.checkValueInInterval(minor, 0, maxValue, 'minor');
return SimpleLanguageVersion(major, minor, null);
}

Expand Down Expand Up @@ -349,7 +355,7 @@ abstract class LanguageVersion implements Comparable<LanguageVersion> {
/// 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.
Expand Down Expand Up @@ -400,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.');
}
2 changes: 1 addition & 1 deletion pkgs/package_config/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: package_config
version: 2.1.1
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
Expand Down
96 changes: 94 additions & 2 deletions pkgs/package_config/test/package_config_impl_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,23 @@ void main() {
});

test('negative major', () {
expect(() => LanguageVersion(-1, 1), throwsArgumentError);
expect(
() => LanguageVersion(-1, 1),
throwsA(isA<RangeError>().having(
(e) => e.name,
'message',
contains('major'),
)));
});

test('negative minor', () {
expect(() => LanguageVersion(1, -1), throwsArgumentError);
expect(
() => LanguageVersion(1, -1),
throwsA(isA<RangeError>().having(
(e) => e.name,
'message',
contains('minor'),
)));
});

test('minimal parse', () {
Expand Down Expand Up @@ -57,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<StateError>()));
expect(() => version <= otherVersion, throwsA(isA<StateError>()));

expect(() => version > otherVersion, throwsA(isA<StateError>()));
expect(() => version >= otherVersion, throwsA(isA<StateError>()));
}

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', () {
Expand Down
7 changes: 6 additions & 1 deletion pkgs/pub_semver/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
## 2.2.0-wip
## 2.2.0

- Remove dependency on `package:meta`.
- Mark `Version` class as `final` instead of with `@sealed`.
- Clarify that the lists returned by
the `preRelease` and `build` properties of `Version` and
the `ranges` property of `VersionUnion` should not be modified.
- Note that `VersionConstraint.any` and `VersionConstraint.empty` static fields
shouldn't be reassigned and will be made `final` in a future release.

## 2.1.5

Expand Down
10 changes: 8 additions & 2 deletions pkgs/pub_semver/lib/src/version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,17 @@ final class Version implements VersionConstraint, VersionRange {
/// This is split into a list of components, each of which may be either a
/// string or a non-negative integer. It may also be empty, indicating that
/// this version has no pre-release identifier.
///
/// **Note:** The returned list shouldn't be modified.
final List<Object> preRelease;

/// The build identifier: "foo" in "1.2.3+foo".
///
/// This is split into a list of components, each of which may be either a
/// string or a non-negative integer. It may also be empty, indicating that
/// this version has no build identifier.
///
/// **Note:** The returned list shouldn't be modified.
final List<Object> build;

/// The original string representation of the version number.
Expand All @@ -94,8 +98,10 @@ final class Version implements VersionConstraint, VersionRange {

Version._(this.major, this.minor, this.patch, String? preRelease,
String? build, this._text)
: preRelease = preRelease == null ? <Object>[] : _splitParts(preRelease),
build = build == null ? [] : _splitParts(build) {
: preRelease = preRelease == null || preRelease.isEmpty
? []
: _splitParts(preRelease),
build = build == null || build.isEmpty ? [] : _splitParts(build) {
if (major < 0) throw ArgumentError('Major version must be non-negative.');
if (minor < 0) throw ArgumentError('Minor version must be non-negative.');
if (patch < 0) throw ArgumentError('Patch version must be non-negative.');
Expand Down
6 changes: 6 additions & 0 deletions pkgs/pub_semver/lib/src/version_constraint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ import 'version_union.dart';
/// version.
abstract class VersionConstraint {
/// A [VersionConstraint] that allows all versions.
///
/// **Note:** This property shouldn't be reassigned.
/// It will be made `final` in a future release.
static VersionConstraint any = VersionRange();

/// A [VersionConstraint] that allows no versions -- the empty set.
///
/// **Note:** This property shouldn't be reassigned.
/// It will be made `final` in a future release.
static VersionConstraint empty = const _EmptyVersion();

/// Parses a version constraint.
Expand Down
2 changes: 2 additions & 0 deletions pkgs/pub_semver/lib/src/version_union.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class VersionUnion implements VersionConstraint {
/// * Its contents are disjoint and non-adjacent. In other words, for any two
/// constraints next to each other in the list, there's some version between
/// those constraints that they don't match.
///
/// **Note:** The returned list shouldn't be modified.
final List<VersionRange> ranges;

@override
Expand Down
2 changes: 1 addition & 1 deletion pkgs/pub_semver/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: pub_semver
version: 2.2.0-wip
version: 2.2.0
description: >-
Versions and version constraints implementing pub's versioning policy. This
is very similar to vanilla semver, with a few corner cases.
Expand Down

0 comments on commit c31f839

Please sign in to comment.