Skip to content

Commit

Permalink
Merge pull request #141 from yumemi-inc/improve/remove-pub_semver-pac…
Browse files Browse the repository at this point in the history
…kage
  • Loading branch information
blendthink authored Jan 10, 2025
2 parents 22c14b1 + fabbf75 commit 0ff7095
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 97 deletions.
1 change: 1 addition & 0 deletions packages/yumemi_lints/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Examples of version updates are as follows:

- Changed to be independent of the `path` package.
- Changed to be independent of the `meta` package.
- Changed to be independent of the `pub_semver` package.

## 2.2.0

Expand Down
3 changes: 3 additions & 0 deletions packages/yumemi_lints/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ include: package:yumemi_lints/dart/2.17/recommended.yaml
linter:
rules:
avoid_print: false

# This project does not use the `meta` package, so it has been disabled.
avoid_equals_and_hash_code_on_mutable_classes: false
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'dart:io';
import 'dart:isolate';

import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';
import 'package:yumemi_lints/src/models/exceptions.dart';
import 'package:yumemi_lints/src/models/exit_status.dart';
import 'package:yumemi_lints/src/models/project_type.dart';
import 'package:yumemi_lints/src/models/version.dart';

class UpdateCommandService {
const UpdateCommandService();
Expand Down Expand Up @@ -52,7 +52,7 @@ class UpdateCommandService {
}

final includeLine =
'include: package:yumemi_lints/${projectType.name}/${compatibleVersion.excludePatchVersion}/recommended.yaml';
'include: package:yumemi_lints/${projectType.name}/$compatibleVersion/recommended.yaml';
_updateAnalysisOptionsFile(includeLine);
return ExitStatus.success;
}
Expand Down Expand Up @@ -110,7 +110,7 @@ class UpdateCommandService {
.listSync()
.whereType<Directory>()
.map(
(e) => Version.parse('${e.name}.0'),
(e) => Version.parse(e.name),
)
.toList();

Expand Down Expand Up @@ -179,7 +179,7 @@ class UpdateCommandService {
);
}

return extractVersion(flutterVersion as String);
return Version.parse(flutterVersion as String);
}

Version getDartVersion(File pubspecFile) {
Expand All @@ -193,34 +193,7 @@ class UpdateCommandService {
);
}

return extractVersion(dartVersion as String);
}

Version extractVersion(String versionString) {
if (versionString.contains('<') && !versionString.contains('>=')) {
throw const FormatException('Please specify the minimum version.');
}

RegExp regExp;
if (versionString.contains('>=')) {
regExp = RegExp(r'>=\s*(\d+\.\d+\.\d+)');
} else {
regExp = RegExp(r's*(\d+\.\d+\.\d+)');
}
final match = regExp.firstMatch(versionString);

if (match == null) {
throw const FormatException(
'The version of Dart or Flutter could not be found in pubspec.yaml. '
'Please ensure that '
'the version is correctly specified for Dart or Flutter.',
);
}

final version = Version.parse(match.group(1)!);

// For ease of comparison, patch version is fixed at 0.
return Version(version.major, version.minor, 0);
return Version.parse(dartVersion as String);
}

void _updateAnalysisOptionsFile(String includeLine) {
Expand Down Expand Up @@ -259,10 +232,6 @@ class UpdateCommandService {
}
}

extension _VersionExt on Version {
String get excludePatchVersion => '$major.$minor';
}

