Skip to content

Commit

Permalink
Merge pull request #10 from gbassisp/feature/easy-comparable
Browse files Browse the repository at this point in the history
Feature - easy comparable
  • Loading branch information
gbassisp authored Aug 29, 2024
2 parents 93623e7 + 638d0a1 commit 2338642
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<!-- dart package changelog -->

## 1.3.0

- Created `EasyComparable`, `SpecificComparable` and `StrictComparable` to facilitate aligning `>` and `<` operators with `Comparable` interface

## 1.2.0

- Expose `string` constants as static class and as top-level values, e.g., `base62chars` and `base64chars`
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Lean Extensions

[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
[![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason)
[![License: MIT][license_badge]][license_link]

A Very Good Project created by Very Good CLI.
Expand Down
1 change: 1 addition & 0 deletions lib/dart_essentials.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/// Mostly inspired by python
library dart_essentials;

export 'src/interfaces.dart';
export 'src/range.dart' hide RangeFactory, safeRange;
export 'src/string_values.dart' show string;
export 'src/utils.dart';
59 changes: 58 additions & 1 deletion lib/src/interfaces.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import 'package:meta/meta.dart';
/// An easy way to implement [Comparable] in a way that [compareTo] aggrees
/// with [>] and [==] operators
@immutable
@internal
abstract class EasyComparable<T> implements Comparable<T> {
/// [>] operator to compare this and [other]. If this is overridden, the [<]
/// operator uses it to compute its value, but can also be overridden
///
/// **USE [StrictComparable] IF YOU ONLY WANT TO COMPARE TO [T]**
///
/// If you need to specify what Type is used on [>] and [<] operators, use
/// [SpecificComparable]
bool operator >(Object other);

@override
Expand All @@ -19,6 +23,11 @@ abstract class EasyComparable<T> implements Comparable<T> {

/// [<] operator to compare this and [other]. Overriding it is optional,
/// it has a default implementation that uses [==] and [>] operators
///
/// **USE [StrictComparable] IF YOU ONLY WANT TO COMPARE TO [T]**
///
/// If you need to specify what Type is used on [>] and [<] operators, use
/// [SpecificComparable]
bool operator <(Object other) => this != other && !(this > other);

@override
Expand All @@ -44,3 +53,51 @@ abstract class EasyComparable<T> implements Comparable<T> {
);
}
}

/// An easy way to implement [Comparable] in a way that [compareTo] aggrees
/// with [>] and [==] operators
///
/// This is a child class of [EasyComparable] that only takes the covariant
/// type [T] on the [>] and [<] operators
///
/// If you need to specify what Type is used on [>] and [<] operators, use
/// [SpecificComparable]
abstract class StrictComparable<T extends Object>
extends SpecificComparable<T, T> {}

/// An easy way to implement [Comparable] in a way that [compareTo] aggrees
/// with [>] and [==] operators
///
/// This is a child class of [EasyComparable] that only takes the covariant
/// type [T] on the [>] and [<] operators
@immutable
abstract class SpecificComparable<T, U extends Object>
extends EasyComparable<T> {
/// [>] operator to compare this and [other]. If this is overridden, the [<]
/// operator uses it to compute its value, but can also be overridden
///
/// **USE [EasyComparable] IF YOU ONLY WANT TO COMPARE TO ANY [Object]**
///
/// If you need to specify what Type is used on [>] and [<] operators, use
/// [SpecificComparable]
@override
bool operator >(covariant U other);

@override
@mustBeOverridden
bool operator ==(Object other);

@override
@mustBeOverridden
int get hashCode;

/// [<] operator to compare this and [other]. Overriding it is optional,
/// it has a default implementation that uses [==] and [>] operators
///
/// **USE [EasyComparable] IF YOU ONLY WANT TO COMPARE TO ANY [Object]**
///
/// If you need to specify what Type is used on [>] and [<] operators, use
/// [SpecificComparable]
@override
bool operator <(covariant U other) => this != other && !(this > other);
}
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: lean_extensions
description: A collection of extension methods without flutter dependency. Also includes some python-like functionality and json converters for easy validation
version: 1.2.0
version: 1.3.0
repository: https://github.com/gbassisp/lean_extensions
homepage: https://github.com/gbassisp/lean_extensions

Expand All @@ -12,7 +12,7 @@ dependencies:
collection: ^1.0.0
english_numerals: ^1.0.0
json_annotation: ^4.1.0
meta: ^1.0.0
meta: ^1.9.0

dev_dependencies:
test:
Expand Down
104 changes: 99 additions & 5 deletions test/src/interfaces_test.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import 'package:lean_extensions/dart_essentials.dart';
import 'package:lean_extensions/src/extensions.dart';
import 'package:lean_extensions/src/interfaces.dart';
import 'package:test/test.dart';

import 'test_utils.dart';

class _IntComparable extends EasyComparable<_IntComparable> {
_IntComparable(this.v);

Expand All @@ -22,19 +23,112 @@ class _IntComparable extends EasyComparable<_IntComparable> {
int get hashCode => v.hashCode;
}

class _NullableComparable extends EasyComparable<_NullableComparable?> {
_NullableComparable(this.v);

final int v;

@override
bool operator ==(Object other) {
return other is _NullableComparable ? v == other.v : v == other.toInt();
}

@override
bool operator >(Object other) {
return other is _NullableComparable ? v > other.v : v > other.toInt();
}

@override
int get hashCode => v.hashCode;
}

class _InvalidComparable extends EasyComparable<_InvalidComparable> {
@override
bool operator ==(Object other) => false;

@override
bool operator >(Object other) => false;

@override
bool operator <(Object other) => false;

@override
int get hashCode => 0;
}

class _StrictInt extends StrictComparable<_StrictInt> {
_StrictInt(this.v);

final int v;

@override
bool operator ==(Object other) => other is _StrictInt && other.v == v;

@override
int get hashCode => v.hashCode;

@override
bool operator >(_StrictInt other) => v > other.v;
@override
bool operator <(_StrictInt other) => v < other.v;
}

void main() {
final ints = List.generate(100, (index) => index);
final stricts = ints.map((e) => _StrictInt(e)).toList();
final valids = ints.map((e) => _IntComparable(e)).toList();
group('EasyComparable abstract class', () {
final nullables = ints
.map<_NullableComparable?>((e) => _NullableComparable(e))
.toList()
..add(null);
final invalids = ints.map((e) => _InvalidComparable()).toList();
test('compareTo', () {
for (final _ in range(1000)) {
valids
..shuffle()
..sort();
final res = valids.map((e) => e.v);

expect(res, containsAllInOrder(ints));
}
});

test('invalid comparable', () {
// all operators < , > and == return false
for (final _ in range(1000)) {
invalids.shuffle();
expect(invalids.sort, throwsSomething);
}
});

test('null comparable', () {
for (final _ in range(1000)) {
nullables.shuffle();
expect(nullables.sort, throwsSomething);
}
});
});
group('StrictComparable abstract class', () {
test('compareTo', () {
final ints = List.generate(100, (index) => index);
final objs = ints.map((e) => _IntComparable(e)).toList();
for (final _ in range(1000)) {
objs
stricts
..shuffle()
..sort();
final res = objs.map((e) => e.v);
final res = valids.map((e) => e.v);

expect(res, containsAllInOrder(ints));
}
});

test('compiler warning', () {
// uncomment the following to get a compiler error. they throw
// argument_type_not_assignable compilation error, as part of
// StrictComparable which doesn't happen on EasyComparable
// expect(() => _StrictInt(1) > 0, throwsSomething);
// expect(() => _StrictInt(1) < 10, throwsSomething);
expect(() => _IntComparable(1) > 0, isNot(throwsSomething));
expect(() => _IntComparable(1) < 10, isNot(throwsSomething));
});
});
}

0 comments on commit 2338642

Please sign in to comment.