Skip to content

Commit 28efbb0

Browse files
committed
fix initialization of record mappers
1 parent 6d8bb03 commit 28efbb0

8 files changed

Lines changed: 239 additions & 46 deletions

File tree

packages/dart_mappable/test/initializer/init_package_test.init.dart

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dart_mappable/test/records/mappable_record_test.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ typedef Point =
1010
@MappableRecord()
1111
typedef Offset<T> = ({double x, T y});
1212

13+
@MappableRecord()
14+
typedef Rect = ({Point topLeft, Point bottomRight});
15+
1316
class RoundingHook extends MappingHook {
1417
const RoundingHook();
1518

@@ -66,5 +69,25 @@ void main() {
6669
expect(p, equals((x: 1, y: 2)));
6770
expect(encode(p), equals({'a': 1, 'y': 2}));
6871
});
72+
73+
test('can decode and encode nested record', () {
74+
var decode = MapperContainer.globals.fromMap;
75+
var encode = MapperContainer.globals.toMap;
76+
77+
RectMapper.ensureInitialized();
78+
79+
var r = decode<Rect>({
80+
'topLeft': {'a': 1.3, 'y': 2},
81+
'bottomRight': {'a': 3.7, 'y': 4},
82+
});
83+
expect(r, equals((topLeft: (x: 1, y: 2), bottomRight: (x: 4, y: 4))));
84+
expect(
85+
encode(r),
86+
equals({
87+
'topLeft': {'a': 1, 'y': 2},
88+
'bottomRight': {'a': 4, 'y': 4},
89+
}),
90+
);
91+
});
6992
});
7093
}

packages/dart_mappable/test/records/mappable_record_test.mapper.dart

Lines changed: 102 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dart_mappable_builder/lib/src/elements/class/alias_class_mapper_element.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
import 'package:analyzer/dart/element/element.dart';
42
import 'package:dart_mappable/dart_mappable.dart';
53

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import 'package:analyzer/dart/element/element.dart';
12
import 'package:analyzer/dart/element/type.dart';
23

4+
import '../../../mapper_group.dart';
35
import '../../record/target_record_mapper_element.dart';
46
import '../class_mapper_element.dart';
57

@@ -12,57 +14,75 @@ mixin LinkedElementsMixin on ClassMapperElement {
1214
linked.add('$prefix${target.mapperName}');
1315
}
1416

15-
void checkType(DartType t) {
16-
var e = t.element;
17-
var m = parent.getMapperForElement(e);
18-
if (m != null) {
19-
linked.add(
20-
'${parent.prefixOfElement(m.annotation.element)}${m.mapperName}',
21-
);
22-
}
17+
linked.addAll(
18+
findLinkedElements(
19+
params.map((p) => p.parameter.type),
20+
element.typeParameters,
21+
parent,
22+
),
23+
);
2324

24-
if (t is ParameterizedType) {
25-
for (var arg in t.typeArguments) {
26-
checkType(arg);
27-
}
28-
}
25+
return linked.toList();
26+
}();
27+
}
2928

30-
r:
31-
if (t is RecordType) {
32-
if (t.alias != null) {
33-
var m = parent.getMapperForElement(t.alias!.element);
34-
if (m != null && m is TargetRecordMapperElement) {
35-
linked.add(
36-
'${parent.prefixOfElement(m.annotation.element)}${m.mapperName}',
37-
);
38-
break r;
39-
}
40-
}
41-
var e = parent.records.get(t);
42-
if (e != null) {
43-
linked.add(e.mapperName);
44-
}
45-
for (var f in [...t.positionalFields, ...t.namedFields]) {
46-
checkType(f.type);
47-
}
48-
}
29+
List<String> findLinkedElements(
30+
Iterable<DartType> types,
31+
List<TypeParameterElement> typeParams,
32+
MapperElementGroup parent,
33+
) {
34+
var linked = <String>{};
35+
36+
void checkType(DartType t) {
37+
var e = t.element;
38+
var m = parent.getMapperForElement(e);
39+
if (m != null) {
40+
linked.add(
41+
'${parent.prefixOfElement(m.annotation.element)}${m.mapperName}',
42+
);
4943
}
5044

51-
for (var param in params) {
52-
checkType(param.parameter.type);
45+
if (t is ParameterizedType) {
46+
for (var arg in t.typeArguments) {
47+
checkType(arg);
48+
}
5349
}
5450

55-
for (var param in element.typeParameters) {
56-
if (param.bound != null) {
57-
var m = parent.getMapperForElement(param.bound!.element);
58-
if (m is ClassMapperElement) {
51+
r:
52+
if (t is RecordType) {
53+
if (t.alias != null) {
54+
var m = parent.getMapperForElement(t.alias!.element);
55+
if (m != null && m is TargetRecordMapperElement) {
5956
linked.add(
6057
'${parent.prefixOfElement(m.annotation.element)}${m.mapperName}',
6158
);
59+
break r;
6260
}
6361
}
62+
var e = parent.records.get(t);
63+
if (e != null) {
64+
linked.add(e.mapperName);
65+
}
66+
for (var f in [...t.positionalFields, ...t.namedFields]) {
67+
checkType(f.type);
68+
}
6469
}
70+
}
6571

66-
return linked.toList();
67-
}();
72+
for (var type in types) {
73+
checkType(type);
74+
}
75+
76+
for (var param in typeParams) {
77+
if (param.bound != null) {
78+
var m = parent.getMapperForElement(param.bound!.element);
79+
if (m is ClassMapperElement) {
80+
linked.add(
81+
'${parent.prefixOfElement(m.annotation.element)}${m.mapperName}',
82+
);
83+
}
84+
}
85+
}
86+
87+
return linked.toList();
6888
}

packages/dart_mappable_builder/lib/src/elements/record/record_mapper_element.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import 'package:analyzer/dart/element/element.dart';
22
import 'package:analyzer/dart/element/type.dart';
33
import 'package:collection/collection.dart';
44

5+
import '../class/mixins/linked_elements_mixin.dart';
6+
import '../field/record_mapper_field_element.dart';
57
import '../mapper_element.dart';
68

79
abstract class RecordMapperElement<T extends Element>
@@ -21,6 +23,19 @@ abstract class RecordMapperElement<T extends Element>
2123

2224
List<String>? get inheritedTypeArgs => null;
2325

26+
@override
27+
List<RecordMapperFieldElement> get fields;
28+
29+
late final List<String> linkedElements = () {
30+
return findLinkedElements(
31+
fields.map((f) => f.param.type),
32+
element is TypeParameterizedElement
33+
? (element as TypeParameterizedElement).typeParameters
34+
: [],
35+
parent,
36+
);
37+
}();
38+
2439
late String genericRecordDeclaration = () {
2540
var t = type.positionalFields
2641
.mapIndexed((i, f) => genericArgAt(i))

packages/dart_mappable_builder/lib/src/generators/record_mapper_generator.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ class RecordMapperGenerator extends MapperGenerator<RecordMapperElement> {
2323
if (_instance == null) {
2424
MapperContainer.globals.use(_instance = ${element.mapperName}._());
2525
MapperBase.addType(${element.genericTypeParamsDeclaration}(f) => f<${element.genericRecordDeclaration}>());
26+
''');
27+
28+
var linked = element.linkedElements;
29+
if (linked.isNotEmpty) {
30+
for (var l in linked) {
31+
output.write(' $l.ensureInitialized();\n');
32+
}
33+
}
34+
35+
output.write('''
2636
}
2737
return _instance!;
2838
}

packages/dart_mappable_builder/lib/src/mapper_group.dart

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,12 @@ class MapperElementGroup {
405405

406406
bool isMapper(Element e) {
407407
return (e is ClassElement && classChecker.hasAnnotationOf(e)) ||
408-
(e is EnumElement && enumChecker.hasAnnotationOf(e));
408+
(e is EnumElement && enumChecker.hasAnnotationOf(e) ||
409+
(e is TypeAliasElement &&
410+
((e.aliasedType.element is ClassElement &&
411+
classChecker.hasAnnotationOf(e)) ||
412+
(e.aliasedType is RecordType &&
413+
recordChecker.hasAnnotationOf(e)))));
409414
}
410415

411416
if (scope == InitializerScope.package ||
@@ -424,16 +429,29 @@ class MapperElementGroup {
424429
})
425430
.where((l) => l != null)
426431
.map(
427-
(lib) =>
428-
MapEntry(lib!, [...lib.classes, ...lib.enums].where(isMapper)),
432+
(lib) => MapEntry(
433+
lib!,
434+
<Element>[
435+
...lib.classes,
436+
...lib.enums,
437+
...lib.typeAliases,
438+
].where(isMapper),
439+
),
429440
)
430441
.where((e) => e.value.isNotEmpty)
431442
.toList();
432443
} else if (scope == InitializerScope.library) {
433444
var lib = await buildStep.inputLibrary;
434445

435446
return [
436-
MapEntry(lib, [...lib.classes, ...lib.enums].where(isMapper)),
447+
MapEntry(
448+
lib,
449+
<Element>[
450+
...lib.classes,
451+
...lib.enums,
452+
...lib.typeAliases,
453+
].where(isMapper),
454+
),
437455
];
438456
}
439457

0 commit comments

Comments
 (0)