extension _ProjectTypeFormalName on ProjectType {
String get formalName {
switch (this) {
Expand Down
86 changes: 86 additions & 0 deletions packages/yumemi_lints/lib/src/models/version.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/// A parsed semantic version number. However, only the major and minor
/// versions are processed.
class Version implements Comparable<Version> {
const Version(this._major, this._minor)
: assert(_major >= 0, 'Major version must be non-negative.'),
assert(_minor >= 0, 'Minor version must be non-negative.');

factory Version.parse(String text) {
if (text.contains('<') && !text.contains('>=')) {
throw MinimumVersionMissingException(text);
}

final RegExp regExp;
if (text.contains('>=')) {
regExp = RegExp(r'>=\s*(\d+)\.(\d+)');
} else {
regExp = RegExp(r'\^?(\d+)\.(\d+)');
}
final match = regExp.firstMatch(text);

if (match == null) {
throw UnsupportedVersionFormatException(text);
}

final major = int.parse(match[1]!);
final minor = int.parse(match[2]!);
return Version(major, minor);
}

final int _major;
final int _minor;

@override
bool operator ==(Object other) =>
other is Version && _major == other._major && _minor == other._minor;

@override
int get hashCode => _major ^ _minor;

@override
int compareTo(Version other) {
if (_major != other._major) {
return _major.compareTo(other._major);
}
if (_minor != other._minor) {
return _minor.compareTo(other._minor);
}
return 0;
}

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
String toString() => '$_major.$_minor';
}

abstract class _VersionParseException extends FormatException {
const _VersionParseException({
required String text,
required String message,
}) : super(
'Could not parse `$text`. $message',
);
}

class MinimumVersionMissingException extends _VersionParseException {
const MinimumVersionMissingException(String text)
: super(
text: text,
message: 'The minimum version must be specified if the version '
'is in range format.',
);
}

class UnsupportedVersionFormatException extends _VersionParseException {
const UnsupportedVersionFormatException(String text)
: super(
text: text,
message:
'Only version formats matching the following regular expressions '
r'are supported: `>=\s*(\d+)\.(\d+)` or `\^?(\d+)\.(\d+)`.',
);
}
1 change: 0 additions & 1 deletion packages/yumemi_lints/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ environment:
dependencies:
args: ^2.3.1
file: ^6.1.4
pub_semver: ^2.1.4
yaml: ^3.1.1

dev_dependencies:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'package:file/memory.dart';
import 'package:mockito/mockito.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart';
import 'package:yumemi_lints/src/command_services/update_command_service.dart';
import 'package:yumemi_lints/src/models/exceptions.dart';
import 'package:yumemi_lints/src/models/project_type.dart';
import 'package:yumemi_lints/src/models/version.dart';

void main() {
const updateCommandService = UpdateCommandService();
Expand Down Expand Up @@ -64,65 +64,6 @@ environment:
});
});

group('UpdateCommandService.extractVersion', () {
// arrange
const versions = [
'^2.17.0',
'2.17.0',
'>=2.17.0 <4.0.0',
'<4.0.0 >=2.17.0',
'^2.17.1',
];

for (var i = 0; i < versions.length; i++) {
// act
final version = updateCommandService.extractVersion(versions[i]);

test(
'Successfully extract version '
'when input version is ${versions[i]}', () {
// assert
expect(
version,
Version(2, 17, 0),
);
});
}

// arrange
const versionErrorScenarios = [
{
'version': 'any',
'errorMessage': 'The version of Dart or Flutter could not be found '
'in pubspec.yaml. Please ensure that '
'the version is correctly specified for Dart or Flutter.',
},
{
'version': '<4.0.0',
'errorMessage': 'Please specify the minimum version.',
},
];

for (var i = 0; i < versionErrorScenarios.length; i++) {
test(
'Failure to extract version '
'when input version is ${versionErrorScenarios[i]['version']}', () {
// act, assert
expect(
() => updateCommandService
.extractVersion(versionErrorScenarios[i]['version']!),
throwsA(
isA<FormatException>().having(
(e) => e.message,
'errorMessage',
versionErrorScenarios[i]['errorMessage'],
),
),
);
});
}
});

group('UpdateCommandService.getCompatibleVersion', () {
// arrange
const projectType = ProjectType.dart;
Expand Down
78 changes: 78 additions & 0 deletions packages/yumemi_lints/test/models/version_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:test/test.dart';
import 'package:yumemi_lints/src/models/version.dart';

void main() {
group('Version.parse()', () {
// arrange
const successfulScenarios = [
{
'input': '^2.17.0',
'expected': Version(2, 17),
},
{
'input': '2.17.0',
'expected': Version(2, 17),
},
{
'input': '>=2.17.0 <4.0.0',
'expected': Version(2, 17),
},
{
'input': '<4.0.0 >=2.17.0',
'expected': Version(2, 17),
},
{
'input': '^2.17.1',
'expected': Version(2, 17),
},
];

for (final scenario in successfulScenarios) {
final input = scenario['input']! as String;
final expected = scenario['expected']! as Version;

test(
'The version is successfully parsed '
'when the input text is `$input`',
() {
// act
final actual = Version.parse(input);

// assert
expect(actual, expected);
},
);
}

// arrange
final failureScenarios = [
{
'input': '<4.0.0',
'expected': throwsA(isA<MinimumVersionMissingException>()),
},
{
'input': 'any',
'expected': throwsA(isA<UnsupportedVersionFormatException>()),
},
];

for (final scenario in failureScenarios) {
final input = scenario['input']! as String;
final expected = scenario['expected']! as Matcher;

test(
'The version fails to be parsed '
'when the input text is `$input`',
() {
// act
void actual() {
Version.parse(input);
}

// assert
expect(actual, expected);
},
);
}
});
}

0 comments on commit 0ff7095

Please sign in to comment.