Skip to content

Instantly share code, notes, and snippets.

@rhalff
Created April 25, 2020 15:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rhalff/5b809c2296e847f9a652c6a7cc009cd0 to your computer and use it in GitHub Desktop.
Save rhalff/5b809c2296e847f9a652c6a7cc009cd0 to your computer and use it in GitHub Desktop.
Equatable Annotation
import 'package:collection/collection.dart';
import 'package:meta/meta.dart' show immutable;
//////////////////////////////////////////////
// Public Api
//////////////////////////////////////////////
@immutable
@equatableProps
class SomeObject extends $_SomeObjectEquatable {
final int one;
final int two;
final int three;
final int four;
final int five;
const SomeObject({
this.one,
this.two,
this.three,
this.four,
this.five,
});
}
/////////////////////////////////////////
// Main test.
/////////////////////////////////////////
void main() {
var a = SomeObject(one: 1, two: 2, three: 3);
print(a.props);
var b = SomeObject(one: 1, two: 2, three: 3);
print(b.props);
var c = SomeObject(one: 2, two: 2, three: 3, four: 4, five: 5);
print(c.props);
var d = SomeObject(one: 2, two: 2, three: 3, four: 4, five: 5);
print(d.props);
print(a == b);
print(a == c);
print(b == c);
print(c == d);
}
///////////////////////////////////////////////////
// Annotation
// (should also take some params to select props)
///////////////////////////////////////////////////
class EquatableProps {
const EquatableProps();
}
const equatableProps = EquatableProps();
/////////////////////////////////////////
// simple_object.g.dart (generated code)
/////////////////////////////////////////
class $_SomeObjectEquatable extends $_EquatableBase {
const $_SomeObjectEquatable();
List<Object> getProps(o) => [
o.one,
o.two,
o.three,
o.four,
o.five,
];
}
////////////////////////////////////
// equatable_base.dart
////////////////////////////////////
abstract class $_EquatableBase {
const $_EquatableBase();
bool get stringify => false;
List<Object> getProps(dynamic o); // => [];
static bool compare(dynamic _this, Object other) =>
identical(_this, other) ||
other is $_EquatableBase &&
_this.runtimeType == other.runtimeType &&
equals(_this.props, other.props);
@override
String toString() => stringify
? mapPropsToString(runtimeType, getProps(this))
: '$runtimeType';
List<Object> get props => getProps(this);
@override
bool operator ==(Object other) => compare(this, other);
@override
int get hashCode => runtimeType.hashCode ^ mapPropsToHashCode(getProps(this));
}
///////////////////////////////
/// equatable_utils.dart
//////////////////////////////
/// Returns a `hashCode` for [props].
int mapPropsToHashCode(Iterable props) =>
_finish(props?.fold(0, _combine) ?? 0);
const DeepCollectionEquality _equality = DeepCollectionEquality();
/// Determines whether [list1] and [list2] are equal.
bool equals(List list1, List list2) {
if (identical(list1, list2)) return true;
if (list1 == null || list2 == null) return false;
final length = list1.length;
if (length != list2.length) return false;
for (var i = 0; i < length; i++) {
final unit1 = list1[i];
final unit2 = list2[i];
if (unit1 is Iterable || unit1 is Map) {
if (!_equality.equals(unit1, unit2)) return false;
} else if (unit1?.runtimeType != unit2?.runtimeType) {
return false;
} else if (unit1 != unit2) {
return false;
}
}
return true;
}
/// Jenkins Hash Functions
/// https://en.wikipedia.org/wiki/Jenkins_hash_function
int _combine(int hash, dynamic object) {
if (object is Map) {
object.forEach((key, value) {
hash = hash ^ _combine(hash, [key, value]);
});
return hash;
}
final objectHashCode =
object is Iterable ? mapPropsToHashCode(object) : object.hashCode;
hash = 0x1fffffff & (hash + objectHashCode);
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
int _finish(int hash) {
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
/// Returns a string for [props].
String mapPropsToString(Type runtimeType, List<Object> props) =>
'$runtimeType${props?.map((prop) => prop?.toString() ?? '') ?? '()'}';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment