Skip to content

Commit

Permalink
feat: copyWith support for setting nullable fields to null (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel authored May 17, 2024
1 parent 59928ae commit aa330e5
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 23 deletions.
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,22 @@ import 'package:struct_annotation/struct_annotation.dart';
@Struct()
class Person {
final String name;
final int age;
}
void main() {
// 🪨 Create a const instance with required, name parameters.
const jane = Person(name: 'Jane', age: 42);
const dash = Person(name: 'Dash');
// 🖨️ Create copies of your object.
final john = jane.copyWith(name: 'John');
final sparky = dash.copyWith(name: () => 'Sparky');
// ✨ Human-readable string representation.
print(jane); // Person(name: Jane, age: 42)
print(john); // Person(name: John, age: 42)
print(dash); // Person(name: Dash)
print(sparky); // Person(name: Sparky)
// ☯️ Value equality comparisons.
print(jane == jane.copyWith()); // true
print(john == john.copyWith(age: 21)); // false
print(dash == dash.copyWith()); // true
print(dash == sparky); // false
}
```

Expand Down
Binary file removed assets/demo.mp4
Binary file not shown.
15 changes: 7 additions & 8 deletions example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@ import 'package:struct_annotation/struct_annotation.dart';
@Struct()
class Person {
final String name;
final int age;
}

void main() {
// 🪨 Create a const instance with required, name parameters.
const jane = Person(name: 'Jane', age: 42);
const dash = Person(name: 'Dash');

// 🖨️ Create copies of your object.
final john = jane.copyWith(name: 'John');
final sparky = dash.copyWith(name: () => 'Sparky');

// ✨ Human-readable string representation.
print(jane); // Person(name: Jane, age: 42)
print(john); // Person(name: John, age: 42)
print(dash); // Person(name: Dash)
print(sparky); // Person(name: Sparky)

// ☯️ Value equality comparisons.
print(jane == jane.copyWith()); // true
print(john == john.copyWith(age: 21)); // false
print(dash == dash.copyWith()); // true
print(dash == sparky); // false
}
6 changes: 3 additions & 3 deletions lib/src/struct_annotation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import 'package:macros/macros.dart';
/// print(dash); // Person(name: Dash)
///
/// // Generated `copyWith`
/// print(dash.copyWith(name: 'Sparky')); // Person(name: Sparky)
/// print(dash.copyWith(name: () => 'Sparky')); // Person(name: Sparky)
///
/// // Generated `hashCode` and `operator==` for value based equality.
/// print(dash == Person(name: 'Dash')); // true
Expand Down Expand Up @@ -153,7 +153,7 @@ macro class Struct with _Shared implements ClassDeclarationsMacro, ClassDefiniti
[
'external ${clazz.identifier.name} copyWith({',
for (final field in fields)
...[field.type!.identifier.name, '? ', field.identifier.name, ',']
...[field.type!.identifier.name, if(field.type!.isNullable) '?', ' Function()? ', field.identifier.name, ',']
,'});',
],
),
Expand Down Expand Up @@ -330,7 +330,7 @@ macro class Struct with _Shared implements ClassDeclarationsMacro, ClassDefiniti
clazzName,
'(',
for (final field in fields)
...[field.identifier.name,': ', field.identifier.name, ' ?? ', 'this.',field.identifier.name, ','],
...[field.identifier.name, ': ', field.identifier.name, '!= null ? ',field.identifier.name, '.call()', ' : this.',field.identifier.name, ','],
');'
],
),
Expand Down
30 changes: 25 additions & 5 deletions test/src/struct_annotation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ void main() {
});

test('hashCode', () {
expect(const EmptyClass().hashCode, equals(const EmptyClass().hashCode));
expect(
const EmptyClass().hashCode,
equals(const EmptyClass().hashCode),
);
});

test('operator==', () {
Expand Down Expand Up @@ -53,7 +56,7 @@ void main() {

test('copyWith', () {
expect(instance.copyWith(), equals(instance));
final copy = instance.copyWith(value: 'bye');
final copy = instance.copyWith(value: () => 'bye');
expect(copy, isNot(equals(instance)));
expect(copy.value, equals('bye'));
});
Expand Down Expand Up @@ -102,7 +105,21 @@ void main() {
test('copyWith', () {
expect(nullInstance.copyWith(), equals(nullInstance));
expect(nonNullInstance.copyWith(), equals(nonNullInstance));
final copy = nonNullInstance.copyWith(value: 'bye');
expect(nullInstance.copyWith(value: null), equals(nullInstance));
expect(nonNullInstance.copyWith(value: null), equals(nonNullInstance));
expect(
nullInstance.copyWith(value: () => 'hello'),
isA<NullableStringFieldClass>().having(
(e) => e.value,
'value',
'hello',
),
);
expect(
nonNullInstance.copyWith(value: () => null),
isA<NullableStringFieldClass>().having((e) => e.value, 'value', isNull),
);
final copy = nonNullInstance.copyWith(value: () => 'bye');
expect(copy, isNot(equals(nonNullInstance)));
expect(copy.value, equals('bye'));
});
Expand All @@ -112,8 +129,11 @@ void main() {
nonNullInstance.hashCode,
equals(Object.hashAll([nonNullInstance.value])),
);

expect(nullInstance.hashCode, equals(Object.hashAll([nullInstance.value])));

expect(
nullInstance.hashCode,
equals(Object.hashAll([nullInstance.value])),
);
});

test('operator==', () {
Expand Down

0 comments on commit aa330e5

Please sign in to comment.