Created
April 4, 2021 16:35
-
-
Save nythrox/f2f59bfdefc22e3ae3c2ac05a0cf37a3 to your computer and use it in GitHub Desktop.
Automatic Immutability in Dart using noSuchMethod() with no build_runner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
main() { | |
setupImmutable(() => User()); | |
final user1 = create(User() | |
..name = "uwu" | |
..age = 100); | |
final user2 = copy(user1..name = "owo"..child = User()); | |
final user3 = copy(user2..name = "eheh"); | |
} | |
abstract class _User { | |
String name; | |
int age; | |
User child; | |
} | |
class User extends Immutable<User> implements _User {} | |
class Change { | |
final dynamic key; | |
final dynamic value; | |
const Change(this.key, this.value); | |
} | |
final instanciations = <Type, dynamic Function()>{}; | |
void setupImmutable<T>(T Function() setup) { | |
instanciations[T] = setup; | |
} | |
T instanciate<T>(Type type) { | |
return instanciations[type](); | |
} | |
class Immutable<T extends Immutable<dynamic>> { | |
Map<String, dynamic> _values = {}; | |
List<Change> _changes = []; | |
@override | |
noSuchMethod(Invocation invocation) { | |
print(invocation.runtimeType); | |
print(invocation.memberName); | |
final name = getName(invocation.memberName); | |
if (invocation.isGetter) { | |
return _values[name]; | |
} | |
if (invocation.isSetter) { | |
_changes.add(Change(name, invocation.positionalArguments.first)); | |
return; | |
} | |
if (invocation.isMethod) { | |
return Function.apply( | |
_values[name], | |
invocation.positionalArguments, | |
invocation.namedArguments, | |
); | |
} | |
} | |
// T merge(T other) {} | |
T copyChangesTo(T instance) { | |
instance._values = {..._values}; | |
_changes.forEach((change) { | |
instance._values[change.key] = change.value; | |
}); | |
return instance; | |
} | |
T copy<T extends Immutable>(T changed) { | |
return copyChangesTo(changed as dynamic) as dynamic; | |
} | |
T copyFrom<T extends Immutable>(T changed) { | |
return changed.copy(this) as dynamic; | |
} | |
// @override | |
// bool operator ==(Object other) { | |
// if (other is Immutable<T>) { | |
// return DeepCollectionEquality().equals(_values, other._values); | |
// } | |
// return false; | |
// } | |
// @override | |
// int get hashCode => DeepCollectionEquality().hash(_values); | |
} | |
T create<T extends Immutable>(T value) { | |
return value.copyChangesTo(value); | |
} | |
T copy<T extends Immutable>(T from) { | |
final newInstance = instanciate(T); | |
return newInstance.copyFrom(from); | |
} | |
final _create = create; | |
String getName(Symbol s) { | |
String text = s.toString().replaceFirst("Symbol", ""); | |
text = text.replaceAll("(", ""); | |
text = text.replaceAll(")", ""); | |
text = text.replaceAll("=", ""); | |
text = text.replaceAll('"', ""); | |
text = text.replaceAll('"', ""); | |
return text; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment