From 9482509ccb7b1bfc43975b6996b3db906da5b5ad Mon Sep 17 00:00:00 2001 From: Honza Bittner Date: Tue, 26 Mar 2024 08:21:59 +0100 Subject: [PATCH 1/3] Consider rename for target classes without ctors where setters are used --- examples/drift/lib/db.drift.dart | 10 ++-- examples/drift/lib/mappr.auto_mappr.dart | 1 - examples/example/lib/enum.auto_mappr.dart | 1 - .../example/lib/equatable.auto_mappr.dart | 1 - examples/example/lib/nested.auto_mappr.dart | 1 - examples/example/lib/nullable.auto_mappr.dart | 1 - examples/example/lib/rename.auto_mappr.dart | 1 - examples/freezed/analysis_options.yaml | 4 ++ .../lib/freezed_example.auto_mappr.dart | 1 - .../freezed/lib/freezed_example.freezed.dart | 37 ++++++++------- examples/injectable/lib/getit.config.dart | 3 +- examples/injectable/lib/mappr.auto_mappr.dart | 1 - .../lib/serializable.auto_mappr.dart | 1 - .../fixture/json_serializable.auto_mappr.dart | 1 - packages/auto_mappr/build.yaml | 2 +- .../example/lib/mappr.auto_mappr.dart | 1 - .../map_bodies/class_body_builder.dart | 46 +++++++++++++++---- .../lib/src/models/type_mapping.dart | 2 + .../test/integration/fixture/rename.dart | 26 +++++++++++ .../test/integration/rename_test.dart | 16 +++++++ 20 files changed, 111 insertions(+), 46 deletions(-) diff --git a/examples/drift/lib/db.drift.dart b/examples/drift/lib/db.drift.dart index 1880bdb6..8d0d128b 100644 --- a/examples/drift/lib/db.drift.dart +++ b/examples/drift/lib/db.drift.dart @@ -38,10 +38,9 @@ class $TodosTable extends Todos with TableInfo<$TodosTable, Todo> { @override List get $columns => [id, title, content, category]; @override - String get aliasedName => _alias ?? actualTableName; + String get aliasedName => _alias ?? 'todos'; @override - String get actualTableName => $name; - static const String $name = 'todos'; + String get actualTableName => 'todos'; @override VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { @@ -279,10 +278,9 @@ class $CategoriesTable extends Categories @override List get $columns => [id, description]; @override - String get aliasedName => _alias ?? actualTableName; + String get aliasedName => _alias ?? 'categories'; @override - String get actualTableName => $name; - static const String $name = 'categories'; + String get actualTableName => 'categories'; @override VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { diff --git a/examples/drift/lib/mappr.auto_mappr.dart b/examples/drift/lib/mappr.auto_mappr.dart index 6375e8b7..a31ec4b5 100644 --- a/examples/drift/lib/mappr.auto_mappr.dart +++ b/examples/drift/lib/mappr.auto_mappr.dart @@ -20,7 +20,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/example/lib/enum.auto_mappr.dart b/examples/example/lib/enum.auto_mappr.dart index 5dc2fc20..c1c90492 100644 --- a/examples/example/lib/enum.auto_mappr.dart +++ b/examples/example/lib/enum.auto_mappr.dart @@ -21,7 +21,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/example/lib/equatable.auto_mappr.dart b/examples/example/lib/equatable.auto_mappr.dart index aa4e99ba..018399df 100644 --- a/examples/example/lib/equatable.auto_mappr.dart +++ b/examples/example/lib/equatable.auto_mappr.dart @@ -19,7 +19,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/example/lib/nested.auto_mappr.dart b/examples/example/lib/nested.auto_mappr.dart index 7b57f462..7bc1de54 100644 --- a/examples/example/lib/nested.auto_mappr.dart +++ b/examples/example/lib/nested.auto_mappr.dart @@ -21,7 +21,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/example/lib/nullable.auto_mappr.dart b/examples/example/lib/nullable.auto_mappr.dart index fc567416..15855f48 100644 --- a/examples/example/lib/nullable.auto_mappr.dart +++ b/examples/example/lib/nullable.auto_mappr.dart @@ -20,7 +20,6 @@ class $Mappr implements _i2.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i2.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/example/lib/rename.auto_mappr.dart b/examples/example/lib/rename.auto_mappr.dart index 5796d6ce..c1c49985 100644 --- a/examples/example/lib/rename.auto_mappr.dart +++ b/examples/example/lib/rename.auto_mappr.dart @@ -19,7 +19,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/freezed/analysis_options.yaml b/examples/freezed/analysis_options.yaml index fa8a6489..eebb270f 100644 --- a/examples/freezed/analysis_options.yaml +++ b/examples/freezed/analysis_options.yaml @@ -1 +1,5 @@ include: package:netglade_analysis/lints.yaml + +analyzer: + exclude: + - "**/*.freezed.dart" diff --git a/examples/freezed/lib/freezed_example.auto_mappr.dart b/examples/freezed/lib/freezed_example.auto_mappr.dart index 2332c276..569c0bd9 100644 --- a/examples/freezed/lib/freezed_example.auto_mappr.dart +++ b/examples/freezed/lib/freezed_example.auto_mappr.dart @@ -19,7 +19,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/freezed/lib/freezed_example.freezed.dart b/examples/freezed/lib/freezed_example.freezed.dart index 7477ddfc..84a2c90d 100644 --- a/examples/freezed/lib/freezed_example.freezed.dart +++ b/examples/freezed/lib/freezed_example.freezed.dart @@ -12,7 +12,7 @@ part of 'freezed_example.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); /// @nodoc mixin _$UserInfoUnion { @@ -79,11 +79,11 @@ class _$UserInfoUnionCopyWithImpl<$Res, $Val extends UserInfoUnion> } /// @nodoc -abstract class _$$UserInfoImplCopyWith<$Res> +abstract class _$$UserInfoCopyWith<$Res> implements $UserInfoUnionCopyWith<$Res> { - factory _$$UserInfoImplCopyWith( - _$UserInfoImpl value, $Res Function(_$UserInfoImpl) then) = - __$$UserInfoImplCopyWithImpl<$Res>; + factory _$$UserInfoCopyWith( + _$UserInfo value, $Res Function(_$UserInfo) then) = + __$$UserInfoCopyWithImpl<$Res>; @override @useResult $Res call( @@ -94,11 +94,10 @@ abstract class _$$UserInfoImplCopyWith<$Res> } /// @nodoc -class __$$UserInfoImplCopyWithImpl<$Res> - extends _$UserInfoUnionCopyWithImpl<$Res, _$UserInfoImpl> - implements _$$UserInfoImplCopyWith<$Res> { - __$$UserInfoImplCopyWithImpl( - _$UserInfoImpl _value, $Res Function(_$UserInfoImpl) _then) +class __$$UserInfoCopyWithImpl<$Res> + extends _$UserInfoUnionCopyWithImpl<$Res, _$UserInfo> + implements _$$UserInfoCopyWith<$Res> { + __$$UserInfoCopyWithImpl(_$UserInfo _value, $Res Function(_$UserInfo) _then) : super(_value, _then); @pragma('vm:prefer-inline') @@ -109,7 +108,7 @@ class __$$UserInfoImplCopyWithImpl<$Res> Object? updatedAt = null, Object? primarySectionId = null, }) { - return _then(_$UserInfoImpl( + return _then(_$UserInfo( email: null == email ? _value.email : email // ignore: cast_nullable_to_non_nullable @@ -132,8 +131,8 @@ class __$$UserInfoImplCopyWithImpl<$Res> /// @nodoc -class _$UserInfoImpl implements UserInfo { - _$UserInfoImpl( +class _$UserInfo implements UserInfo { + _$UserInfo( {required this.email, required this.loginIdentifier, required this.updatedAt, @@ -155,10 +154,10 @@ class _$UserInfoImpl implements UserInfo { } @override - bool operator ==(Object other) { + bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$UserInfoImpl && + other is _$UserInfo && (identical(other.email, email) || other.email == email) && (identical(other.loginIdentifier, loginIdentifier) || other.loginIdentifier == loginIdentifier) && @@ -175,8 +174,8 @@ class _$UserInfoImpl implements UserInfo { @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$UserInfoImplCopyWith<_$UserInfoImpl> get copyWith => - __$$UserInfoImplCopyWithImpl<_$UserInfoImpl>(this, _$identity); + _$$UserInfoCopyWith<_$UserInfo> get copyWith => + __$$UserInfoCopyWithImpl<_$UserInfo>(this, _$identity); } abstract class UserInfo implements UserInfoUnion { @@ -184,7 +183,7 @@ abstract class UserInfo implements UserInfoUnion { {required final String email, required final String loginIdentifier, required final DateTime updatedAt, - final int primarySectionId}) = _$UserInfoImpl; + final int primarySectionId}) = _$UserInfo; @override String get email; @@ -196,6 +195,6 @@ abstract class UserInfo implements UserInfoUnion { int get primarySectionId; @override @JsonKey(ignore: true) - _$$UserInfoImplCopyWith<_$UserInfoImpl> get copyWith => + _$$UserInfoCopyWith<_$UserInfo> get copyWith => throw _privateConstructorUsedError; } diff --git a/examples/injectable/lib/getit.config.dart b/examples/injectable/lib/getit.config.dart index 9818a2fc..bd6ea92c 100644 --- a/examples/injectable/lib/getit.config.dart +++ b/examples/injectable/lib/getit.config.dart @@ -4,7 +4,8 @@ // InjectableConfigGenerator // ************************************************************************** -// ignore_for_file: type=lint +// ignore_for_file: unnecessary_lambdas +// ignore_for_file: lines_longer_than_80_chars // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes diff --git a/examples/injectable/lib/mappr.auto_mappr.dart b/examples/injectable/lib/mappr.auto_mappr.dart index c1e46640..eac09338 100644 --- a/examples/injectable/lib/mappr.auto_mappr.dart +++ b/examples/injectable/lib/mappr.auto_mappr.dart @@ -19,7 +19,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/json_serializable/lib/serializable.auto_mappr.dart b/examples/json_serializable/lib/serializable.auto_mappr.dart index d0c56839..9d58f240 100644 --- a/examples/json_serializable/lib/serializable.auto_mappr.dart +++ b/examples/json_serializable/lib/serializable.auto_mappr.dart @@ -20,7 +20,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart b/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart index d9c3b40a..9ebc5566 100644 --- a/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart +++ b/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart @@ -20,7 +20,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/packages/auto_mappr/build.yaml b/packages/auto_mappr/build.yaml index 9d87d9d3..515dde0d 100644 --- a/packages/auto_mappr/build.yaml +++ b/packages/auto_mappr/build.yaml @@ -5,7 +5,7 @@ targets: enabled: true generate_for: # include: - # - test/integration/fixture/type_converters/required_to_nullable.dart + # - test/integration/fixture/rename.dart exclude: - test/integration/error_fixture/** options: diff --git a/packages/auto_mappr/example/lib/mappr.auto_mappr.dart b/packages/auto_mappr/example/lib/mappr.auto_mappr.dart index b7978897..586a8e9a 100644 --- a/packages/auto_mappr/example/lib/mappr.auto_mappr.dart +++ b/packages/auto_mappr/example/lib/mappr.auto_mappr.dart @@ -20,7 +20,6 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} diff --git a/packages/auto_mappr/lib/src/builder/map_bodies/class_body_builder.dart b/packages/auto_mappr/lib/src/builder/map_bodies/class_body_builder.dart index 023d39f9..57053d58 100644 --- a/packages/auto_mappr/lib/src/builder/map_bodies/class_body_builder.dart +++ b/packages/auto_mappr/lib/src/builder/map_bodies/class_body_builder.dart @@ -217,30 +217,60 @@ class ClassBodyBuilder extends MapBodyBuilderBase { }) { final targetSetters = mapping.target.getAllSetters(); - final potentialSetterFields = sourceFields.keys.where((field) => !alreadyMapped.contains(field)).toList(); - final fields = potentialSetterFields - .map((key) => sourceFields[key]) + // Select only those who has not been mapped yet. + final potentialSetterFields = sourceFields.keys.where((field) { + final fieldMapping = mapping.tryGetFieldMappingFromFrom(field); + final from = fieldMapping?.field; + + // If the field has a rename, check that. + if (from != null) { + return !alreadyMapped.contains(from); + } + + // Or check it direclty. + return !alreadyMapped.contains(field); + }).toList(); + + final notMappedSourceFields = potentialSetterFields + .map((sourceKey) => sourceFields[sourceKey]) .whereNotNull() // Use only those that match. - .where((accessor) => targetSetters.any((targetAccessor) => targetAccessor.displayName == accessor.displayName)) + .where((accessor) { + final fieldMapping = mapping.tryGetFieldMappingFromFrom(accessor.name); + final from = fieldMapping?.field; + + return + // Contains rename. + from != null || + // Or matches directly. + targetSetters.any((targetAccessor) => targetAccessor.displayName == accessor.displayName); + }) // Skip ignored fields. .where((accessor) => !mapping.fieldShouldBeIgnored(accessor.displayName)) .toList(); - if (fields.isEmpty) { + if (notMappedSourceFields.isEmpty) { return constructorExpression; } final targetClassGetters = mapping.target.getAllGetters(); var cascadedAssignments = constructorExpression; - for (final sourceField in fields) { - final targetField = targetClassGetters.firstWhereOrNull((field) => field.displayName == sourceField.displayName); + for (final sourceField in notMappedSourceFields) { + // Is there a rename? + final fieldMapping = mapping.tryGetFieldMappingFromFrom(sourceField.name); + final from = fieldMapping?.field; + + // Rename or original field name. + final sourceFieldName = from ?? sourceField.name; + final targetField = targetClassGetters.firstWhereOrNull((field) => field.displayName == sourceFieldName); + + // final targetField = targetClassSetters.firstWhereOrNull((field) => field.displayName == sourceField.displayName); if (targetField == null) continue; // Assign result.X = model.X - cascadedAssignments = cascadedAssignments.cascade(sourceField.displayName).assign( + cascadedAssignments = cascadedAssignments.cascade(sourceFieldName).assign( ValueAssignmentBuilder( mapperConfig: mapperConfig, mapping: mapping, diff --git a/packages/auto_mappr/lib/src/models/type_mapping.dart b/packages/auto_mappr/lib/src/models/type_mapping.dart index 180859b6..edc02c68 100644 --- a/packages/auto_mappr/lib/src/models/type_mapping.dart +++ b/packages/auto_mappr/lib/src/models/type_mapping.dart @@ -65,6 +65,8 @@ class TypeMapping extends Equatable { FieldMapping? tryGetFieldMapping(String field) => fieldMappings.firstWhereOrNull((x) => x.field == field); + FieldMapping? tryGetFieldMappingFromFrom(String from) => fieldMappings.firstWhereOrNull((x) => x.from == from); + bool fieldShouldBeIgnored(String field) => hasFieldMapping(field) && (getFieldMapping(field)?.ignore ?? false); @override diff --git a/packages/auto_mappr/test/integration/fixture/rename.dart b/packages/auto_mappr/test/integration/fixture/rename.dart index 32bfa30d..6feaf926 100644 --- a/packages/auto_mappr/test/integration/fixture/rename.dart +++ b/packages/auto_mappr/test/integration/fixture/rename.dart @@ -57,6 +57,13 @@ import 'rename.auto_mappr.dart'; Field.custom('nameAndId', custom: Mappr.convertToNameAndIdNamed), ], ), + // no constructor with late fields + MapType( + fields: [ + Field('alpha', from: 'value'), + Field('beta', from: 'secondValue'), + ], + ), ]) class Mappr extends $Mappr { static String convertToNameAndIdPositional(CustomPositionalDto? dto) => '${dto?.name} #${dto?.id}'; @@ -304,3 +311,22 @@ class CustomNamedDto { const CustomNamedDto({required this.id, required this.name}); } + +// No constructor with late fields. + +class NoConstructorWithLateDto { + final int value; + final String secondValue; + + const NoConstructorWithLateDto({required this.value, required this.secondValue}); +} + +class NoConstructorWithLate with EquatableMixin { + // ignore: avoid-unassigned-late-fields, will be set using Mappr + late int alpha; + // ignore: avoid-unassigned-late-fields, will be set using Mappr + late String beta; + + @override + List get props => [alpha, beta]; +} diff --git a/packages/auto_mappr/test/integration/rename_test.dart b/packages/auto_mappr/test/integration/rename_test.dart index d1ee822a..bc325c8a 100644 --- a/packages/auto_mappr/test/integration/rename_test.dart +++ b/packages/auto_mappr/test/integration/rename_test.dart @@ -128,4 +128,20 @@ void main() { expect(converted, equals(const fixture.CustomNamed(nameAndId: 'monitor #11'))); }); }); + + group('with no constructor and with late fields', () { + test('Renamed when using positional parameters', () { + const dto = fixture.NoConstructorWithLateDto(value: 42, secondValue: 'colosseum'); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + fixture.NoConstructorWithLate() + ..alpha = 42 + ..beta = 'colosseum', + ), + ); + }); + }); } From f4ed06388096afcfe71e5f1afccf4995fd963cb5 Mon Sep 17 00:00:00 2001 From: Honza Bittner Date: Tue, 26 Mar 2024 08:32:08 +0100 Subject: [PATCH 2/3] Add changelog --- packages/auto_mappr/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/auto_mappr/CHANGELOG.md b/packages/auto_mappr/CHANGELOG.md index cc562661..79ea9248 100644 --- a/packages/auto_mappr/CHANGELOG.md +++ b/packages/auto_mappr/CHANGELOG.md @@ -1,7 +1,11 @@ [//]: # (## Unreleased) +## 2.4.0 +- Fix nested types ignores settings about null values (whenNull and ignoreNull). [#191](https://github.com/netglade/auto_mappr/pull/191) +- Fix not considering renaming ("from") when mapping using setters. [#192](https://github.com/netglade/auto_mappr/pull/192) + ## 2.3.0 -- Support zero argument function in custom mapping [#169](https://github.com/netglade/auto_mappr/pull/169) +- Support zero argument function in custom mapping. [#169](https://github.com/netglade/auto_mappr/pull/169) ## 2.2.0 - Improve error messages. [#147](https://github.com/netglade/auto_mappr/pull/147) From cc4fbed86931aabd3f0b3ef3b1f236daf978a87e Mon Sep 17 00:00:00 2001 From: Honza Bittner Date: Tue, 26 Mar 2024 08:33:11 +0100 Subject: [PATCH 3/3] Bump version --- packages/auto_mappr/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auto_mappr/pubspec.yaml b/packages/auto_mappr/pubspec.yaml index cc1adbed..3e8233dd 100644 --- a/packages/auto_mappr/pubspec.yaml +++ b/packages/auto_mappr/pubspec.yaml @@ -1,6 +1,6 @@ name: auto_mappr description: Code generation for mapping between different objects with ease. -version: 2.3.0 +version: 2.4.0 repository: https://github.com/netglade/auto_mappr issue_tracker: https://github.com/netglade/auto_mappr/issues screenshots: