Skip to content

Commit

Permalink
Limited union support
Browse files Browse the repository at this point in the history
  • Loading branch information
rrousselGit committed May 18, 2024
1 parent 64d0732 commit 4676207
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 56 deletions.
20 changes: 10 additions & 10 deletions packages/freezed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,16 @@ void main() {

## Features

| features | status | comment |
| --------------- | ------ | ------------------------------------------------------------------------ |
| copyWith | ok | |
| toString | ok | |
| ==/hashCode | ok | |
| unions | ok | Supported, but macros are a bit bugged with regards to class inheritance |
| json support | wip | |
| default values | ko | Requires macros to be more developed |
| deep copy | ko | Requires macros to be more developed |
| generic classes | ko | Requires macros to be more developed |
| features | status | comment |
| --------------- | ------ | ------------------------------------------------------------------------- |
| copyWith | ok | |
| toString | ok | |
| ==/hashCode | ok | |
| unions | wip | Supported, but macros are a bit bugged with regards to class inheritance. |
| json support | wip | |
| default values | ko | Requires macros to be more developed |
| deep copy | ko | Requires macros to be more developed |
| generic classes | ko | Requires macros to be more developed |

[build_runner]: https://pub.dev/packages/build_runner
[freezed]: https://pub.dartlang.org/packages/freezed
Expand Down
64 changes: 64 additions & 0 deletions packages/freezed/example/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// ignore_for_file: avoid_unused_constructor_parameters

import 'package:freezed/freezed.dart';

// The usual syntax, based on constructors.
// We no-longer use "factory" constructors.
@Freezed()
class Person {
Person(
String name, {
required int age,
});
}

// Alternatively, you can define classes using fields:

@Freezed()
class Person2 {
final String name;
final int age;
}

void main() {
var person = Person('John', age: 30);
person = person.copyWith(age: 31);

print(person.name); // John
print(person.age); // 31

// Person2 works the same!
var person2 = Person2(name: 'John', age: 30);
person2 = person2.copyWith(age: 31);
}

// ---- Advanced usage: Unions ----

class Engine {}

@Freezed()
sealed class Vehicle {
final Engine engine;
}

@Freezed()
class Plane implements Vehicle {
final Wing wing;

// Currently necessary due to augmentation libraries not being fully implemented.
// This won't be needed in the future.
@override
final Engine engine;
}

class Wing {}

@Freezed()
class Car implements Vehicle {
final Wheel wheel;

@override
final Engine engine;
}

class Wheel {}
69 changes: 46 additions & 23 deletions packages/freezed/lib/freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,15 @@ class _FreezedField {
required this.isRequired,
required this.isNamed,
required this.isInherited,
required this.needsDeclaration,
});

final TypeAnnotation type;
final String name;
final bool isNamed;
final bool isRequired;
final bool isInherited;
final bool needsDeclaration;

bool get isPositional => !isNamed;
bool get isOptional => !isRequired;
Expand Down Expand Up @@ -273,16 +275,21 @@ macro class Freezed implements ClassDeclarationsMacro {

if (targetConstructor == null) {
for (final field in await builder.fieldsOf(clazz)) {
if (field.hasInitializer) continue;

fields.add(
_FreezedField(
type: field.type,
isNamed: true,
name: field.identifier.name,
isRequired: true,
isInherited: false,
needsDeclaration: false,
),
);
}

// TODO once augmentation libraries work properly, automatically add fields coming from Freezed interfaces.
} else {
if (superCtor != null) {
builder.report(
Expand All @@ -304,6 +311,7 @@ macro class Freezed implements ClassDeclarationsMacro {
isNamed: field.isNamed,
isRequired: field.isRequired,
isInherited: false,
needsDeclaration: true,
),
);
}
Expand All @@ -320,14 +328,17 @@ macro class Freezed implements ClassDeclarationsMacro {
isNamed: field.isNamed,
isRequired: field.isRequired,
isInherited: true,
needsDeclaration: false,
),
);
}
}

builder.declareInLibrary(
DeclarationCode.fromParts([
'augment class ${clazz.identifier.name}',
'augment ',
if (clazz.hasSealed) 'sealed ',
'class ${clazz.identifier.name}',
if (clazz.superclass case final superClass?) ...[
' extends ',
superClass.code,
Expand All @@ -343,15 +354,15 @@ macro class Freezed implements ClassDeclarationsMacro {
hasConstructor: targetConstructor != null,
targetSuperCtor: targetSuperCtor,
);
if (targetConstructor != null) {
await _generateFields(fields, builder, clazz);
}
await _generateFields(fields, builder, clazz);
await _generateCopyWith(fields, builder, clazz);
await _generateToString(fields, builder, clazz);

// TODO generate equal/hash only if fields are final: https://github.com/dart-lang/sdk/issues/55764
await _generateEqual(fields, builder, clazz);
await _generateHash(fields, builder, clazz);
if (!clazz.hasSealed) {
// TODO generate equal/hash only if fields are final: https://github.com/dart-lang/sdk/issues/55764
await _generateEqual(fields, builder, clazz);
await _generateHash(fields, builder, clazz);
}

builder.declareInLibrary(
DeclarationCode.fromParts([
Expand All @@ -364,11 +375,8 @@ macro class Freezed implements ClassDeclarationsMacro {
ClassDeclaration clazz,
MemberDeclarationBuilder builder,
) async {
final superCtor = this.superCtor;
if (superCtor == null) return null;

final superClass = clazz.superclass;
if (superClass == null) {
if (superClass == null && superCtor != null) {
builder.report(
Diagnostic(
DiagnosticMessage(
Expand All @@ -379,15 +387,16 @@ macro class Freezed implements ClassDeclarationsMacro {
);
return null;
}
if (superClass == null) return null;

final superDeclaration =
await builder.typeDeclarationOf(superClass.identifier);
final superCtors = await builder.constructorsOf(superDeclaration);
final targetCtor = superCtors.firstWhereOrNull(
(e) => e.identifier.name == superCtor,
(e) => e.identifier.name == (superCtor ?? ''),
);

if (targetCtor == null) {
if (targetCtor == null && superCtor != null) {
builder.report(
Diagnostic(
DiagnosticMessage(
Expand Down Expand Up @@ -600,7 +609,7 @@ macro class Freezed implements ClassDeclarationsMacro {
else
'this.',
param.name,
', ',
',',
];
}

Expand Down Expand Up @@ -636,7 +645,8 @@ macro class Freezed implements ClassDeclarationsMacro {

return [
'super',
if (superCtor!.isNotEmpty) '.$superCtor',
if (targetSuperCtor.identifier.name.isNotEmpty)
'.${targetSuperCtor.identifier.name}',
'()',
];
}
Expand All @@ -649,7 +659,8 @@ macro class Freezed implements ClassDeclarationsMacro {
'(',
...parameters(),
')',
if (superCtor != null || (hasConstructor && fields.isNotEmpty)) ' : ',
if (targetSuperCtor != null || (hasConstructor && fields.isNotEmpty))
' : ',
if (fields.isNotEmpty && hasConstructor) ...await initializers(),
...await superParts(),
';',
Expand All @@ -664,13 +675,14 @@ macro class Freezed implements ClassDeclarationsMacro {
ClassDeclaration clazz,
) async {
final fieldsCode = DeclarationCode.fromParts([
for (final field in fields) ...[
'\n final ',
field.type.code,
' ',
field.name,
';',
],
for (final field in fields)
if (field.needsDeclaration) ...[
'\n final ',
field.type.code,
' ',
field.name,
';',
],
]);

builder.declareInLibrary(fieldsCode);
Expand Down Expand Up @@ -709,6 +721,17 @@ macro class Freezed implements ClassDeclarationsMacro {
)
.asDeclarationCode();

if (clazz.hasSealed) {
builder.declareInLibrary(
DeclarationCode.fromParts([
' ',
copyWithPrototype,
' get copyWith;',
]),
);
return;
}

builder.declareInLibrary(
await builder.parts(args: {
'CopyWithPrototype': copyWithPrototype,
Expand Down
45 changes: 45 additions & 0 deletions packages/freezed/test/integration/simple_fields.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:freezed/freezed.dart';

@Freezed()
class Simple {
final int foo;
final String bar;
final double initialized = 42;
}

@Freezed(constructor: 'custom')
class Named {
final int foo;
}

class SuperBase {
SuperBase.custom(int value) : value = value * 2;
final int value;
}

@Freezed(superCtor: 'custom')
class Super extends SuperBase {
final int foo;
}

// TODO uncomment once "super.field" works in augmentation constructors.
// @Freezed()
// sealed class ExtendsBase {
// final int a;
// }

// @Freezed()
// class Extends extends ExtendsBase {
// final int b;
// }

// TODO augmentations are confused once again
// @Freezed()
// sealed class ImplementsBase {
// final int a;
// }

// @Freezed()
// class Implements implements ImplementsBase {
// final int b;
// }
22 changes: 0 additions & 22 deletions packages/freezed/test/integration/simple_fields_test.dart

This file was deleted.

2 changes: 1 addition & 1 deletion packages/freezed/test/simple_fields_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:test/test.dart';

import 'integration/simple_fields_test.dart';
import 'integration/simple_fields.dart';

void main() {
group('Simple', () {
Expand Down

0 comments on commit 4676207

Please sign in to comment.