diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 746b52cb..0f104c28 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -46,6 +46,11 @@ updates: directory: "/examples/freezed" schedule: interval: "weekly" + + - package-ecosystem: "pub" + directory: "/examples/injectable" + schedule: + interval: "weekly" - package-ecosystem: "pub" directory: "/examples/json_serializable" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 79d59391..fc5dadd6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dart-lang/setup-dart@v1 - uses: bluefireteam/melos-action@v2 diff --git a/examples/drift/.gitignore b/examples/drift/.gitignore index b94efff6..adce9480 100644 --- a/examples/drift/.gitignore +++ b/examples/drift/.gitignore @@ -11,3 +11,4 @@ pubspec.lock # Example !*.g.dart +!*.auto_mappr.dart diff --git a/examples/drift/lib/mappr.g.dart b/examples/drift/lib/mappr.auto_mappr.dart similarity index 81% rename from examples/drift/lib/mappr.g.dart rename to examples/drift/lib/mappr.auto_mappr.dart index 5b8a0208..a31ec4b5 100644 --- a/examples/drift/lib/mappr.g.dart +++ b/examples/drift/lib/mappr.auto_mappr.dart @@ -1,26 +1,26 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'mappr.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'db.dart' as _i2; +import 'mappr.dart' as _i3; /// {@template package:examples_drift/mappr.dart} /// Available mappings: /// - `Todo` → `TodoItem`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_drift/mappr.dart} @@ -28,13 +28,14 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.Todo>() || + sourceTypeOf == _typeOf<_i2.Todo?>()) && + (targetTypeOf == _typeOf<_i3.TodoItem>() || + targetTypeOf == _typeOf<_i3.TodoItem?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -50,7 +51,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -69,7 +70,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -85,7 +86,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -105,7 +106,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -121,7 +122,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -140,7 +141,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -156,7 +157,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -175,7 +176,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -190,25 +191,26 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.Todo>() || + sourceTypeOf == _typeOf<_i2.Todo?>()) && + (targetTypeOf == _typeOf<_i3.TodoItem>() || + targetTypeOf == _typeOf<_i3.TodoItem?>())) { if (canReturnNull && model == null) { return null; } - return (_map__Todo__To__TodoItem((model as Todo?)) as TARGET); + return (_map__i2$Todo_To__i3$TodoItem((model as _i2.Todo?)) as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - TodoItem _map__Todo__To__TodoItem(Todo? input) { + _i3.TodoItem _map__i2$Todo_To__i3$TodoItem(_i2.Todo? input) { final model = input; if (model == null) { throw Exception( r'Mapping Todo → TodoItem failed because Todo was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return TodoItem( + return _i3.TodoItem( id: model.id, title: model.title, ); diff --git a/examples/drift/lib/mappr.dart b/examples/drift/lib/mappr.dart index a996efc6..567df05a 100644 --- a/examples/drift/lib/mappr.dart +++ b/examples/drift/lib/mappr.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:examples_drift/db.dart'; -part 'mappr.g.dart'; +import 'package:examples_drift/mappr.auto_mappr.dart'; class TodoItem { final int id; diff --git a/examples/drift/pubspec.yaml b/examples/drift/pubspec.yaml index b642eea2..ffa45dd5 100644 --- a/examples/drift/pubspec.yaml +++ b/examples/drift/pubspec.yaml @@ -7,13 +7,13 @@ environment: sdk: ">=2.18.7 <4.0.0" dependencies: - auto_mappr_annotation: ^1.2.0 + auto_mappr_annotation: ^2.0.0 drift: ^2.7.0 equatable: ^2.0.5 dev_dependencies: - auto_mappr: ^1.6.0 + auto_mappr: ^2.0.0 build_runner: ^2.0.0 drift_dev: ^2.7.0 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/examples/example/.gitignore b/examples/example/.gitignore index b94efff6..adce9480 100644 --- a/examples/example/.gitignore +++ b/examples/example/.gitignore @@ -11,3 +11,4 @@ pubspec.lock # Example !*.g.dart +!*.auto_mappr.dart diff --git a/examples/example/lib/enum.g.dart b/examples/example/lib/enum.auto_mappr.dart similarity index 71% rename from examples/example/lib/enum.g.dart rename to examples/example/lib/enum.auto_mappr.dart index 6cdd3e9e..c1c90492 100644 --- a/examples/example/lib/enum.g.dart +++ b/examples/example/lib/enum.auto_mappr.dart @@ -1,16 +1,15 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'enum.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'enum.dart' as _i2; /// {@template package:examples_example/enum.dart} /// Available mappings: @@ -18,11 +17,11 @@ part of 'enum.dart'; /// - `Vehicle` → `Vehicle`. /// - `Vehicle` → `VehicleX`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_example/enum.dart} @@ -30,26 +29,26 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserType>() || + sourceTypeOf == _typeOf<_i2.UserType?>()) && + (targetTypeOf == _typeOf<_i2.PersonType>() || + targetTypeOf == _typeOf<_i2.PersonType?>())) { return true; } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.Vehicle>() || + sourceTypeOf == _typeOf<_i2.Vehicle?>()) && + (targetTypeOf == _typeOf<_i2.Vehicle>() || + targetTypeOf == _typeOf<_i2.Vehicle?>())) { return true; } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.Vehicle>() || + sourceTypeOf == _typeOf<_i2.Vehicle?>()) && + (targetTypeOf == _typeOf<_i2.VehicleX>() || + targetTypeOf == _typeOf<_i2.VehicleX?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -65,7 +64,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -84,7 +83,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -100,7 +99,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -120,7 +119,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -136,7 +135,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -155,7 +154,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -171,7 +170,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -190,7 +189,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -205,63 +204,66 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserType>() || + sourceTypeOf == _typeOf<_i2.UserType?>()) && + (targetTypeOf == _typeOf<_i2.PersonType>() || + targetTypeOf == _typeOf<_i2.PersonType?>())) { if (canReturnNull && model == null) { return null; } - return (_map__UserType__To__PersonType((model as UserType?)) as TARGET); + return (_map__i2$UserType_To__i2$PersonType((model as _i2.UserType?)) + as TARGET); } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.Vehicle>() || + sourceTypeOf == _typeOf<_i2.Vehicle?>()) && + (targetTypeOf == _typeOf<_i2.Vehicle>() || + targetTypeOf == _typeOf<_i2.Vehicle?>())) { if (canReturnNull && model == null) { return null; } - return (_map__Vehicle__To__Vehicle((model as Vehicle?)) as TARGET); + return (_map__i2$Vehicle_To__i2$Vehicle((model as _i2.Vehicle?)) + as TARGET); } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.Vehicle>() || + sourceTypeOf == _typeOf<_i2.Vehicle?>()) && + (targetTypeOf == _typeOf<_i2.VehicleX>() || + targetTypeOf == _typeOf<_i2.VehicleX?>())) { if (canReturnNull && model == null) { return null; } - return (_map__Vehicle__To__VehicleX((model as Vehicle?)) as TARGET); + return (_map__i2$Vehicle_To__i2$VehicleX((model as _i2.Vehicle?)) + as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - PersonType _map__UserType__To__PersonType(UserType? input) { + _i2.PersonType _map__i2$UserType_To__i2$PersonType(_i2.UserType? input) { final model = input; if (model == null) { throw Exception( r'Mapping UserType → PersonType failed because UserType was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return PersonType.values.firstWhere((x) => x.name == model.name); + return _i2.PersonType.values.firstWhere((x) => x.name == model.name); } - Vehicle _map__Vehicle__To__Vehicle(Vehicle? input) { + _i2.Vehicle _map__i2$Vehicle_To__i2$Vehicle(_i2.Vehicle? input) { final model = input; if (model == null) { throw Exception( r'Mapping Vehicle → Vehicle failed because Vehicle was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return Vehicle.values.firstWhere((x) => x.name == model.name); + return _i2.Vehicle.values.firstWhere((x) => x.name == model.name); } - VehicleX _map__Vehicle__To__VehicleX(Vehicle? input) { + _i2.VehicleX _map__i2$Vehicle_To__i2$VehicleX(_i2.Vehicle? input) { final model = input; if (model == null) { throw Exception( r'Mapping Vehicle → VehicleX failed because Vehicle was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return VehicleX.values.firstWhere((x) => x.name == model.name); + return _i2.VehicleX.values.firstWhere((x) => x.name == model.name); } } diff --git a/examples/example/lib/enum.dart b/examples/example/lib/enum.dart index 670a1438..ee44c3cb 100644 --- a/examples/example/lib/enum.dart +++ b/examples/example/lib/enum.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'enum.g.dart'; +import 'package:examples_example/enum.auto_mappr.dart'; enum UserType { student, admin, parent } diff --git a/examples/example/lib/equatable.g.dart b/examples/example/lib/equatable.auto_mappr.dart similarity index 82% rename from examples/example/lib/equatable.g.dart rename to examples/example/lib/equatable.auto_mappr.dart index d4ce6859..018399df 100644 --- a/examples/example/lib/equatable.g.dart +++ b/examples/example/lib/equatable.auto_mappr.dart @@ -1,26 +1,25 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'equatable.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'equatable.dart' as _i2; /// {@template package:examples_example/equatable.dart} /// Available mappings: /// - `UserDto` → `User`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_example/equatable.dart} @@ -28,13 +27,14 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -50,7 +50,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -69,7 +69,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -85,7 +85,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -105,7 +105,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -121,7 +121,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -140,7 +140,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -156,7 +156,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -175,7 +175,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -190,25 +190,26 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { if (canReturnNull && model == null) { return null; } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); + return (_map__i2$UserDto_To__i2$User((model as _i2.UserDto?)) as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - User _map__UserDto__To__User(UserDto? input) { + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return User( + return _i2.User( id: model.id, name: model.name, tag: null, diff --git a/examples/example/lib/equatable.dart b/examples/example/lib/equatable.dart index 29fee030..ac72afd4 100644 --- a/examples/example/lib/equatable.dart +++ b/examples/example/lib/equatable.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'equatable.g.dart'; +import 'package:examples_example/equatable.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/examples/example/lib/nested.g.dart b/examples/example/lib/nested.auto_mappr.dart similarity index 68% rename from examples/example/lib/nested.g.dart rename to examples/example/lib/nested.auto_mappr.dart index f50fa375..7bc1de54 100644 --- a/examples/example/lib/nested.g.dart +++ b/examples/example/lib/nested.auto_mappr.dart @@ -1,16 +1,15 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'nested.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'nested.dart' as _i2; /// {@template package:examples_example/nested.dart} /// Available mappings: @@ -18,11 +17,11 @@ part of 'nested.dart'; /// - `NestedDto` → `Nested`. /// - `NestedTagDto` → `NestedTag`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_example/nested.dart} @@ -30,25 +29,26 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { return true; } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.NestedDto>() || + sourceTypeOf == _typeOf<_i2.NestedDto?>()) && + (targetTypeOf == _typeOf<_i2.Nested>() || + targetTypeOf == _typeOf<_i2.Nested?>())) { return true; } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.NestedTagDto>() || + sourceTypeOf == _typeOf<_i2.NestedTagDto?>()) && + (targetTypeOf == _typeOf<_i2.NestedTag>() || + targetTypeOf == _typeOf<_i2.NestedTag?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -64,7 +64,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -83,7 +83,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -99,7 +99,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -119,7 +119,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -135,7 +135,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -154,7 +154,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -170,7 +170,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -189,7 +189,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -204,99 +204,105 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { if (canReturnNull && model == null) { return null; } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); + return (_map__i2$UserDto_To__i2$User((model as _i2.UserDto?)) as TARGET); } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.NestedDto>() || + sourceTypeOf == _typeOf<_i2.NestedDto?>()) && + (targetTypeOf == _typeOf<_i2.Nested>() || + targetTypeOf == _typeOf<_i2.Nested?>())) { if (canReturnNull && model == null) { return null; } - return (_map__NestedDto__To__Nested((model as NestedDto?)) as TARGET); + return (_map__i2$NestedDto_To__i2$Nested((model as _i2.NestedDto?)) + as TARGET); } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.NestedTagDto>() || + sourceTypeOf == _typeOf<_i2.NestedTagDto?>()) && + (targetTypeOf == _typeOf<_i2.NestedTag>() || + targetTypeOf == _typeOf<_i2.NestedTag?>())) { if (canReturnNull && model == null) { return null; } - return (_map__NestedTagDto__To__NestedTag((model as NestedTagDto?)) - as TARGET); + return (_map__i2$NestedTagDto_To__i2$NestedTag( + (model as _i2.NestedTagDto?)) as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - User _map__UserDto__To__User(UserDto? input) { + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return User( + return _i2.User( id: model.id, - name: _map__NestedDto__To__Nested(model.name), - nestedItems: - model.nestedItems.map(_map__NestedDto__To__Nested).toList(), + name: _map__i2$NestedDto_To__i2$Nested(model.name), + nestedItems: model.nestedItems + .map<_i2.Nested>((value) => _map__i2$NestedDto_To__i2$Nested(value)) + .toList(), nestedItemsNullable: model.nestedItemsNullable - ?.map(_map__NestedDto__To__Nested) + ?.map<_i2.Nested>( + (value) => _map__i2$NestedDto_To__i2$Nested(value)) .toList() ?? - [], + <_i2.Nested>[], nestedItemsNullable2: model.nestedItemsNullable2 - .map(_map__NestedDto__To__Nested) + .map<_i2.Nested>((value) => _map__i2$NestedDto_To__i2$Nested(value)) .toList(), itemsWithNullableItem: model.itemsWithNullableItem .whereNotNull() - .map(_map__NestedDto__To__Nested) + .map<_i2.Nested>((value) => _map__i2$NestedDto_To__i2$Nested(value)) .toList(), itemsWithNullableItem2: model.itemsWithNullableItem2 - .map(_map__NestedDto__To__Nested_Nullable) + .map<_i2.Nested?>( + (value) => _map__i2$NestedDto_To__i2$Nested_Nullable(value)) .toList(), tag: null, ); } - Nested _map__NestedDto__To__Nested(NestedDto? input) { + _i2.Nested _map__i2$NestedDto_To__i2$Nested(_i2.NestedDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping NestedDto → Nested failed because NestedDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return Nested( + return _i2.Nested( id: model.id, name: model.name, - tag: _map__NestedTagDto__To__NestedTag(model.tag), + tag: _map__i2$NestedTagDto_To__i2$NestedTag(model.tag), ); } - NestedTag _map__NestedTagDto__To__NestedTag(NestedTagDto? input) { + _i2.NestedTag _map__i2$NestedTagDto_To__i2$NestedTag( + _i2.NestedTagDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping NestedTagDto → NestedTag failed because NestedTagDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return NestedTag(); + return _i2.NestedTag(); } - Nested? _map__NestedDto__To__Nested_Nullable(NestedDto? input) { + _i2.Nested? _map__i2$NestedDto_To__i2$Nested_Nullable(_i2.NestedDto? input) { final model = input; if (model == null) { return null; } - return Nested( + return _i2.Nested( id: model.id, name: model.name, - tag: _map__NestedTagDto__To__NestedTag(model.tag), + tag: _map__i2$NestedTagDto_To__i2$NestedTag(model.tag), ); } } diff --git a/examples/example/lib/nested.dart b/examples/example/lib/nested.dart index 68a83b79..cbd79836 100644 --- a/examples/example/lib/nested.dart +++ b/examples/example/lib/nested.dart @@ -3,7 +3,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'nested.g.dart'; +import 'package:examples_example/nested.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/examples/example/lib/nullable.g.dart b/examples/example/lib/nullable.auto_mappr.dart similarity index 74% rename from examples/example/lib/nullable.g.dart rename to examples/example/lib/nullable.auto_mappr.dart index e2c9cf1f..15855f48 100644 --- a/examples/example/lib/nullable.g.dart +++ b/examples/example/lib/nullable.auto_mappr.dart @@ -1,27 +1,26 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'nullable.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i2; + +import 'nullable.dart' as _i1; /// {@template package:examples_example/nullable.dart} /// Available mappings: /// - `UserDto` → `User` -- With default value. /// - `NestedDto` → `Nested`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i2.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i2.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_example/nullable.dart} @@ -29,19 +28,20 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i1.UserDto>() || + sourceTypeOf == _typeOf<_i1.UserDto?>()) && + (targetTypeOf == _typeOf<_i1.User>() || + targetTypeOf == _typeOf<_i1.User?>())) { return true; } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i1.NestedDto>() || + sourceTypeOf == _typeOf<_i1.NestedDto?>()) && + (targetTypeOf == _typeOf<_i1.Nested>() || + targetTypeOf == _typeOf<_i1.Nested?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -57,7 +57,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -76,7 +76,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -92,7 +92,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -112,7 +112,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -128,7 +128,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -147,7 +147,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -163,7 +163,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -182,7 +182,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -197,60 +197,62 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i1.UserDto>() || + sourceTypeOf == _typeOf<_i1.UserDto?>()) && + (targetTypeOf == _typeOf<_i1.User>() || + targetTypeOf == _typeOf<_i1.User?>())) { if (canReturnNull && model == null) { - return (const User( + return (const _i1.User( id: 1, - tag: Nested( + tag: _i1.Nested( id: 1, name: r'default', ), ) as TARGET); } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); + return (_map__i1$UserDto_To__i1$User((model as _i1.UserDto?)) as TARGET); } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i1.NestedDto>() || + sourceTypeOf == _typeOf<_i1.NestedDto?>()) && + (targetTypeOf == _typeOf<_i1.Nested>() || + targetTypeOf == _typeOf<_i1.Nested?>())) { if (canReturnNull && model == null) { return null; } - return (_map__NestedDto__To__Nested((model as NestedDto?)) as TARGET); + return (_map__i1$NestedDto_To__i1$Nested((model as _i1.NestedDto?)) + as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - User _map__UserDto__To__User(UserDto? input) { + _i1.User _map__i1$UserDto_To__i1$User(_i1.UserDto? input) { final model = input; if (model == null) { - return const User( + return const _i1.User( id: 1, - tag: Nested( + tag: _i1.Nested( id: 1, name: r'default', ), ); } - return User( + return _i1.User( id: model.id, tag: model.tag == null - ? Mappr.defaultNested() - : _map__NestedDto__To__Nested(model.tag), - name: _map__NestedDto__To__Nested(model.name), + ? _i1.Mappr.defaultNested() + : _map__i1$NestedDto_To__i1$Nested(model.tag), + name: _map__i1$NestedDto_To__i1$Nested(model.name), ); } - Nested _map__NestedDto__To__Nested(NestedDto? input) { + _i1.Nested _map__i1$NestedDto_To__i1$Nested(_i1.NestedDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping NestedDto → Nested failed because NestedDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return Nested( + return _i1.Nested( id: model.id, name: model.name, ); diff --git a/examples/example/lib/nullable.dart b/examples/example/lib/nullable.dart index 42ee90e5..04eb4fc7 100644 --- a/examples/example/lib/nullable.dart +++ b/examples/example/lib/nullable.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'nullable.g.dart'; +import 'package:examples_example/nullable.auto_mappr.dart'; @AutoMappr([ MapType( diff --git a/examples/example/lib/rename.g.dart b/examples/example/lib/rename.auto_mappr.dart similarity index 82% rename from examples/example/lib/rename.g.dart rename to examples/example/lib/rename.auto_mappr.dart index 89de1b19..c1c49985 100644 --- a/examples/example/lib/rename.g.dart +++ b/examples/example/lib/rename.auto_mappr.dart @@ -1,26 +1,25 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'rename.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'rename.dart' as _i2; /// {@template package:examples_example/rename.dart} /// Available mappings: /// - `UserDto` → `User`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_example/rename.dart} @@ -28,13 +27,14 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -50,7 +50,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -69,7 +69,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -85,7 +85,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -105,7 +105,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -121,7 +121,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -140,7 +140,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -156,7 +156,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -175,7 +175,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -190,25 +190,26 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { if (canReturnNull && model == null) { return null; } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); + return (_map__i2$UserDto_To__i2$User((model as _i2.UserDto?)) as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - User _map__UserDto__To__User(UserDto? input) { + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return User( + return _i2.User( id: model.id, name: model.xname, ); diff --git a/examples/example/lib/rename.dart b/examples/example/lib/rename.dart index 9def8c16..59b1bf8c 100644 --- a/examples/example/lib/rename.dart +++ b/examples/example/lib/rename.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'rename.g.dart'; +import 'package:examples_example/rename.auto_mappr.dart'; @AutoMappr([ MapType( diff --git a/examples/example/pubspec.yaml b/examples/example/pubspec.yaml index 64aa0f2d..53669132 100644 --- a/examples/example/pubspec.yaml +++ b/examples/example/pubspec.yaml @@ -7,11 +7,11 @@ environment: sdk: ">=2.18.7 <4.0.0" dependencies: - auto_mappr_annotation: ^1.2.0 + auto_mappr_annotation: ^2.0.0 equatable: ^2.0.5 dev_dependencies: - auto_mappr: ^1.3.0 + auto_mappr: ^2.0.0 build_runner: ^2.0.0 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/examples/freezed/.gitignore b/examples/freezed/.gitignore index b94efff6..adce9480 100644 --- a/examples/freezed/.gitignore +++ b/examples/freezed/.gitignore @@ -11,3 +11,4 @@ pubspec.lock # Example !*.g.dart +!*.auto_mappr.dart diff --git a/examples/freezed/lib/freezed_example.g.dart b/examples/freezed/lib/freezed_example.auto_mappr.dart similarity index 81% rename from examples/freezed/lib/freezed_example.g.dart rename to examples/freezed/lib/freezed_example.auto_mappr.dart index 625fc49e..569c0bd9 100644 --- a/examples/freezed/lib/freezed_example.g.dart +++ b/examples/freezed/lib/freezed_example.auto_mappr.dart @@ -1,26 +1,25 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'freezed_example.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'freezed_example.dart' as _i2; /// {@template package:examples_freezed/freezed_example.dart} /// Available mappings: /// - `UserInfo` → `UserInfoCompanion`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_freezed/freezed_example.dart} @@ -28,14 +27,14 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserInfo>() || + sourceTypeOf == _typeOf<_i2.UserInfo?>()) && + (targetTypeOf == _typeOf<_i2.UserInfoCompanion>() || + targetTypeOf == _typeOf<_i2.UserInfoCompanion?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -51,7 +50,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -70,7 +69,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -86,7 +85,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -106,7 +105,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -122,7 +121,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -141,7 +140,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -157,7 +156,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -176,7 +175,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -191,27 +190,28 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserInfo>() || + sourceTypeOf == _typeOf<_i2.UserInfo?>()) && + (targetTypeOf == _typeOf<_i2.UserInfoCompanion>() || + targetTypeOf == _typeOf<_i2.UserInfoCompanion?>())) { if (canReturnNull && model == null) { return null; } - return (_map__UserInfo__To__UserInfoCompanion((model as UserInfo?)) - as TARGET); + return (_map__i2$UserInfo_To__i2$UserInfoCompanion( + (model as _i2.UserInfo?)) as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - UserInfoCompanion _map__UserInfo__To__UserInfoCompanion(UserInfo? input) { + _i2.UserInfoCompanion _map__i2$UserInfo_To__i2$UserInfoCompanion( + _i2.UserInfo? input) { final model = input; if (model == null) { throw Exception( r'Mapping UserInfo → UserInfoCompanion failed because UserInfo was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return UserInfoCompanion( + return _i2.UserInfoCompanion( email: model.email, loginIdentifier: model.loginIdentifier, updatedAt: model.updatedAt, diff --git a/examples/freezed/lib/freezed_example.dart b/examples/freezed/lib/freezed_example.dart index 9861f228..86270d48 100644 --- a/examples/freezed/lib/freezed_example.dart +++ b/examples/freezed/lib/freezed_example.dart @@ -1,8 +1,8 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:examples_freezed/freezed_example.auto_mappr.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'freezed_example.freezed.dart'; -part 'freezed_example.g.dart'; @freezed class UserInfoUnion with _$UserInfoUnion { diff --git a/examples/freezed/pubspec.yaml b/examples/freezed/pubspec.yaml index b9f6c438..35833e70 100644 --- a/examples/freezed/pubspec.yaml +++ b/examples/freezed/pubspec.yaml @@ -7,13 +7,13 @@ environment: sdk: ">=2.18.7 <4.0.0" dependencies: - auto_mappr_annotation: ^1.2.0 + auto_mappr_annotation: ^2.0.0 equatable: ^2.0.5 freezed_annotation: ^2.2.0 dev_dependencies: - auto_mappr: ^1.6.0 + auto_mappr: ^2.0.0 build_runner: ^2.0.0 freezed: ^2.3.2 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/examples/injectable/.gitignore b/examples/injectable/.gitignore index b94efff6..adce9480 100644 --- a/examples/injectable/.gitignore +++ b/examples/injectable/.gitignore @@ -11,3 +11,4 @@ pubspec.lock # Example !*.g.dart +!*.auto_mappr.dart diff --git a/examples/injectable/lib/mappr.g.dart b/examples/injectable/lib/mappr.auto_mappr.dart similarity index 82% rename from examples/injectable/lib/mappr.g.dart rename to examples/injectable/lib/mappr.auto_mappr.dart index 3cdd617f..eac09338 100644 --- a/examples/injectable/lib/mappr.g.dart +++ b/examples/injectable/lib/mappr.auto_mappr.dart @@ -1,26 +1,25 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'mappr.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'mappr.dart' as _i2; /// {@template package:examples_injectable/mappr.dart} /// Available mappings: /// - `UserDto` → `User`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro package:examples_injectable/mappr.dart} @@ -28,13 +27,14 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -50,7 +50,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -69,7 +69,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -85,7 +85,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -105,7 +105,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -121,7 +121,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -140,7 +140,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -156,7 +156,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -175,7 +175,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -190,25 +190,26 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { if (canReturnNull && model == null) { return null; } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); + return (_map__i2$UserDto_To__i2$User((model as _i2.UserDto?)) as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - User _map__UserDto__To__User(UserDto? input) { + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return User( + return _i2.User( id: model.id, name: model.name, ); diff --git a/examples/injectable/lib/mappr.dart b/examples/injectable/lib/mappr.dart index 696257ba..44acf6a1 100644 --- a/examples/injectable/lib/mappr.dart +++ b/examples/injectable/lib/mappr.dart @@ -1,8 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:examples_injectable/mappr.auto_mappr.dart'; import 'package:injectable/injectable.dart'; -part 'mappr.g.dart'; - class User { final int id; final String name; diff --git a/examples/injectable/pubspec.yaml b/examples/injectable/pubspec.yaml index 65cb9f49..60675b0e 100644 --- a/examples/injectable/pubspec.yaml +++ b/examples/injectable/pubspec.yaml @@ -7,14 +7,14 @@ environment: sdk: ">=2.18.7 <4.0.0" dependencies: - auto_mappr_annotation: ^1.2.0 + auto_mappr_annotation: ^2.0.0 equatable: ^2.0.5 get_it: ^7.3.0 injectable: ^2.1.1 dev_dependencies: - auto_mappr: ^1.6.0 + auto_mappr: ^2.0.0 build_runner: ^2.0.0 injectable_generator: ^2.1.5 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/examples/json_serializable/.gitignore b/examples/json_serializable/.gitignore index b94efff6..adce9480 100644 --- a/examples/json_serializable/.gitignore +++ b/examples/json_serializable/.gitignore @@ -11,3 +11,4 @@ pubspec.lock # Example !*.g.dart +!*.auto_mappr.dart diff --git a/examples/json_serializable/build.yaml b/examples/json_serializable/build.yaml index 0a0ad92b..1fd05e48 100644 --- a/examples/json_serializable/build.yaml +++ b/examples/json_serializable/build.yaml @@ -3,3 +3,5 @@ targets: builders: auto_mappr: enabled: true + json_serializable: + enabled: true diff --git a/examples/json_serializable/lib/serializable.auto_mappr.dart b/examples/json_serializable/lib/serializable.auto_mappr.dart new file mode 100644 index 00000000..9d58f240 --- /dev/null +++ b/examples/json_serializable/lib/serializable.auto_mappr.dart @@ -0,0 +1,245 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// AutoMapprGenerator +// ************************************************************************** + +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'serializable.dart' as _i2; + +/// {@template package:examples_json_serializable/serializable.dart} +/// Available mappings: +/// - `UserDto` → `User`. +/// - `ValueHolderDto` → `ValueHolder`. +/// {@endtemplate} +class $Mappr implements _i1.AutoMapprInterface { + const $Mappr(); + + Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; + + /// {@macro AutoMapprInterface:canConvert} + /// {@macro package:examples_json_serializable/serializable.dart} + @override + bool canConvert({bool recursive = true}) { + final sourceTypeOf = _typeOf(); + final targetTypeOf = _typeOf(); + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { + return true; + } + if ((sourceTypeOf == _typeOf<_i2.ValueHolderDto>() || + sourceTypeOf == _typeOf<_i2.ValueHolderDto?>()) && + (targetTypeOf == _typeOf<_i2.ValueHolder>() || + targetTypeOf == _typeOf<_i2.ValueHolder?>())) { + return true; + } + if (recursive) { + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return true; + } + } + } + return false; + } + + /// {@macro AutoMapprInterface:convert} + /// {@macro package:examples_json_serializable/serializable.dart} + @override + TARGET convert(SOURCE? model) { + if (canConvert(recursive: false)) { + return _convert(model)!; + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convert(model)!; + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// {@macro AutoMapprInterface:tryConvert} + /// {@macro package:examples_json_serializable/serializable.dart} + @override + TARGET? tryConvert(SOURCE? model) { + if (canConvert(recursive: false)) { + return _convert( + model, + canReturnNull: true, + ); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvert(model); + } + } + + return null; + } + + /// {@macro AutoMapprInterface:convertIterable} + /// {@macro package:examples_json_serializable/serializable.dart} + @override + Iterable convertIterable(Iterable model) { + if (canConvert(recursive: false)) { + return model.map((item) => _convert(item)!); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convertIterable(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Iterable. + /// + /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null + /// + /// {@macro package:examples_json_serializable/serializable.dart} + @override + Iterable tryConvertIterable( + Iterable model) { + if (canConvert(recursive: false)) { + return model.map((item) => _convert(item, canReturnNull: true)); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvertIterable(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// {@macro AutoMapprInterface:convertList} + /// {@macro package:examples_json_serializable/serializable.dart} + @override + List convertList(Iterable model) { + if (canConvert(recursive: false)) { + return convertIterable(model).toList(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convertList(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into List. + /// + /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null + /// + /// {@macro package:examples_json_serializable/serializable.dart} + @override + List tryConvertList(Iterable model) { + if (canConvert(recursive: false)) { + return tryConvertIterable(model).toList(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvertList(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// {@macro AutoMapprInterface:convertSet} + /// {@macro package:examples_json_serializable/serializable.dart} + @override + Set convertSet(Iterable model) { + if (canConvert(recursive: false)) { + return convertIterable(model).toSet(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convertSet(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Set. + /// + /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null + /// + /// {@macro package:examples_json_serializable/serializable.dart} + @override + Set tryConvertSet(Iterable model) { + if (canConvert(recursive: false)) { + return tryConvertIterable(model).toSet(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvertSet(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + TARGET? _convert( + SOURCE? model, { + bool canReturnNull = false, + }) { + final sourceTypeOf = _typeOf(); + final targetTypeOf = _typeOf(); + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { + if (canReturnNull && model == null) { + return null; + } + return (_map__i2$UserDto_To__i2$User((model as _i2.UserDto?)) as TARGET); + } + if ((sourceTypeOf == _typeOf<_i2.ValueHolderDto>() || + sourceTypeOf == _typeOf<_i2.ValueHolderDto?>()) && + (targetTypeOf == _typeOf<_i2.ValueHolder>() || + targetTypeOf == _typeOf<_i2.ValueHolder?>())) { + if (canReturnNull && model == null) { + return null; + } + return (_map__i2$ValueHolderDto_To__i2$ValueHolder( + (model as _i2.ValueHolderDto?)) as TARGET); + } + throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); + } + + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { + final model = input; + if (model == null) { + throw Exception( + r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' + r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); + } + return _i2.User( + firstName: model.firstName, + lastName: model.lastName, + ); + } + + _i2.ValueHolder _map__i2$ValueHolderDto_To__i2$ValueHolder( + _i2.ValueHolderDto? input) { + final model = input; + if (model == null) { + throw Exception( + r'Mapping ValueHolderDto → ValueHolder failed because ValueHolderDto was null, and no default value was provided. ' + r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); + } + return _i2.ValueHolder(model.json); + } +} diff --git a/examples/json_serializable/lib/serializable.dart b/examples/json_serializable/lib/serializable.dart index ac32698a..61325968 100644 --- a/examples/json_serializable/lib/serializable.dart +++ b/examples/json_serializable/lib/serializable.dart @@ -1,4 +1,5 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:examples_json_serializable/serializable.auto_mappr.dart'; import 'package:json_annotation/json_annotation.dart'; part 'serializable.g.dart'; diff --git a/examples/json_serializable/lib/serializable.g.dart b/examples/json_serializable/lib/serializable.g.dart index 69c429dc..532b7555 100644 --- a/examples/json_serializable/lib/serializable.g.dart +++ b/examples/json_serializable/lib/serializable.g.dart @@ -2,246 +2,6 @@ part of 'serializable.dart'; -// ************************************************************************** -// AutoMapprGenerator -// ************************************************************************** - -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings - -/// {@template package:examples_json_serializable/serializable.dart} -/// Available mappings: -/// - `UserDto` → `User`. -/// - `ValueHolderDto` → `ValueHolder`. -/// {@endtemplate} -class $Mappr implements AutoMapprInterface { - const $Mappr(); - - Type _typeOf() => T; - List get _modules => const []; - - /// {@macro AutoMapprInterface:canConvert} - /// {@macro package:examples_json_serializable/serializable.dart} - @override - bool canConvert({bool recursive = true}) { - final sourceTypeOf = _typeOf(); - final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { - return true; - } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { - return true; - } - if (recursive) { - for (final mappr in _modules) { - if (mappr.canConvert()) { - return true; - } - } - } - return false; - } - - /// {@macro AutoMapprInterface:convert} - /// {@macro package:examples_json_serializable/serializable.dart} - @override - TARGET convert(SOURCE? model) { - if (canConvert(recursive: false)) { - return _convert(model)!; - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convert(model)!; - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// {@macro AutoMapprInterface:tryConvert} - /// {@macro package:examples_json_serializable/serializable.dart} - @override - TARGET? tryConvert(SOURCE? model) { - if (canConvert(recursive: false)) { - return _convert( - model, - canReturnNull: true, - ); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvert(model); - } - } - - return null; - } - - /// {@macro AutoMapprInterface:convertIterable} - /// {@macro package:examples_json_serializable/serializable.dart} - @override - Iterable convertIterable(Iterable model) { - if (canConvert(recursive: false)) { - return model.map((item) => _convert(item)!); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convertIterable(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Iterable. - /// - /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null - /// - /// {@macro package:examples_json_serializable/serializable.dart} - @override - Iterable tryConvertIterable( - Iterable model) { - if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// {@macro AutoMapprInterface:convertList} - /// {@macro package:examples_json_serializable/serializable.dart} - @override - List convertList(Iterable model) { - if (canConvert(recursive: false)) { - return convertIterable(model).toList(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convertList(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into List. - /// - /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null - /// - /// {@macro package:examples_json_serializable/serializable.dart} - @override - List tryConvertList(Iterable model) { - if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvertList(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// {@macro AutoMapprInterface:convertSet} - /// {@macro package:examples_json_serializable/serializable.dart} - @override - Set convertSet(Iterable model) { - if (canConvert(recursive: false)) { - return convertIterable(model).toSet(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convertSet(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Set. - /// - /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null - /// - /// {@macro package:examples_json_serializable/serializable.dart} - @override - Set tryConvertSet(Iterable model) { - if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvertSet(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - TARGET? _convert( - SOURCE? model, { - bool canReturnNull = false, - }) { - final sourceTypeOf = _typeOf(); - final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { - if (canReturnNull && model == null) { - return null; - } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); - } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { - if (canReturnNull && model == null) { - return null; - } - return (_map__ValueHolderDto__To__ValueHolder((model as ValueHolderDto?)) - as TARGET); - } - throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); - } - - User _map__UserDto__To__User(UserDto? input) { - final model = input; - if (model == null) { - throw Exception( - r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' - r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); - } - return User( - firstName: model.firstName, - lastName: model.lastName, - ); - } - - ValueHolder _map__ValueHolderDto__To__ValueHolder(ValueHolderDto? input) { - final model = input; - if (model == null) { - throw Exception( - r'Mapping ValueHolderDto → ValueHolder failed because ValueHolderDto was null, and no default value was provided. ' - r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); - } - return ValueHolder(model.json); - } -} - // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** diff --git a/examples/json_serializable/pubspec.yaml b/examples/json_serializable/pubspec.yaml index ab88881e..15489698 100644 --- a/examples/json_serializable/pubspec.yaml +++ b/examples/json_serializable/pubspec.yaml @@ -7,12 +7,12 @@ environment: sdk: ">=2.18.7 <4.0.0" dependencies: - auto_mappr_annotation: ^1.2.0 + auto_mappr_annotation: ^2.0.0 json_annotation: ^4.8.1 dev_dependencies: - auto_mappr: ^1.6.0 + auto_mappr: ^2.0.0 build_runner: ^2.0.0 json_serializable: ^6.6.1 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart b/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart new file mode 100644 index 00000000..9ebc5566 --- /dev/null +++ b/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart @@ -0,0 +1,245 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// AutoMapprGenerator +// ************************************************************************** + +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'json_serializable.dart' as _i2; + +/// {@template asset:examples_json_serializable/test/fixture/json_serializable.dart} +/// Available mappings: +/// - `UserDto` → `User`. +/// - `ValueHolderDto` → `ValueHolder`. +/// {@endtemplate} +class $Mappr implements _i1.AutoMapprInterface { + const $Mappr(); + + Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; + + /// {@macro AutoMapprInterface:canConvert} + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + bool canConvert({bool recursive = true}) { + final sourceTypeOf = _typeOf(); + final targetTypeOf = _typeOf(); + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { + return true; + } + if ((sourceTypeOf == _typeOf<_i2.ValueHolderDto>() || + sourceTypeOf == _typeOf<_i2.ValueHolderDto?>()) && + (targetTypeOf == _typeOf<_i2.ValueHolder>() || + targetTypeOf == _typeOf<_i2.ValueHolder?>())) { + return true; + } + if (recursive) { + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return true; + } + } + } + return false; + } + + /// {@macro AutoMapprInterface:convert} + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + TARGET convert(SOURCE? model) { + if (canConvert(recursive: false)) { + return _convert(model)!; + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convert(model)!; + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// {@macro AutoMapprInterface:tryConvert} + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + TARGET? tryConvert(SOURCE? model) { + if (canConvert(recursive: false)) { + return _convert( + model, + canReturnNull: true, + ); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvert(model); + } + } + + return null; + } + + /// {@macro AutoMapprInterface:convertIterable} + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + Iterable convertIterable(Iterable model) { + if (canConvert(recursive: false)) { + return model.map((item) => _convert(item)!); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convertIterable(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Iterable. + /// + /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null + /// + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + Iterable tryConvertIterable( + Iterable model) { + if (canConvert(recursive: false)) { + return model.map((item) => _convert(item, canReturnNull: true)); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvertIterable(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// {@macro AutoMapprInterface:convertList} + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + List convertList(Iterable model) { + if (canConvert(recursive: false)) { + return convertIterable(model).toList(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convertList(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into List. + /// + /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null + /// + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + List tryConvertList(Iterable model) { + if (canConvert(recursive: false)) { + return tryConvertIterable(model).toList(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvertList(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// {@macro AutoMapprInterface:convertSet} + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + Set convertSet(Iterable model) { + if (canConvert(recursive: false)) { + return convertIterable(model).toSet(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.convertSet(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Set. + /// + /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null + /// + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + Set tryConvertSet(Iterable model) { + if (canConvert(recursive: false)) { + return tryConvertIterable(model).toSet(); + } + for (final mappr in _delegates) { + if (mappr.canConvert()) { + return mappr.tryConvertSet(model); + } + } + + throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); + } + + TARGET? _convert( + SOURCE? model, { + bool canReturnNull = false, + }) { + final sourceTypeOf = _typeOf(); + final targetTypeOf = _typeOf(); + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { + if (canReturnNull && model == null) { + return null; + } + return (_map__i2$UserDto_To__i2$User((model as _i2.UserDto?)) as TARGET); + } + if ((sourceTypeOf == _typeOf<_i2.ValueHolderDto>() || + sourceTypeOf == _typeOf<_i2.ValueHolderDto?>()) && + (targetTypeOf == _typeOf<_i2.ValueHolder>() || + targetTypeOf == _typeOf<_i2.ValueHolder?>())) { + if (canReturnNull && model == null) { + return null; + } + return (_map__i2$ValueHolderDto_To__i2$ValueHolder( + (model as _i2.ValueHolderDto?)) as TARGET); + } + throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); + } + + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { + final model = input; + if (model == null) { + throw Exception( + r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' + r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); + } + return _i2.User( + firstName: model.firstName, + lastName: model.lastName, + ); + } + + _i2.ValueHolder _map__i2$ValueHolderDto_To__i2$ValueHolder( + _i2.ValueHolderDto? input) { + final model = input; + if (model == null) { + throw Exception( + r'Mapping ValueHolderDto → ValueHolder failed because ValueHolderDto was null, and no default value was provided. ' + r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); + } + return _i2.ValueHolder(model.json); + } +} diff --git a/examples/json_serializable/test/fixture/json_serializable.dart b/examples/json_serializable/test/fixture/json_serializable.dart index 317e4ceb..01ecb731 100644 --- a/examples/json_serializable/test/fixture/json_serializable.dart +++ b/examples/json_serializable/test/fixture/json_serializable.dart @@ -1,6 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'json_serializable.auto_mappr.dart'; part 'json_serializable.g.dart'; @AutoMappr([ diff --git a/examples/json_serializable/test/fixture/json_serializable.g.dart b/examples/json_serializable/test/fixture/json_serializable.g.dart index 174c21ee..7445c5c6 100644 --- a/examples/json_serializable/test/fixture/json_serializable.g.dart +++ b/examples/json_serializable/test/fixture/json_serializable.g.dart @@ -2,246 +2,6 @@ part of 'json_serializable.dart'; -// ************************************************************************** -// AutoMapprGenerator -// ************************************************************************** - -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings - -/// {@template asset:examples_json_serializable/test/fixture/json_serializable.dart} -/// Available mappings: -/// - `UserDto` → `User`. -/// - `ValueHolderDto` → `ValueHolder`. -/// {@endtemplate} -class $Mappr implements AutoMapprInterface { - const $Mappr(); - - Type _typeOf() => T; - List get _modules => const []; - - /// {@macro AutoMapprInterface:canConvert} - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - bool canConvert({bool recursive = true}) { - final sourceTypeOf = _typeOf(); - final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { - return true; - } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { - return true; - } - if (recursive) { - for (final mappr in _modules) { - if (mappr.canConvert()) { - return true; - } - } - } - return false; - } - - /// {@macro AutoMapprInterface:convert} - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - TARGET convert(SOURCE? model) { - if (canConvert(recursive: false)) { - return _convert(model)!; - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convert(model)!; - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// {@macro AutoMapprInterface:tryConvert} - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - TARGET? tryConvert(SOURCE? model) { - if (canConvert(recursive: false)) { - return _convert( - model, - canReturnNull: true, - ); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvert(model); - } - } - - return null; - } - - /// {@macro AutoMapprInterface:convertIterable} - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - Iterable convertIterable(Iterable model) { - if (canConvert(recursive: false)) { - return model.map((item) => _convert(item)!); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convertIterable(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Iterable. - /// - /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null - /// - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - Iterable tryConvertIterable( - Iterable model) { - if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// {@macro AutoMapprInterface:convertList} - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - List convertList(Iterable model) { - if (canConvert(recursive: false)) { - return convertIterable(model).toList(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convertList(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into List. - /// - /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null - /// - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - List tryConvertList(Iterable model) { - if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvertList(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// {@macro AutoMapprInterface:convertSet} - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - Set convertSet(Iterable model) { - if (canConvert(recursive: false)) { - return convertIterable(model).toSet(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.convertSet(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Set. - /// - /// When an item in the source iterable is null, uses `whenSourceIsNull` if defined or null - /// - /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} - @override - Set tryConvertSet(Iterable model) { - if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); - } - for (final mappr in _modules) { - if (mappr.canConvert()) { - return mappr.tryConvertSet(model); - } - } - - throw Exception('No ${_typeOf()} -> ${_typeOf()} mapping.'); - } - - TARGET? _convert( - SOURCE? model, { - bool canReturnNull = false, - }) { - final sourceTypeOf = _typeOf(); - final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { - if (canReturnNull && model == null) { - return null; - } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); - } - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || - targetTypeOf == _typeOf())) { - if (canReturnNull && model == null) { - return null; - } - return (_map__ValueHolderDto__To__ValueHolder((model as ValueHolderDto?)) - as TARGET); - } - throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); - } - - User _map__UserDto__To__User(UserDto? input) { - final model = input; - if (model == null) { - throw Exception( - r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' - r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); - } - return User( - firstName: model.firstName, - lastName: model.lastName, - ); - } - - ValueHolder _map__ValueHolderDto__To__ValueHolder(ValueHolderDto? input) { - final model = input; - if (model == null) { - throw Exception( - r'Mapping ValueHolderDto → ValueHolder failed because ValueHolderDto was null, and no default value was provided. ' - r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); - } - return ValueHolder(model.json); - } -} - // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** diff --git a/melos.yaml b/melos.yaml index 0627e6ff..b01c7a06 100644 --- a/melos.yaml +++ b/melos.yaml @@ -1,13 +1,9 @@ name: auto_mappr_workspace packages: - - packages/** - - packages/**/example - - examples/** - -command: - bootstrap: - usePubspecOverrides: true + - packages/* + - packages/*/example + - examples/* scripts: # ANALYZING diff --git a/packages/auto_mappr/.gitignore b/packages/auto_mappr/.gitignore index c6e36089..5f71119f 100644 --- a/packages/auto_mappr/.gitignore +++ b/packages/auto_mappr/.gitignore @@ -1,4 +1,5 @@ *.g.dart +*.auto_mappr.dart # Files and directories created by pub. .dart_tool/ diff --git a/packages/auto_mappr/CHANGELOG.md b/packages/auto_mappr/CHANGELOG.md index f761e8a5..dc19e98b 100644 --- a/packages/auto_mappr/CHANGELOG.md +++ b/packages/auto_mappr/CHANGELOG.md @@ -1,5 +1,21 @@ [//]: # (## Unreleased) +## 2.0.0 +- **Breaking**: Allow "absorbing" modules using `includes` on `@AutoMappr`. Previous `modules` is now `delegates`. [#117](https://github.com/netglade/auto_mappr/pull/117) +- **Breaking**: Remove shared AutoMappr builder that used PartBuilder, now `.auto_mappr.dart` is generated using LibraryBuilder. [#117](https://github.com/netglade/auto_mappr/pull/117) +- Add type converters, use `converters` on `AutoMappr` or `MapType`. [#119](https://github.com/netglade/auto_mappr/pull/119) +- Add a `reverse` option on `MapType`, which includes the reverse mapping. [#115](https://github.com/netglade/auto_mappr/pull/115) +- Add a support for Dart 3 and Records feature. [#116](https://github.com/netglade/auto_mappr/pull/116) + +## 2.0.0-beta2 +- Add type converters, use `converters` on `AutoMappr` or `MapType`. [#119](https://github.com/netglade/auto_mappr/pull/119) + +## 2.0.0-beta1 +- **Breaking**: Allow "absorbing" modules using `includes` on `@AutoMappr`. Previous `modules` is now `delegates`. [#117](https://github.com/netglade/auto_mappr/pull/117) +- **Breaking**: Remove shared AutoMappr builder that used PartBuilder, now `.auto_mappr.dart` is generated using LibraryBuilder. [#117](https://github.com/netglade/auto_mappr/pull/117) +- Add a `reverse` option on `MapType`, which includes the reverse mapping. [#115](https://github.com/netglade/auto_mappr/pull/115) +- Add a support for Dart 3 and Records feature. [#116](https://github.com/netglade/auto_mappr/pull/116) + ## 1.7.0 - Adhere to netglade_analysis. [#94](https://github.com/netglade/auto_mappr/pull/94) - Update analyzer and mocktail packages. [#111](https://github.com/netglade/auto_mappr/pull/111) diff --git a/packages/auto_mappr/README.md b/packages/auto_mappr/README.md index c9ab94a5..841adaa0 100644 --- a/packages/auto_mappr/README.md +++ b/packages/auto_mappr/README.md @@ -45,9 +45,14 @@ Heavily inspired by [C# AutoMapper][auto_mapper_net_link]. - [Mapping from source](#mapping-from-source) - [Nullability handling](#nullability-handling) - [Forced non-nullable field for nullable source](#forced-non-nullable-field-for-nullable-source) + - [Type converters](#type-converters) - [Generics](#generics) - [Library import aliases](#library-import-aliases) - [Modules](#modules) + - [Including](#including) + - [Delegating](#delegating) + - [Reverse mapping](#reverse-mapping) + - [Records](#records) - [Works with `equatable`](#works-with-equatable) - [Works with `json_serializable`](#works-with-json_serializable) - [Works with generated source and target classes](#works-with-generated-source-and-target-classes) @@ -110,7 +115,7 @@ Then use `MapType()` for each mapping. ```dart import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'my_file.g.dart'; +import 'my_file.auto_mappr.dart'; @AutoMappr([ MapType(), @@ -125,7 +130,7 @@ See [features](#-features) for a complete list. ```dart import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'my_file.g.dart'; +import 'my_file.auto_mappr.dart'; @AutoMappr([ MapType( @@ -232,13 +237,7 @@ dart pub add --dev auto_mappr ### Run the generator -For a Flutter project: - -```shell -flutter pub run build_runner build -``` - -For a Dart project: +For Dart or Flutter projects: ```shell dart run build_runner build @@ -539,56 +538,95 @@ import 'my_domain.dart' as entity; class Mappr extends $Mappr {} ``` -Note that importing a list of `MapType` from another library -and putting it inside `@AutoMappr` annotation is not possible, -since we cannot generate the type correctly (it can overlap with something else) -while using shared part builder. - ### Modules -Each AutoMappr class can be used as a **module**. -That means a mappr used inside of another mappr. -Each AutoMappr class can include a list of modules -that can be used to nest modules -and use all of its underlying mappings. +Each AutoMappr can be considered as a **module**. +The only rule is that the mappr must be constant, +and that most of the time means you have to add an `const` constructor to be able to use it. +Other modules (AutoMappr classes) can then use it in two ways. -Note that modules work as disjunctive units -and their mappings cannot be internally used by by mappr that imported it. -They only work for grouping at the moment. +#### Including -Applications are often split into independent parts (we will call them **features**). -Each feature should probably have its own independent mappr, -that is used as a module. +Including a module means that you want to "absorb" it's mappings +and use them later anywhere **inside** any mapped object. +Basically imagine copy-pasting definitions from included module to yours. -Imagine that in a feature you have a local mappr `UserMappr`. +That can be handy when you have a common/shared mappr with mappings between objects shared across the app. +Since you want to use these common/shared mappings, +you **include** them in your mappr. ```dart -// file: user_mappr.dart -@AutoMappr([ - MapType(), -]) -class UserMappr extends $UserMappr { - const UserMappr(); // must implement const constructor +// file: shared_mappr.dart +@AutoMappr( + [ + MapType(), + ], +) +class SharedMappr extends $SharedMappr { + const SharedMappr(); // must be const! } + +// file: user_mappr.dart +@AutoMappr( + [ + MapType(), // UserDto uses AddressDto inside + ], + includes: [SharedMappr()], // include shared mappr +) +class UserMappr extends $UserMappr {} + +// file: settings_mappr.dart +@AutoMappr( + [ + MapType(), // ProfileDto uses AddressDto inside + ], + includes: [SharedMappr()], // include shared mappr +) +class SettingsMappr extends $SettingsMappr {} ``` -And in some global place, -you can have a main mappr that unifies all smaller mapprs -(`UserMappr` in this case). -As usual, it can also set it's own mappings -(`MapType()`). +#### Delegating + +Delegating to a module is a bit different from including them. +A mappr **delegates** to a **standalone** module. +When your mappr does not know how to convert the **top level object** (the object you put inside `mappr.convert()` method), +it asks delegates to do it. +So you can think of them as disjunctive units that are **grouped** together. + +This is usefull when you have an app with a mappr for each feature +and you want to create one main mappr using other feature mapprs. +The main mappr may not have any mapping at all +and it delegates everything to feature mapprs. ```dart // file: main_mappr.dart +@AutoMappr( + [], + delegates: [ + UserFeatureMappr(), + SettingsFeatureMappr(), + // other features + ], +) +class MainMappr extends $MainMappr {} + +// file: user_feature_mappr.dart @AutoMappr( [ - MapType(), + MapType(), + MapType(), ], - modules: [ - UserMappr(), // use module +) +class UserFeatureMappr extends $UserFeatureMappr {} + +// file: settings_feature_mappr.dart +@AutoMappr( + [ + MapType(), + MapType(), ], ) -class MainMappr extends $MainMappr {} +class SettingsFeatureMappr extends $SettingsFeatureMappr {} ``` Then you can use this main mappr to map between objects specified from every included mappr. @@ -596,14 +634,113 @@ Then you can use this main mappr to map between objects specified from every inc ```dart final mappr = MainMappr(); -final Group user = mappr.convert(GroupDto(...)); // from this mappr -final User user = mappr.convert(UserDto(...)); // from included mappr +final Settings settings = mappr.convert(SettingsDto(...)); // delegates to settings feature mappr +final User user = mappr.convert(UserDto(...)); // delegates to user feature mappr ``` That can be handy for example with dependency injection, -so you can only provide one grouping/main mappr that can handle everything. -Each feature in your app can return an instance of const `AutoMapprInterface`, -that each mappr internally implements. +so you can only provide one main mappr that can handle everything by delegating to other mapprs. + +### Type converters + +`MapType`s are usefull for mappings that you can use from the **outside**. +AutoMappr mapps one object to another using constructors and fields +and you use the `.convert()` method on it. + +But when you need to customize an **inner** converting of types, +there are `TypeConverter`s that help you with that. +Boxing, `String` to `DateTime`, and stuff like that, `TypeConverter`s are your friend. +Note since type converters are only used internally, +you cannot in any way use them using the `.convert()` method. + +Global type converters are also "absorbed" from included modules. +To make the priority crystal clear: + +1. (local) type converters from `MapType`, in order +1. (global) type converters from `AutoMappr`, in order +1. (global included) type converters from `included` modules, in order + +Use `MapType` for most of the things. +Use `TypeConverter` only for cases that cannot be solve otherwise. + +Imagine you have an `UserDto` with a date in a `String` and a `User` model with a date in `DateTime`. +`MapType` will handle mapping of all the constructor parameters and other fields, +while `TypeConverter` will convert `String` to `DateTime`. + +```dart +@AutoMappr( + [ + MapType( + // MapType specific + converters: [ + TypeConverter() + ], + ), + ], + converters: [ + // or globally here + ], +) +class Mappr extends $Mappr { + static DateTime stringToDateTime(String source) { + // ... + } +} +``` + +You can also create methods that can convert "any" (to "any") using `Object`. +But if you work with type parameters (generics) +note that you have to either return the correct type with correct type parameters +or initialize it inside correctly. +It cannot be cast successfully otherwise. +Therefore if you need a method that converts `"any"` to `Value("any")`, +and to make it work for `int` and `String`, +it must look like one of these: + +```dart +static Value objectToValueObject(T source) { + return Value(source); +} + +static Value objectToValueObject2(Object source) { + if (source is int) { + return Value(source); + } + + if (source is String) { + return Value(source); + } + + return Value(source); +} +``` + +### Reverse mapping + +When you want to create a bidirectional mapping (e.g. normal: source to target and reversed: target to source), +you can use `reverse` option on `MapType`. + +Note that it's your responsibility to make sure those objects +support normal and reverse mapping +and to keep them in sync. +Also note that reverse mapping might not work properly when additional configuration +such as [whenSourceIsNull] or [constructor] is used. + +For more complicated scenarios two separate mappings are recommended instead. + +### Records + +Converting records is supported for both positional and named record's fields. + +Target positional fields must have their source field equivalent. +Target named fields must have their source field equivalent determined by name. +Both positional and named target fields without source equivalent must be nullable in order +for mapping to be created successfully +and then thier value will be `null`. + +Note that we do not have a function similar to `convertList` for records +due to Dart 3 "limitation" +as we cannot iterate throught it's positional or named fields on the fly. ### Works with `equatable` @@ -614,8 +751,8 @@ Equatable and other packages with similar conditions implicitly works. ### Works with `json_serializable` -AutoMappr uses a `SharedPartBuilder`. -That means it can share the `.g.dart` file with packages like JSON Serializable +AutoMappr uses a `LibraryBuilder` with `.auto_mappr.dart` file output. +That means it does not interfere with shared `.g.dart` file with packages like JSON Serializable to generate other code to the generated super class. ### Works with generated source and target classes @@ -633,10 +770,8 @@ Check the [customizing the build](#-customizing-the-build) chapter to learn more ## ⚙ Customizing the build -By default, AutoMappr uses the `auto_mappr:auto_mappr` builder -that works with `SharedPartBuilder`, which generates combined `.g.dart` files. -If you need to use `PartBuilder` to generate not-shared `.auto_mappr.dart` part files, -you can use the `auto_mappr:not_shared` builder. +By default, AutoMappr uses the `auto_mappr:auto_mappr` builder that works with `LibraryBuilder`, +which generates `.auto_mappr.dart` file. Modify your `build.yaml` file: @@ -648,19 +783,16 @@ targets: builders: # Or disable specific ones. auto_mappr: - enabled: false - # And enable the not_shared builder. - auto_mappr:not_shared: enabled: true ``` ### Builder options -- ignoreNullableSourceField - Force bang operator on non-nullable target's field if source's field is nullable + +- `ignoreNullableSourceField` - Force bang operator on non-nullable target's field if source's field is nullable ### Default dependencies -By default both `auto_mappr` builders has defined required inputs for freezed -and drift classes. +By default the `auto_mappr` builder has defined required inputs for freezed and drift classes. ```yaml required_inputs: [".freezed.dart", ".drift.dart"] @@ -677,25 +809,6 @@ which you can add a input dependency to. Specify the `required_inputs` dependency on your local AutoMappr builder and disable the builders provided by AutoMappr. -Shared builder: - -```yaml -targets: - $default: - auto_apply_builders: true - builders: - # Enable their generators according to their documentation. - drift_dev:not_shared: - enabled: true - drift_dev:preparing_builder: - enabled: true - # Disable Drift's shared builder - drift_dev:drift_dev: - enabled: false -``` - -Not shared builder: - ```yaml targets: $default: @@ -711,9 +824,6 @@ targets: enabled: false auto_mappr: - enabled: false - # Enable the not_shared builder. - auto_mappr:not_shared: enabled: true ``` diff --git a/packages/auto_mappr/build.yaml b/packages/auto_mappr/build.yaml index 905672fe..2fe76f57 100644 --- a/packages/auto_mappr/build.yaml +++ b/packages/auto_mappr/build.yaml @@ -13,16 +13,7 @@ builders: auto_mappr: import: "package:auto_mappr/auto_mappr.dart" builder_factories: ["autoMapprBuilder"] - build_extensions: { ".dart": [".auto_mappr.g.part"] } - auto_apply: dependents - build_to: cache - applies_builders: ["source_gen:combining_builder"] - required_inputs: [".freezed.dart", ".drift.dart"] - - not_shared: - import: "package:auto_mappr/auto_mappr.dart" - builder_factories: ["autoMapprBuilderNotShared"] build_extensions: { ".dart": [".auto_mappr.dart"] } - auto_apply: none + auto_apply: dependents build_to: source required_inputs: [".freezed.dart", ".drift.dart"] diff --git a/packages/auto_mappr/example/.gitignore b/packages/auto_mappr/example/.gitignore index 641b8adc..38c84cf9 100644 --- a/packages/auto_mappr/example/.gitignore +++ b/packages/auto_mappr/example/.gitignore @@ -1,2 +1,3 @@ # Example !*.g.dart +!*.auto_mappr.dart diff --git a/packages/auto_mappr/example/lib/mappr.g.dart b/packages/auto_mappr/example/lib/mappr.auto_mappr.dart similarity index 82% rename from packages/auto_mappr/example/lib/mappr.g.dart rename to packages/auto_mappr/example/lib/mappr.auto_mappr.dart index 596834af..ab771404 100644 --- a/packages/auto_mappr/example/lib/mappr.g.dart +++ b/packages/auto_mappr/example/lib/mappr.auto_mappr.dart @@ -1,26 +1,25 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'mappr.dart'; - // ************************************************************************** // AutoMapprGenerator // ************************************************************************** -// ignore_for_file: non_constant_identifier_names, prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables -// ignore_for_file: require_trailing_commas, unnecessary_const -// ignore_for_file: unnecessary_lambdas, unnecessary_parenthesis -// ignore_for_file: unnecessary_raw_strings +// ignore_for_file: type=lint, unnecessary_cast, unused_local_variable + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as _i1; + +import 'mappr.dart' as _i2; /// {@template asset:auto_mappr/example/lib/mappr.dart} /// Available mappings: /// - `UserDto` → `User`. /// {@endtemplate} -class $Mappr implements AutoMapprInterface { +class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; - List get _modules => const []; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} /// {@macro asset:auto_mappr/example/lib/mappr.dart} @@ -28,13 +27,14 @@ class $Mappr implements AutoMapprInterface { bool canConvert({bool recursive = true}) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { return true; } if (recursive) { - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return true; } @@ -50,7 +50,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return _convert(model)!; } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convert(model)!; } @@ -69,7 +69,7 @@ class $Mappr implements AutoMapprInterface { canReturnNull: true, ); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvert(model); } @@ -85,7 +85,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item)!); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertIterable(model); } @@ -105,7 +105,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return model.map((item) => _convert(item, canReturnNull: true)); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertIterable(model); } @@ -121,7 +121,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertList(model); } @@ -140,7 +140,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toList(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertList(model); } @@ -156,7 +156,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return convertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.convertSet(model); } @@ -175,7 +175,7 @@ class $Mappr implements AutoMapprInterface { if (canConvert(recursive: false)) { return tryConvertIterable(model).toSet(); } - for (final mappr in _modules) { + for (final mappr in _delegates) { if (mappr.canConvert()) { return mappr.tryConvertSet(model); } @@ -190,25 +190,26 @@ class $Mappr implements AutoMapprInterface { }) { final sourceTypeOf = _typeOf(); final targetTypeOf = _typeOf(); - if ((sourceTypeOf == _typeOf() || - sourceTypeOf == _typeOf()) && - (targetTypeOf == _typeOf() || targetTypeOf == _typeOf())) { + if ((sourceTypeOf == _typeOf<_i2.UserDto>() || + sourceTypeOf == _typeOf<_i2.UserDto?>()) && + (targetTypeOf == _typeOf<_i2.User>() || + targetTypeOf == _typeOf<_i2.User?>())) { if (canReturnNull && model == null) { return null; } - return (_map__UserDto__To__User((model as UserDto?)) as TARGET); + return (_map__i2$UserDto_To__i2$User((model as _i2.UserDto?)) as TARGET); } throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } - User _map__UserDto__To__User(UserDto? input) { + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { throw Exception( r'Mapping UserDto → User failed because UserDto was null, and no default value was provided. ' r'Consider setting the whenSourceIsNull parameter on the MapType to handle null values during mapping.'); } - return User( + return _i2.User( id: model.id, name: model.xname, ); diff --git a/packages/auto_mappr/example/lib/mappr.dart b/packages/auto_mappr/example/lib/mappr.dart index 77eaeeb2..e6c5fd84 100644 --- a/packages/auto_mappr/example/lib/mappr.dart +++ b/packages/auto_mappr/example/lib/mappr.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'mappr.g.dart'; +import 'package:auto_mappr_example/mappr.auto_mappr.dart'; @AutoMappr([ MapType( diff --git a/packages/auto_mappr/example/pubspec.yaml b/packages/auto_mappr/example/pubspec.yaml index 0d169acf..011f1001 100644 --- a/packages/auto_mappr/example/pubspec.yaml +++ b/packages/auto_mappr/example/pubspec.yaml @@ -4,14 +4,14 @@ version: 1.0.0 publish_to: none environment: - sdk: ">=2.18.7 <4.0.0" + sdk: ^3.0.0 dependencies: - auto_mappr_annotation: ^1.1.0 + auto_mappr_annotation: ^2.0.0 equatable: ^2.0.5 dev_dependencies: - auto_mappr: ^1.3.0 + auto_mappr: ^2.0.0 build_runner: ^2.0.0 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/packages/auto_mappr/lib/auto_mappr.dart b/packages/auto_mappr/lib/auto_mappr.dart index 48fc4da3..f6458534 100644 --- a/packages/auto_mappr/lib/auto_mappr.dart +++ b/packages/auto_mappr/lib/auto_mappr.dart @@ -5,12 +5,9 @@ import 'package:build/build.dart'; import 'package:source_gen/source_gen.dart'; /// Main Builder for the `AutoMappr` Annotation. -Builder autoMapprBuilder(BuilderOptions options) => SharedPartBuilder( - [AutoMapprGenerator(builderOptions: options)], - 'auto_mappr', - ); - -Builder autoMapprBuilderNotShared(BuilderOptions options) => PartBuilder( - [AutoMapprGenerator(builderOptions: options)], - '.auto_mappr.dart', +Builder autoMapprBuilder(BuilderOptions options) => LibraryBuilder( + AutoMapprGenerator(builderOptions: options), + generatedExtension: '.auto_mappr.dart', + allowSyntaxErrors: true, + options: options, ); diff --git a/packages/auto_mappr/lib/src/builder/assignments/assignment_builder_base.dart b/packages/auto_mappr/lib/src/builder/assignments/assignment_builder_base.dart new file mode 100644 index 00000000..762398e2 --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/assignment_builder_base.dart @@ -0,0 +1,20 @@ +import 'package:auto_mappr/src/models/models.dart'; +import 'package:code_builder/code_builder.dart'; + +abstract class AssignmentBuilderBase { + final SourceAssignment assignment; + final AutoMapprConfig mapperConfig; + final TypeMapping mapping; + final void Function(TypeMapping? mapping)? usedNullableMethodCallback; + + const AssignmentBuilderBase({ + required this.assignment, + required this.mapperConfig, + required this.mapping, + required this.usedNullableMethodCallback, + }); + + Expression buildAssignment(); + + bool canAssign(); +} diff --git a/packages/auto_mappr/lib/src/builder/assignments/assignments.dart b/packages/auto_mappr/lib/src/builder/assignments/assignments.dart new file mode 100644 index 00000000..916cc770 --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/assignments.dart @@ -0,0 +1,6 @@ +export 'assignment_builder_base.dart'; +export 'iterable_assignment_builder.dart'; +export 'map_assignment_builder.dart'; +export 'nested_object_assignment_builder.dart'; +export 'record_assignment_builder.dart'; +export 'type_converter_builder.dart'; diff --git a/packages/auto_mappr/lib/src/builder/assignments/iterable_assignment_builder.dart b/packages/auto_mappr/lib/src/builder/assignments/iterable_assignment_builder.dart new file mode 100644 index 00000000..c6fafe79 --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/iterable_assignment_builder.dart @@ -0,0 +1,93 @@ +import 'package:auto_mappr/src/builder/assignments/assignment_builder_base.dart'; +import 'package:auto_mappr/src/builder/assignments/nested_object_mixin.dart'; +import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:auto_mappr/src/models/models.dart'; +import 'package:code_builder/code_builder.dart'; + +class IterableAssignmentBuilder extends AssignmentBuilderBase with NestedObjectMixin { + const IterableAssignmentBuilder({ + required super.assignment, + required super.mapperConfig, + required super.mapping, + required super.usedNullableMethodCallback, + }); + + @override + bool canAssign() { + return assignment.canAssignIterable(); + } + + @override + Expression buildAssignment() { + final sourceType = assignment.sourceType!; + final targetType = assignment.targetType; + + final sourceNullable = sourceType.isNullable; + final targetNullable = targetType.isNullable; + + final sourceIterableType = sourceType.genericParameterTypeOrSelf; + final targetIterableType = targetType.genericParameterTypeOrSelf; + + final shouldFilterNullInSource = sourceIterableType.isNullable && targetIterableType.isNotNullable; + + final assignNestedObject = (!targetIterableType.isPrimitiveType && !targetIterableType.isSpecializedListType) && + (!targetIterableType.isSame(sourceIterableType)); + + // When [sourceIterableType] is nullable and [targetIterableType] is not, remove null values. + final sourceIterableExpression = refer('model').property(assignment.sourceField!.name).maybeWhereIterableNotNull( + condition: shouldFilterNullInSource, + isOnNullable: sourceNullable, + ); + + final defaultIterableValueExpression = targetType.defaultIterableExpression(); + + if (assignNestedObject) { + return sourceIterableExpression + // Map complex nested types. + .maybeNullSafeProperty('map', isOnNullable: sourceNullable) + .call( + [_map(assignment)], + {}, + [EmitterHelper.current.typeRefer(type: targetIterableType)], + ) + // Call toList, toSet or nothing. + // isOnNullable is false, because if map() was called, the value is non-null + .maybeToIterableCall( + source: sourceType, + target: targetType, + forceCast: true, //map was used so we want force toIterable() call + isOnNullable: false, + ) + // When [sourceNullable], use default value. + .maybeIfNullThen(defaultIterableValueExpression, isOnNullable: sourceNullable && !targetNullable); + } + + return sourceIterableExpression + .maybeToIterableCall( + source: sourceType, + target: targetType, + forceCast: shouldFilterNullInSource, // if whereNotNull was used -> we want to force toIterable() call + isOnNullable: !targetNullable && sourceNullable, + ) + .maybeIfNullThen( + defaultIterableValueExpression, + isOnNullable: !targetNullable && sourceNullable, + ); + } + + Expression _map(SourceAssignment assignment) { + final targetListType = assignment.targetType.genericParameterTypeOrSelf; + final sourceListType = assignment.sourceType!.genericParameterTypeOrSelf; + + final body = assignNestedObject( + assignment: assignment, + source: sourceListType, + target: targetListType, + convertMethodArgument: refer('value'), + ); + + return refer('(value) => ${body.accept(EmitterHelper.current.emitter)}'); + } +} diff --git a/packages/auto_mappr/lib/src/builder/assignments/map_assignment_builder.dart b/packages/auto_mappr/lib/src/builder/assignments/map_assignment_builder.dart new file mode 100644 index 00000000..79052c8b --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/map_assignment_builder.dart @@ -0,0 +1,163 @@ +import 'package:analyzer/dart/element/type.dart'; +import 'package:auto_mappr/src/builder/assignments/assignment_builder_base.dart'; +import 'package:auto_mappr/src/builder/assignments/nested_object_mixin.dart'; +import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:auto_mappr/src/models/models.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:collection/collection.dart'; +import 'package:source_gen/source_gen.dart'; + +class MapAssignmentBuilder extends AssignmentBuilderBase with NestedObjectMixin { + const MapAssignmentBuilder({ + required super.assignment, + required super.mapperConfig, + required super.mapping, + required super.usedNullableMethodCallback, + }); + + @override + bool canAssign() { + return assignment.canAssignMap(); + } + + @override + Expression buildAssignment() { + final sourceType = assignment.sourceType!; + final targetType = assignment.targetType; + + final sourceNullable = sourceType.isNullable; + + final sourceKeyType = (sourceType as ParameterizedType).typeArguments.firstOrNull; + final sourceValueType = sourceType.typeArguments.lastOrNull; + + final targetKeyType = (targetType as ParameterizedType).typeArguments.firstOrNull; + final targetValueType = targetType.typeArguments.lastOrNull; + + final sourceNullableKey = sourceKeyType?.isNullable ?? false; + final sourceNullableValue = sourceValueType?.isNullable ?? false; + final targetNullableKey = targetKeyType?.isNullable ?? false; + final targetNullableValue = targetValueType?.isNullable ?? false; + + if (targetKeyType == null || targetValueType == null) { + final emittedTarget = EmitterHelper.current.typeReferEmitted(type: targetType); + + throw InvalidGenerationSourceError( + 'Target key or value type is null for $emittedTarget', + ); + } + + if (sourceKeyType == null || sourceValueType == null) { + final emittedSource = EmitterHelper.current.typeReferEmitted(type: sourceType); + + throw InvalidGenerationSourceError( + 'Source key or value type is null for $emittedSource', + ); + } + + final keyMapping = mapperConfig.findMapping(source: sourceKeyType, target: targetKeyType); + final valueMapping = mapperConfig.findMapping(source: sourceValueType, target: targetValueType); + + // Keys: source is null, target is not null, and default value does not exist. + final shouldRemoveNullsKey = + sourceNullableKey && !targetNullableKey && (!(keyMapping?.hasWhenNullDefault() ?? false)); + + // Value: source is null, target is not null, and default value does not exist. + final shouldRemoveNullsValue = + sourceNullableValue && !targetNullableValue && (!(valueMapping?.hasWhenNullDefault() ?? false)); + + final sourceMapExpression = refer('model').property(assignment.sourceField!.name); + + final defaultMapValueExpression = literalMap( + {}, + EmitterHelper.current.typeRefer(type: targetKeyType), + EmitterHelper.current.typeRefer(type: targetValueType), + ); + + final assignNestedObjectKey = !targetKeyType.isPrimitiveType && (targetKeyType != sourceKeyType); + final assignNestedObjectValue = !targetValueType.isPrimitiveType && (targetValueType != sourceValueType); + + final shouldDoMapCall = assignNestedObjectKey || assignNestedObjectValue; + + return sourceMapExpression + // Filter nulls when source key/value is nullable and target is not. + .maybeWhereMapNotNull( + isOnNullable: sourceNullable, + keyIsNullable: shouldRemoveNullsKey, + valueIsNullable: shouldRemoveNullsValue, + keyType: sourceKeyType, + valueType: sourceValueType, + ) + .maybeCall( + 'map', + isOnNullable: sourceNullable, + // Call map only when actually some mapping is required. + condition: shouldDoMapCall, + positionalArguments: [_map(assignment)], + typeArguments: [ + EmitterHelper.current.typeRefer(type: targetKeyType), + EmitterHelper.current.typeRefer(type: targetValueType), + ], + ) + // When [sourceNullable], use default value. + .maybeIfNullThen(defaultMapValueExpression, isOnNullable: sourceNullable); + } + + Expression _map(SourceAssignment assignment) { + final sourceKeyType = (assignment.sourceType! as ParameterizedType).typeArguments.firstOrNull; + final sourceValueType = (assignment.sourceType! as ParameterizedType).typeArguments.lastOrNull; + final targetKeyType = (assignment.targetType as ParameterizedType).typeArguments.firstOrNull; + final targetValueType = (assignment.targetType as ParameterizedType).typeArguments.lastOrNull; + + if (targetKeyType == null || targetValueType == null) { + final emittedTarget = EmitterHelper.current.typeReferEmitted(type: assignment.targetType); + throw InvalidGenerationSourceError( + 'Target key or value type is null for $emittedTarget', + ); + } + + if (sourceKeyType == null || sourceValueType == null) { + final emittedSource = EmitterHelper.current.typeReferEmitted(type: assignment.sourceType); + throw InvalidGenerationSourceError( + 'Source key or value type is null for $emittedSource', + ); + } + + final assignNestedObjectKey = !targetKeyType.isPrimitiveType && (targetKeyType != sourceKeyType); + final assignNestedObjectValue = !targetValueType.isPrimitiveType && (targetValueType != sourceValueType); + + final keysAreSameType = sourceKeyType == targetKeyType; + final valuesAreSameType = sourceValueType == targetValueType; + + // Returns a tear off when no nested call is needed. + if (!assignNestedObjectKey && !assignNestedObjectValue) { + return refer('MapEntry.new'); + } + + final sourceMapExpression = refer('key'); + final targetMapExpression = refer('value'); + + final keyExpression = assignNestedObjectKey + ? assignNestedObject( + assignment: assignment, + source: sourceKeyType, + target: targetKeyType, + convertMethodArgument: keysAreSameType ? null : sourceMapExpression, + ) + : sourceMapExpression; + + final valueExpression = assignNestedObjectValue + ? assignNestedObject( + assignment: assignment, + source: sourceValueType, + target: targetValueType, + convertMethodArgument: valuesAreSameType ? null : targetMapExpression, + ) + : targetMapExpression; + + return refer( + '(key, value) => MapEntry(${keyExpression.accept(EmitterHelper.current.emitter)}, ${valueExpression.accept(EmitterHelper.current.emitter)})', + ); + } +} diff --git a/packages/auto_mappr/lib/src/builder/assignments/nested_object_assignment_builder.dart b/packages/auto_mappr/lib/src/builder/assignments/nested_object_assignment_builder.dart new file mode 100644 index 00000000..f2ac78fa --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/nested_object_assignment_builder.dart @@ -0,0 +1,39 @@ +import 'package:analyzer/dart/element/type.dart'; +import 'package:auto_mappr/src/builder/assignments/assignment_builder_base.dart'; +import 'package:auto_mappr/src/builder/assignments/nested_object_mixin.dart'; +import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:code_builder/code_builder.dart'; + +class NestedObjectAssignmentBuilder extends AssignmentBuilderBase with NestedObjectMixin { + final DartType source; + final DartType target; + final Expression? convertMethodArgument; + final bool includeGenericTypes; + + const NestedObjectAssignmentBuilder({ + required super.assignment, + required super.mapperConfig, + required super.mapping, + required super.usedNullableMethodCallback, + required this.source, + required this.target, + this.convertMethodArgument, + this.includeGenericTypes = false, + }); + + @override + bool canAssign() { + return !assignment.targetType.isPrimitiveType; + } + + @override + Expression buildAssignment() { + return assignNestedObject( + source: source, + target: target, + assignment: assignment, + convertMethodArgument: convertMethodArgument, + includeGenericTypes: includeGenericTypes, + ); + } +} diff --git a/packages/auto_mappr/lib/src/builder/assignments/nested_object_mixin.dart b/packages/auto_mappr/lib/src/builder/assignments/nested_object_mixin.dart new file mode 100644 index 00000000..3f7f984b --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/nested_object_mixin.dart @@ -0,0 +1,150 @@ +import 'package:analyzer/dart/element/type.dart'; +import 'package:auto_mappr/src/builder/assignments/assignments.dart'; +import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; +import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:auto_mappr/src/models/models.dart'; +import 'package:build/build.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:source_gen/source_gen.dart'; + +/// Separate logic as a mixin so we do not soil [AssignmentBuilderBase]. +mixin NestedObjectMixin on AssignmentBuilderBase { + /// Assigns nested object as either: + /// - default value + /// - call to already generated mapping between two types + /// + /// If [convertMethodArgument] is null, uses a tear off call instead. + Expression assignNestedObject({ + required DartType source, + required DartType target, + required SourceAssignment assignment, + Expression? convertMethodArgument, + bool includeGenericTypes = false, + }) { + final sourceOnModel = refer('model').property(assignment.sourceField!.displayName); + + // Source and target is the same. + if (source.isSame(target)) { + return sourceOnModel; + } + + final nestedMapping = mapperConfig.findMapping( + source: source, + target: target, + ); + + // Type converters. + final typeConvertersBuilder = TypeConverterBuilder( + assignment: assignment, + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: null, + convertMethodArgument: convertMethodArgument, + source: source, + target: target, + ); + if (typeConvertersBuilder.canAssign()) { + return typeConvertersBuilder.buildAssignment(); + } + + // Unknown mapping. + if (nestedMapping == null) { + final sourceName = assignment.sourceField?.getDisplayString(withNullability: true); + + if (target.isNullable) { + log.warning("Can't find nested mapping '$assignment' but target is nullable. Setting null"); + + return literalNull; + } + + final emittedTarget = EmitterHelper.current.typeReferEmitted(type: target); + throw InvalidGenerationSourceError( + 'Trying to map nested object from "$assignment" but no mapping is configured.', + todo: 'Configure mapping from $sourceName to $emittedTarget', + ); + } + + final convertCallExpression = mappingCall( + nestedMapping: nestedMapping, + source: source, + target: target, + convertMethodArgument: convertMethodArgument, + includeGenericTypes: includeGenericTypes, + ); + + // If source == null and target not nullable -> use whenNullDefault if possible + final fieldMapping = mapping.tryGetFieldMapping(assignment.targetName); + if (source.isNullable && (fieldMapping?.whenNullExpression != null)) { + // Generates code like: + // + // model.name == null + // ? const Nested( + // id: 123, + // name: 'test', + // ) + // : _map_NestedDto_To_Nested(model.name), + return sourceOnModel.equalTo(literalNull).conditional( + fieldMapping!.whenNullExpression!, + convertCallExpression, + ); + } + + // Generates code like: + // + // `_map_NestedDto_To_Nested(model.name)` + return convertCallExpression; + } + + /// Generates a mapping call `_mapAlphaDto_to_Alpha(convertMethodArgument)`. + /// When [convertMethodArgument] is null, then a tear off `_mapAlphaDto_to_Alpha` is generated. + /// + /// This function also marks nullable mapping to be generated + /// using the [usedNullableMethodCallback] callback. + Expression mappingCall({ + required DartType source, + required DartType target, + required TypeMapping nestedMapping, + Expression? convertMethodArgument, + bool includeGenericTypes = false, + }) { + final targetNullable = target.isNullable; + + final useNullableMethod = targetNullable && !mapping.hasWhenNullDefault(); + + // When target is nullable, use nullable convert method. + // But use non-nullable when the mapping has default value. + // + // Otherwise use non-nullable. + final convertMethod = refer( + useNullableMethod + ? MethodBuilderBase.constructNullableConvertMethodName( + source: source, + target: target, + config: mapperConfig, + ) + : MethodBuilderBase.constructConvertMethodName( + source: source, + target: target, + config: mapperConfig, + ), + ); + + if (useNullableMethod) { + usedNullableMethodCallback?.call(nestedMapping); + } + + return convertMethodArgument == null + ? convertMethod + : convertMethod.call( + [convertMethodArgument], + {}, + includeGenericTypes + ? [ + EmitterHelper.current.typeRefer(type: source), + EmitterHelper.current.typeRefer(type: target), + ] + : [], + ); + } +} diff --git a/packages/auto_mappr/lib/src/builder/assignments/record_assignment_builder.dart b/packages/auto_mappr/lib/src/builder/assignments/record_assignment_builder.dart new file mode 100644 index 00000000..10484e6c --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/record_assignment_builder.dart @@ -0,0 +1,151 @@ +import 'package:analyzer/dart/element/nullability_suffix.dart'; +import 'package:analyzer/dart/element/type.dart' as type; +import 'package:auto_mappr/src/builder/assignments/assignment_builder_base.dart'; +import 'package:auto_mappr/src/builder/assignments/nested_object_mixin.dart'; +import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:auto_mappr/src/models/source_assignment.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:collection/collection.dart'; +import 'package:source_gen/source_gen.dart'; + +class RecordAssignmentBuilder extends AssignmentBuilderBase with NestedObjectMixin { + const RecordAssignmentBuilder({ + required super.assignment, + required super.mapperConfig, + required super.mapping, + required super.usedNullableMethodCallback, + }); + + @override + bool canAssign() { + return assignment.canAssignRecord(); + } + + @override + Expression buildAssignment() { + final sourceType = assignment.sourceType!; + final targetType = assignment.targetType; + + final sourceRecordType = sourceType as type.RecordType; + final targetRecordType = targetType as type.RecordType; + + final sourcePositional = sourceRecordType.positionalFields; + final targetPositional = targetRecordType.positionalFields; + + // Positional fields check. + for (final (index, targetField) in targetPositional.indexed) { + final sourceField = sourcePositional.elementAtOrNull(index); + if (sourceField == null && targetField.type.nullabilitySuffix == NullabilitySuffix.none) { + throw InvalidGenerationSourceError( + 'Cannot map positional source field to non-nullable target field for source $sourceType and target $targetType', + ); + } + } + + // Named fields check. + final sourceNamed = sourceRecordType.namedFields; + final targetNamed = targetRecordType.namedFields; + for (final targetField in targetNamed) { + if (!sourceNamed.any((sourceField) => sourceField.name == targetField.name) && + targetField.type.nullabilitySuffix == NullabilitySuffix.none) { + throw InvalidGenerationSourceError( + "Cannot find mapping to non-nullable target record's named field $targetField", + ); + } + } + + final positionalFields = [ + for (final (index, targetField) in targetPositional.indexed) + _mapPositionalField( + assignment: assignment, + source: sourcePositional.elementAtOrNull(index), + target: targetField, + index: index + 1, + ).accept(EmitterHelper.current.emitter).toString(), + ]; + + final namedFields = <({String key, String value})>[ + for (final targetField in targetNamed) + ( + key: targetField.name, + value: _mapNamedField( + assignment: assignment, + source: sourceNamed.firstWhereOrNull((sourceField) => sourceField.name == targetField.name), + target: targetField, + ).accept(EmitterHelper.current.emitter).toString(), + ), + ]; + + return CodeExpression( + Code( + '(${positionalFields.join(',')} ${positionalFields.isNotEmpty ? ',' : ''} ${namedFields.map((field) => '${field.key}: ${field.value}').join(',')})', + ), + ); + } + + // Handles mapping of only one named field. + // + // Generates + // - mapping for primitives: `model.alpha` + // - mapping for complex types: `_map_NestedDto_To_Nested(model.alpha)` + // - and when nullable and if `whenNullExpression` defined, uses that as a fallback + Expression _mapNamedField({ + required SourceAssignment assignment, + required type.RecordTypeNamedField? source, + required type.RecordTypeNamedField target, + }) { + if (source == null) return literalNull; + + final valuesAreSameType = source.type == target.type; + final shouldAssignNestedObject = !target.type.isPrimitiveType && !valuesAreSameType; + + final targetRecordExpression = refer(assignment.sourceField!.name); + + if (!shouldAssignNestedObject) { + return refer('model.${targetRecordExpression.accept(EmitterHelper.current.emitter)}.${source.name}'); + } + + final valueExpression = assignNestedObject( + assignment: assignment, + source: source.type, + target: target.type, + convertMethodArgument: valuesAreSameType ? null : targetRecordExpression, + ); + + return refer('${valueExpression.accept(EmitterHelper.current.emitter)})'); + } + + // Handles mapping of only one positional field. + // + // Generates + // - mapping for primitives: `model.$1` + // - mapping for complex types: `_map_NestedDto_To_Nested(model.$1)` + // - and when nullable and if `whenNullExpression` defined, uses that as a fallback + Expression _mapPositionalField({ + required SourceAssignment assignment, + required type.RecordTypePositionalField? source, + required type.RecordTypePositionalField target, + required int index, + }) { + if (source == null) return literalNull; + + final valuesAreSameType = source.type == target.type; + final shouldAssignNestedObject = !target.type.isPrimitiveType && !valuesAreSameType; + + final targetRecordExpression = refer(assignment.sourceField!.name); + + if (!shouldAssignNestedObject) { + return refer('model.${targetRecordExpression.accept(EmitterHelper.current.emitter)}.\$$index'); + } + + final valueExpression = assignNestedObject( + assignment: assignment, + source: source.type, + target: target.type, + convertMethodArgument: valuesAreSameType ? null : targetRecordExpression, + ); + + return refer('${valueExpression.accept(EmitterHelper.current.emitter)})'); + } +} diff --git a/packages/auto_mappr/lib/src/builder/assignments/type_converter_builder.dart b/packages/auto_mappr/lib/src/builder/assignments/type_converter_builder.dart new file mode 100644 index 00000000..3540c004 --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/assignments/type_converter_builder.dart @@ -0,0 +1,57 @@ +import 'package:analyzer/dart/element/type.dart'; +import 'package:auto_mappr/src/builder/assignments/assignment_builder_base.dart'; +import 'package:auto_mappr/src/extensions/executable_element_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:collection/collection.dart'; + +class TypeConverterBuilder extends AssignmentBuilderBase { + final Expression? convertMethodArgument; + final DartType source; + final DartType target; + + const TypeConverterBuilder({ + required super.assignment, + required super.mapperConfig, + required super.mapping, + required super.usedNullableMethodCallback, + required this.convertMethodArgument, + required this.source, + required this.target, + }); + + @override + bool canAssign() { + return assignment.typeConverters.firstWhereOrNull( + (converter) => converter.canBeUsed( + mappingSource: source, + mappingTarget: target, + ), + ) != + null; + } + + @override + Expression buildAssignment() { + final converter = assignment.typeConverters + // ignore: avoid-unsafe-collection-methods, checked by [canAssign] + .firstWhere((c) => c.canBeUsed(mappingSource: source, mappingTarget: target)); + + // Call. + if (convertMethodArgument case final methodArgument?) { + final targetRefer = EmitterHelper.current.typeRefer(type: target); + + return EmitterHelper.current + .refer(converter.converter.referCallString, converter.converter.library.identifier) + .call([methodArgument]).asA(targetRefer); + } + + final sourceEmitted = EmitterHelper.current.typeReferEmitted(type: source); + final targetEmitted = EmitterHelper.current.typeReferEmitted(type: target); + + // Tear-off. + return EmitterHelper.current + .refer(converter.converter.referCallString, converter.converter.library.identifier) + .asA(refer('$targetEmitted Function($sourceEmitted)')); + } +} diff --git a/packages/auto_mappr/lib/src/builder/auto_mappr_builder.dart b/packages/auto_mappr/lib/src/builder/auto_mappr_builder.dart index 3f076c92..56c58d22 100644 --- a/packages/auto_mappr/lib/src/builder/auto_mappr_builder.dart +++ b/packages/auto_mappr/lib/src/builder/auto_mappr_builder.dart @@ -1,29 +1,23 @@ // ignore_for_file: public_member_api_docs import 'package:analyzer/dart/element/element.dart'; -import 'package:auto_mappr/src/builder/map_model_body_method_builder.dart'; import 'package:auto_mappr/src/builder/methods/methods.dart'; -import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:auto_mappr/src/helpers/urls.dart'; import 'package:auto_mappr/src/models/auto_mappr_config.dart'; import 'package:auto_mappr/src/models/type_mapping.dart'; import 'package:built_collection/built_collection.dart'; import 'package:code_builder/code_builder.dart'; +/// Entrypoint for mappr class generation. class AutoMapprBuilder { final AutoMapprConfig config; final ClassElement mapperClassElement; static const List fileIgnores = [ - 'unnecessary_parenthesis', - 'non_constant_identifier_names', - 'unnecessary_const', - 'require_trailing_commas', - 'unnecessary_raw_strings', - 'unnecessary_lambdas', - - // Can we fix this somehow? (const defaults, const customs). - 'prefer_const_constructors', - 'prefer_const_literals_to_create_immutables', + // ignore everything + 'type=lint', + 'unused_local_variable', + 'unnecessary_cast', ]; const AutoMapprBuilder({ @@ -35,18 +29,16 @@ class AutoMapprBuilder { return Library( (b) => b ..ignoreForFile = ListBuilder(fileIgnores) - ..body.addAll( - [ - Class( - (b) => b - ..name = '\$${mapperClassElement.displayName}' - ..implements = ListBuilder([refer('AutoMapprInterface')]) - ..methods.addAll(_buildMethods()) - ..constructors.addAll(_buildConstructors()) - ..docs = ListBuilder(config.getAvailableMappingsDocComment()), - ), - ], - ), + ..body.addAll([ + Class( + (cb) => cb + ..name = '\$${mapperClassElement.displayName}' + ..implements = ListBuilder([refer('AutoMapprInterface', Urls.annotationPackageUrl)]) + ..methods.addAll(_buildMethods()) + ..constructors.addAll(_buildConstructors()) + ..docs = ListBuilder(config.getAvailableMappingsDocComment()), + ), + ]), ); } @@ -115,51 +107,16 @@ class AutoMapprBuilder { PrivateConvertMethodBuilder(config).buildMethod(), // Generate non-nullable mapping method. - // TODO(later): switch to MappingMethodBuilder. for (final mapping in config.mappers) - Method( - (b) => b - ..name = mapping.mappingMethodName(config: config) - ..requiredParameters.addAll([ - Parameter( - (p) => p - ..name = 'input' - ..type = refer('${mapping.source.getDisplayStringWithLibraryAlias(config: config)}?'), - ), - ]) - ..returns = refer( - mapping.target.getDisplayStringWithLibraryAlias(config: config), - ) - ..body = MapModelBodyMethodBuilder( - mapping: mapping, - mapperConfig: config, - usedNullableMethodCallback: usedNullableMappingMethod, - ).build(), - ), + MappingMethodBuilder( + config, + mapping: mapping, + usedNullableMethodCallback: usedNullableMappingMethod, + ).buildMethod(), // Generates nullable mapping method only when nullable method is used. - // TODO(later): switch to MappingMethodBuilder. for (final mapping in config.mappers.where(nullableMappings.contains)) - Method( - (b) => b - ..name = mapping.nullableMappingMethodName(config: config) - ..requiredParameters.addAll([ - Parameter( - (p) => p - ..name = 'input' - ..type = refer('${mapping.source.getDisplayStringWithLibraryAlias(config: config)}?'), - ), - ]) - ..returns = refer('${mapping.target.getDisplayStringWithLibraryAlias( - withNullability: true, - config: config, - )}?') - ..body = MapModelBodyMethodBuilder( - mapping: mapping, - mapperConfig: config, - nullable: true, - ).build(), - ), + MappingMethodBuilder(config, mapping: mapping, nullable: true).buildMethod(), ]; } } 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 new file mode 100644 index 00000000..16f1d6ad --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/map_bodies/class_body_builder.dart @@ -0,0 +1,330 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:auto_mappr/src/builder/map_bodies/map_body_builder_base.dart'; +import 'package:auto_mappr/src/builder/value_assignment_builder.dart'; +import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:auto_mappr/src/extensions/interface_type_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:auto_mappr/src/models/source_assignment.dart'; +import 'package:build/build.dart'; +import 'package:code_builder/code_builder.dart' show Code, Expression; +import 'package:collection/collection.dart'; +import 'package:source_gen/source_gen.dart'; + +class ClassBodyBuilder extends MapBodyBuilderBase { + const ClassBodyBuilder({ + required super.mapperConfig, + required super.mapping, + required super.usedNullableMethodCallback, + }); + + @override + Code build() { + final sourceFields = _getAllReadableFields(classType: mapping.source); + + // Name of the source field names which can be mapped into constructor field + final mappedSourceFieldNames = []; + + // Map fields using a constructor. + // Returns an constructor call without `;`. + // + // One( + // usingConstructor1: model.usingConstructor1, + // usingConstructor2: model.usingConstructor2, + // ) + final constructorExpression = _processConstructorMapping( + mappedSourceFieldNames: mappedSourceFieldNames, + sourceFields: sourceFields, + ); + + // Map fields not mapped directly in constructor as setters if possible. + // + // Code like: + // + // + // ..withoutConstructor1 = model.withoutConstructor1 + // ..withoutConstructor2 = model.withoutConstructor2 + // ..withoutConstructor3 = model.withoutConstructor3 + final constructorWithSetters = _mapSetterFields( + alreadyMapped: mappedSourceFieldNames, + sourceFields: sourceFields, + constructorExpression: constructorExpression, + ); + + return constructorWithSetters.returned.statement; + } + + @override + bool canProcess() { + return !mapping.isEnumMapping; + } + + /// Returns all public fields (instance or static) that have a getter. + Map _getAllReadableFields({ + required InterfaceType classType, + }) { + final fieldsWithGetter = classType.getAllGetters(); + + return {for (final field in fieldsWithGetter) field.name: field}; + } + + Expression _processConstructorMapping({ + required List mappedSourceFieldNames, + required Map sourceFields, + }) { + final mappedTargetConstructorParams = []; + final notMappedTargetParameters = []; + + final targetConstructor = _findBestConstructor(mapping.target, forcedConstructor: mapping.constructor); + + if (targetConstructor == null) { + throw InvalidGenerationSourceError( + 'There is no target constructor in ${mapping.target}', + ); + } + + final targetClassGetters = (mapping.target).getAllGetters(); + + // Map constructor parameters + for (var i = 0; i < targetConstructor.parameters.length; i++) { + final param = targetConstructor.parameters.elementAtOrNull(i); + + if (param == null) continue; + + final paramPosition = param.isPositional ? i : null; + final constructorAssignment = ConstructorAssignment(param: param, position: paramPosition); + + final fieldMapping = mapping.tryGetFieldMapping(param.name); + + // Handles renaming. + final from = fieldMapping?.from; + final sourceFieldName = from ?? param.name; + + // Custom mapping has precedence. + if (fieldMapping?.hasCustomMapping() ?? false) { + final targetField = targetClassGetters.firstWhereOrNull((f) => f.displayName == fieldMapping?.field); + + if (targetField == null) continue; + + if (mapping.fieldShouldBeIgnored(targetField.displayName)) { + _assertParamFieldCanBeIgnored(param, targetField); + } + + final sourceAssignment = SourceAssignment( + sourceField: null, + targetField: targetField, + targetConstructorParam: constructorAssignment, + fieldMapping: mapping.tryGetFieldMapping(targetField.displayName), + ); + + mappedTargetConstructorParams.add(sourceAssignment); + mappedSourceFieldNames.add(param.name); + } + // Source field has the same name as target parameter or is renamed using [from]. + else if (sourceFields.containsKey(sourceFieldName)) { + final sourceField = sourceFields[sourceFieldName]!; + + final targetField = from == null + // find target field based on matching source field + ? targetClassGetters.firstWhereOrNull((field) => field.displayName == sourceField.displayName) + // support custom field rename mapping + : targetClassGetters.firstWhereOrNull((field) => field.displayName == fieldMapping?.field); + + if (targetField == null) continue; + + if (mapping.fieldShouldBeIgnored(targetField.displayName)) { + _assertParamFieldCanBeIgnored(param, sourceField); + } + + final sourceAssignment = SourceAssignment( + sourceField: sourceFields[sourceFieldName], + targetField: targetField, + targetConstructorParam: constructorAssignment, + fieldMapping: mapping.tryGetFieldMapping(targetField.displayName), + typeConverters: mapping.typeConverters, + ); + + mappedTargetConstructorParams.add(sourceAssignment); + mappedSourceFieldNames.add(param.name); + } else { + // If not mapped constructor param is optional - skip it + if (param.isOptional) continue; + + final targetField = + (mapping.target).getAllGetters().firstWhereOrNull((field) => field.displayName == param.displayName); + + if (targetField == null && fieldMapping == null) { + throw InvalidGenerationSourceError( + "Can't find mapping for target's constructor parameter: ${param.displayName}. Parameter is required and no mapping or target's class field not found", + ); + } + + notMappedTargetParameters.add( + SourceAssignment( + sourceField: null, + targetField: targetField, + fieldMapping: fieldMapping, + targetConstructorParam: constructorAssignment, + ), + ); + } + } + + _assertNotMappedConstructorParameters(notMappedTargetParameters.map((e) => e.targetConstructorParam!.param)); + + // Prepare and merge mapped and notMapped parameters into Positional and Named arrays + final mappedPositionalParameters = + mappedTargetConstructorParams.where((x) => x.targetConstructorParam?.position != null); + final notMappedPositionalParameters = + notMappedTargetParameters.where((x) => x.targetConstructorParam?.position != null); + + final positionalParameters = [...mappedPositionalParameters, ...notMappedPositionalParameters] + ..sortByCompare((x) => x.targetConstructorParam!.position!, (a, b) => a - b); + + final namedParameters = [ + ...mappedTargetConstructorParams.where((x) => x.targetConstructorParam?.isNamed ?? false), + ...notMappedTargetParameters.where((element) => element.targetConstructorParam?.isNamed ?? false), + ]; + + // Mapped fields into constructor - positional and named + return _mapConstructor( + targetConstructor, + positional: positionalParameters, + named: namedParameters, + ); + } + + void _assertParamFieldCanBeIgnored(ParameterElement param, PropertyAccessorElement sourceField) { + final sourceFieldName = sourceField.getDisplayString(withNullability: true); + if (param.isPositional && param.type.isNotNullable) { + throw InvalidGenerationSourceError( + "Can't ignore field '$sourceFieldName' as it is positional not-nullable parameter", + ); + } + + if (param.isRequiredNamed && param.type.isNotNullable) { + throw InvalidGenerationSourceError( + "Can't ignore field '$sourceFieldName' as it is required named not-nullable parameter", + ); + } + } + + Expression _mapSetterFields({ + required List alreadyMapped, + required Map sourceFields, + required Expression constructorExpression, + }) { + final targetSetters = mapping.target.getAllSetters(); + + final potentialSetterFields = sourceFields.keys.where((field) => !alreadyMapped.contains(field)).toList(); + final fields = potentialSetterFields + .map((key) => sourceFields[key]) + .whereNotNull() + // Use only those that match. + .where((accessor) => targetSetters.any((targetAccessor) => targetAccessor.displayName == accessor.displayName)) + // Skip ignored fields. + .where((accessor) => !mapping.fieldShouldBeIgnored(accessor.displayName)) + .toList(); + + if (fields.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); + + if (targetField == null) continue; + + // Assign result.X = model.X + cascadedAssignments = cascadedAssignments.cascade(sourceField.displayName).assign( + ValueAssignmentBuilder( + mapperConfig: mapperConfig, + mapping: mapping, + assignment: SourceAssignment( + sourceField: sourceField, + targetField: targetField, + ), + usedNullableMethodCallback: usedNullableMethodCallback, + ).build(), + ); + } + + return cascadedAssignments; + } + + /// Tries to find best constructor for mapping. + /// + /// Returns a constructor with the most parameter count. + /// Prefer non factory constructors over factory ones. + ConstructorElement? _findBestConstructor(InterfaceType classType, {String? forcedConstructor}) { + if (forcedConstructor != null) { + final selectedConstructor = classType.constructors.firstWhereOrNull((c) => c.name == forcedConstructor); + if (selectedConstructor != null) return selectedConstructor; + + log.warning( + "Couldn't find constructor '$forcedConstructor', fall-backing to using the most fitted one instead.", + ); + } + + final allConstructors = classType.constructors.where((c) => !c.isPrivate); + + // Sort constructors by number of parameters, descending. + int sortConstructors(ConstructorElement a, ConstructorElement b) => + -a.parameters.length.compareTo(b.parameters.length); + + final nonFactoryConstructors = allConstructors.where((c) => !c.isFactory).sorted(sortConstructors); + final factoryConstructors = + allConstructors.where((c) => c.isFactory && c.name != 'fromJson').sorted(sortConstructors); + + // Prefers non factory constructors over factory ones. + return [...nonFactoryConstructors, ...factoryConstructors].firstOrNull; + } + + Expression _mapConstructor( + ConstructorElement targetConstructor, { + required List positional, + required List named, + }) { + final constructorName = targetConstructor.displayName; + + return EmitterHelper.current.refer(constructorName, targetConstructor.library.identifier).newInstance( + positional.map( + (assignment) => ValueAssignmentBuilder( + mapperConfig: mapperConfig, + mapping: mapping, + assignment: assignment, + usedNullableMethodCallback: usedNullableMethodCallback, + ).build(), + ), + { + for (final assignment in named) + assignment.targetConstructorParam!.param.name: ValueAssignmentBuilder( + mapperConfig: mapperConfig, + mapping: mapping, + assignment: assignment, + usedNullableMethodCallback: usedNullableMethodCallback, + ).build(), + }, + ); + } + + void _assertNotMappedConstructorParameters(Iterable notMapped) { + for (final param in notMapped) { + if (param.isPositional && param.type.isNotNullable) { + throw InvalidGenerationSourceError( + "Can't generate mapping $mapping as there is non mapped not-nullable positional parameter ${param.displayName}", + ); + } + + if (param.isRequiredNamed && param.type.isNotNullable) { + if (param.type.isDartCoreList) return; + throw InvalidGenerationSourceError( + "Can't generate mapping $mapping as there is non mapped not-nullable required named parameter ${param.displayName}", + ); + } + } + } +} diff --git a/packages/auto_mappr/lib/src/builder/enum_assignment_builder.dart b/packages/auto_mappr/lib/src/builder/map_bodies/enum_body_builder.dart similarity index 58% rename from packages/auto_mappr/lib/src/builder/enum_assignment_builder.dart rename to packages/auto_mappr/lib/src/builder/map_bodies/enum_body_builder.dart index 34318b1a..cc2bb8b4 100644 --- a/packages/auto_mappr/lib/src/builder/enum_assignment_builder.dart +++ b/packages/auto_mappr/lib/src/builder/map_bodies/enum_body_builder.dart @@ -1,29 +1,28 @@ import 'package:analyzer/dart/element/element.dart'; -import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; -import 'package:auto_mappr/src/models/models.dart'; +import 'package:auto_mappr/src/builder/map_bodies/map_body_builder_base.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:code_builder/code_builder.dart'; import 'package:source_gen/source_gen.dart'; -class EnumAssignmentBuilder { - final AutoMapprConfig mapperConfig; - final TypeMapping mapping; - - const EnumAssignmentBuilder({ - required this.mapperConfig, - required this.mapping, +class EnumBodyBuilder extends MapBodyBuilderBase { + const EnumBodyBuilder({ + required super.mapperConfig, + required super.mapping, + required super.usedNullableMethodCallback, }); + @override Code build() { final isSourceEnum = mapping.source.element is EnumElement; final isTargetEnum = mapping.target.element is EnumElement; + // Check that both source and target enums are enums. if (!isSourceEnum || !isTargetEnum) { + final sourceDisplay = mapping.source.getDisplayString(withNullability: true); + final targetDisplay = mapping.target.getDisplayString(withNullability: true); + throw InvalidGenerationSourceError( - 'Failed to map $mapping because ${isSourceEnum ? 'target ${mapping.target.getDisplayStringWithLibraryAlias( - config: mapperConfig, - )}' : 'source ${mapping.source.getDisplayStringWithLibraryAlias( - config: mapperConfig, - )}'} is not an enum.', + 'Failed to map $mapping because ${isSourceEnum ? 'target $targetDisplay' : 'source $sourceDisplay'} is not an enum.', ); } @@ -36,18 +35,14 @@ class EnumAssignmentBuilder { final sourceIsSubset = targetValues.containsAll(sourceValues); if (!sourceIsSubset && !mapping.hasWhenNullDefault()) { + final sourceDisplay = mapping.source.getDisplayString(withNullability: true); + final targetDisplay = mapping.target.getDisplayString(withNullability: true); throw InvalidGenerationSourceError( - "Can't map enum ${mapping.source.getDisplayStringWithLibraryAlias( - config: mapperConfig, - )} into ${mapping.target.getDisplayStringWithLibraryAlias( - config: mapperConfig, - )}. Target enum is not superset of source enum.", + "Can't map enum $sourceDisplay into $targetDisplay. Target enum is not superset of source enum.", ); } - final targetReference = refer( - mapping.target.getDisplayStringWithLibraryAlias(config: mapperConfig), - ); + final targetReference = EmitterHelper.current.typeRefer(type: mapping.target); return targetReference .property('values') @@ -57,11 +52,16 @@ class EnumAssignmentBuilder { { if (mapping.hasWhenNullDefault()) 'orElse': refer( - '() => ${mapping.whenSourceIsNullExpression!.accept(DartEmitter())}', + '() => ${mapping.whenSourceIsNullExpression!.accept(EmitterHelper.current.emitter)}', ), }, ) .returned .statement; } + + @override + bool canProcess() { + return mapping.isEnumMapping; + } } diff --git a/packages/auto_mappr/lib/src/builder/map_bodies/map_bodies.dart b/packages/auto_mappr/lib/src/builder/map_bodies/map_bodies.dart new file mode 100644 index 00000000..3978c068 --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/map_bodies/map_bodies.dart @@ -0,0 +1,2 @@ +export 'class_body_builder.dart'; +export 'enum_body_builder.dart'; diff --git a/packages/auto_mappr/lib/src/builder/map_bodies/map_body_builder_base.dart b/packages/auto_mappr/lib/src/builder/map_bodies/map_body_builder_base.dart new file mode 100644 index 00000000..259a0c4a --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/map_bodies/map_body_builder_base.dart @@ -0,0 +1,20 @@ +import 'package:auto_mappr/src/models/auto_mappr_config.dart'; +import 'package:auto_mappr/src/models/type_mapping.dart'; +import 'package:code_builder/code_builder.dart'; + +abstract class MapBodyBuilderBase { + final AutoMapprConfig mapperConfig; + final TypeMapping mapping; + + final void Function(TypeMapping? mapping)? usedNullableMethodCallback; + + const MapBodyBuilderBase({ + required this.mapperConfig, + required this.mapping, + required this.usedNullableMethodCallback, + }); + + Code build(); + + bool canProcess(); +} diff --git a/packages/auto_mappr/lib/src/builder/map_model_body_method_builder.dart b/packages/auto_mappr/lib/src/builder/map_model_body_method_builder.dart index 27c1f96f..d842306b 100644 --- a/packages/auto_mappr/lib/src/builder/map_model_body_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/map_model_body_method_builder.dart @@ -1,17 +1,9 @@ -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:auto_mappr/src/builder/enum_assignment_builder.dart'; -import 'package:auto_mappr/src/builder/value_assignment_builder.dart'; -import 'package:auto_mappr/src/extensions/element_extension.dart'; +import 'package:auto_mappr/src/builder/map_bodies/map_bodies.dart'; import 'package:auto_mappr/src/extensions/expression_extension.dart'; -import 'package:auto_mappr/src/extensions/interface_type_extension.dart'; import 'package:auto_mappr/src/models/models.dart'; -import 'package:build/build.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:collection/collection.dart'; -import 'package:source_gen/source_gen.dart'; +/// Creates mapping body. class MapModelBodyMethodBuilder { final AutoMapprConfig mapperConfig; final TypeMapping mapping; @@ -28,340 +20,36 @@ class MapModelBodyMethodBuilder { Code build() { final block = BlockBuilder(); - final sourceFields = _getAllReadableFields(classType: mapping.source); - - // Name of the source field names which can be mapped into constructor field - final mappedSourceFieldNames = []; - // Input as local model. block.statements.add(declareFinal('model').assign(refer('input')).statement); // Add handling of whenSourceIsNull. block.statements.add(_whenModelIsNullHandling()); // Is there an enum involved in the mapping? - if (mapping.isEnumMapping) { - final enumBuilder = EnumAssignmentBuilder(mapperConfig: mapperConfig, mapping: mapping); - - final assignment = enumBuilder.build(); - - block.statements.add(assignment); + final enumMapBodyBuilder = EnumBodyBuilder( + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: usedNullableMethodCallback, + ); + if (enumMapBodyBuilder.canProcess()) { + block.statements.add(enumMapBodyBuilder.build()); return block.build(); } - // Map fields using a constructor. - // Returns an constructor call without `;`. - // - // One( - // usingConstructor1: model.usingConstructor1, - // usingConstructor2: model.usingConstructor2, - // ) - final constructorExpression = _processConstructorMapping( - mappedSourceFieldNames: mappedSourceFieldNames, - sourceFields: sourceFields, - ); - - // Map fields not mapped directly in constructor as setters if possible. - // - // Code like: - // - // - // ..withoutConstructor1 = model.withoutConstructor1 - // ..withoutConstructor2 = model.withoutConstructor2 - // ..withoutConstructor3 = model.withoutConstructor3 - final constructorWithSetters = _mapSetterFields( - alreadyMapped: mappedSourceFieldNames, - sourceFields: sourceFields, - constructorExpression: constructorExpression, + final classMapBodyBuilder = ClassBodyBuilder( + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: usedNullableMethodCallback, ); // Return a constructor call // optionally with cascaded setter assignments. - block.statements.add(constructorWithSetters.returned.statement); + block.statements.add(classMapBodyBuilder.build()); return block.build(); } - void _assertParamFieldCanBeIgnored(ParameterElement param, PropertyAccessorElement sourceField) { - final sourceFieldName = sourceField.getDisplayString(withNullability: true); - if (param.isPositional && param.type.nullabilitySuffix != NullabilitySuffix.question) { - throw InvalidGenerationSourceError( - "Can't ignore field '$sourceFieldName' as it is positional not-nullable parameter", - ); - } - - if (param.isRequiredNamed && param.type.nullabilitySuffix != NullabilitySuffix.question) { - throw InvalidGenerationSourceError( - "Can't ignore field '$sourceFieldName' as it is required named not-nullable parameter", - ); - } - } - - void _assertNotMappedConstructorParameters(Iterable notMapped) { - for (final param in notMapped) { - if (param.isPositional && param.type.nullabilitySuffix != NullabilitySuffix.question) { - throw InvalidGenerationSourceError( - "Can't generate mapping $mapping as there is non mapped not-nullable positional parameter ${param.displayName}", - ); - } - - if (param.isRequiredNamed && param.type.nullabilitySuffix != NullabilitySuffix.question) { - if (param.type.isDartCoreList) return; - throw InvalidGenerationSourceError( - "Can't generate mapping $mapping as there is non mapped not-nullable required named parameter ${param.displayName}", - ); - } - } - } - - Expression _processConstructorMapping({ - required List mappedSourceFieldNames, - required Map sourceFields, - }) { - final mappedTargetConstructorParams = []; - final notMappedTargetParameters = []; - - final targetConstructor = _findBestConstructor(mapping.target, forcedConstructor: mapping.constructor); - - if (targetConstructor == null) { - throw InvalidGenerationSourceError( - 'There is no target constructor in ${mapping.target}', - ); - } - - final targetClassGetters = (mapping.target).getAllGetters(); - - // Map constructor parameters - for (var i = 0; i < targetConstructor.parameters.length; i++) { - final param = targetConstructor.parameters.elementAtOrNull(i); - - if (param == null) continue; - - final paramPosition = param.isPositional ? i : null; - final constructorAssignment = ConstructorAssignment(param: param, position: paramPosition); - - final fieldMapping = mapping.tryGetFieldMapping(param.name); - - // Handles renaming. - final from = fieldMapping?.from; - final sourceFieldName = from ?? param.name; - - // Custom mapping has precedence. - if (fieldMapping?.hasCustomMapping() ?? false) { - final targetField = - targetClassGetters.firstWhereOrNull((targetField) => targetField.displayName == fieldMapping?.field); - - if (targetField == null) continue; - - if (mapping.fieldShouldBeIgnored(targetField.displayName)) { - _assertParamFieldCanBeIgnored(param, targetField); - } - - final sourceAssignment = SourceAssignment( - sourceField: null, - targetField: targetField, - targetConstructorParam: constructorAssignment, - fieldMapping: mapping.tryGetFieldMapping(targetField.displayName), - typeMapping: mapping, - config: mapperConfig, - ); - - mappedTargetConstructorParams.add(sourceAssignment); - mappedSourceFieldNames.add(param.name); - } - // Source field has the same name as target parameter or is renamed using [from]. - else if (sourceFields.containsKey(sourceFieldName)) { - final sourceField = sourceFields[sourceFieldName]!; - - final targetField = from == null - // find target field based on matching source field - ? targetClassGetters.firstWhereOrNull((field) => field.displayName == sourceField.displayName) - // support custom field rename mapping - : targetClassGetters.firstWhereOrNull((field) => field.displayName == fieldMapping?.field); - - if (targetField == null) continue; - - if (mapping.fieldShouldBeIgnored(targetField.displayName)) { - _assertParamFieldCanBeIgnored(param, sourceField); - } - - final sourceAssignment = SourceAssignment( - sourceField: sourceFields[sourceFieldName], - targetField: targetField, - targetConstructorParam: constructorAssignment, - fieldMapping: mapping.tryGetFieldMapping(targetField.displayName), - typeMapping: mapping, - config: mapperConfig, - ); - - mappedTargetConstructorParams.add(sourceAssignment); - mappedSourceFieldNames.add(param.name); - } else { - // If not mapped constructor param is optional - skip it - if (param.isOptional) continue; - - final targetField = - (mapping.target).getAllGetters().firstWhereOrNull((field) => field.displayName == param.displayName); - - final fieldMapping = mapping.tryGetFieldMapping(param.displayName); - - if (targetField == null && fieldMapping == null) { - throw InvalidGenerationSourceError( - "Can't find mapping for target's constructor parameter: ${param.displayName}. Parameter is required and no mapping or target's class field not found", - ); - } - - notMappedTargetParameters.add( - SourceAssignment( - sourceField: null, - targetField: targetField, - fieldMapping: fieldMapping, - targetConstructorParam: constructorAssignment, - typeMapping: mapping, - config: mapperConfig, - ), - ); - } - } - - _assertNotMappedConstructorParameters(notMappedTargetParameters.map((e) => e.targetConstructorParam!.param)); - - // Prepare and merge mapped and notMapped parameters into Positional and Named arrays - final mappedPositionalParameters = - mappedTargetConstructorParams.where((x) => x.targetConstructorParam?.position != null); - final notMappedPositionalParameters = - notMappedTargetParameters.where((x) => x.targetConstructorParam?.position != null); - - final positionalParameters = [...mappedPositionalParameters, ...notMappedPositionalParameters] - ..sortByCompare((x) => x.targetConstructorParam!.position!, (a, b) => a - b); - - final namedParameters = [ - ...mappedTargetConstructorParams.where((x) => x.targetConstructorParam?.isNamed ?? false), - ...notMappedTargetParameters.where((element) => element.targetConstructorParam?.isNamed ?? false), - ]; - - // Mapped fields into constructor - positional and named - return _mapConstructor( - targetConstructor, - positional: positionalParameters, - named: namedParameters, - ); - } - - Expression _mapConstructor( - ConstructorElement targetConstructor, { - required List positional, - required List named, - }) { - final alias = targetConstructor.enclosingElement.getLibraryAlias(config: mapperConfig); - final constructorName = targetConstructor.displayName; - - return refer('$alias$constructorName').newInstance( - positional.map( - (assignment) => ValueAssignmentBuilder( - mapperConfig: mapperConfig, - mapping: mapping, - assignment: assignment, - usedNullableMethodCallback: usedNullableMethodCallback, - ).build(), - ), - { - for (final assignment in named) - assignment.targetConstructorParam!.param.name: ValueAssignmentBuilder( - mapperConfig: mapperConfig, - mapping: mapping, - assignment: assignment, - usedNullableMethodCallback: usedNullableMethodCallback, - ).build(), - }, - ); - } - - Expression _mapSetterFields({ - required List alreadyMapped, - required Map sourceFields, - required Expression constructorExpression, - }) { - final targetSetters = mapping.target.getAllSetters(); - - final potentialSetterFields = sourceFields.keys.where((field) => !alreadyMapped.contains(field)).toList(); - final fields = potentialSetterFields - .map((key) => sourceFields[key]) - .whereNotNull() - // Use only those that match. - .where((accessor) => targetSetters.any((targetAccessor) => targetAccessor.displayName == accessor.displayName)) - // Skip ignored fields. - .where((accessor) => !mapping.fieldShouldBeIgnored(accessor.displayName)) - .toList(); - - if (fields.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); - - if (targetField == null) continue; - - // Assign result.X = model.X - cascadedAssignments = cascadedAssignments.cascade(sourceField.displayName).assign( - ValueAssignmentBuilder( - mapperConfig: mapperConfig, - mapping: mapping, - assignment: SourceAssignment( - sourceField: sourceField, - targetField: targetField, - typeMapping: mapping, - config: mapperConfig, - ), - usedNullableMethodCallback: usedNullableMethodCallback, - ).build(), - ); - } - - return cascadedAssignments; - } - - /// Returns all public fields (instance or static) that have a getter. - Map _getAllReadableFields({ - required InterfaceType classType, - }) { - final fieldsWithGetter = classType.getAllGetters(); - - return {for (final field in fieldsWithGetter) field.name: field}; - } - - /// Tries to find best constructor for mapping. - /// - /// Returns a constructor with the most parameter count. - /// Prefer non factory constructors over factory ones. - ConstructorElement? _findBestConstructor(InterfaceType classType, {String? forcedConstructor}) { - if (forcedConstructor != null) { - final selectedConstructor = classType.constructors.firstWhereOrNull((c) => c.name == forcedConstructor); - if (selectedConstructor != null) return selectedConstructor; - - log.warning( - "Couldn't find constructor '$forcedConstructor', fall-backing to using the most fitted one instead.", - ); - } - - final allConstructors = classType.constructors.where((c) => !c.isPrivate); - - // Sort constructors by number of parameters, descending. - int sortConstructors(ConstructorElement a, ConstructorElement b) => - -a.parameters.length.compareTo(b.parameters.length); - - final nonFactoryConstructors = allConstructors.where((c) => !c.isFactory).sorted(sortConstructors); - final factoryConstructors = - allConstructors.where((c) => c.isFactory && c.name != 'fromJson').sorted(sortConstructors); - - // Prefers non factory constructors over factory ones. - return [...nonFactoryConstructors, ...factoryConstructors].firstOrNull; - } - Code _whenModelIsNullHandling() { final ifConditionExpression = refer('model').equalTo(literalNull); diff --git a/packages/auto_mappr/lib/src/builder/methods/callable_method.dart b/packages/auto_mappr/lib/src/builder/methods/callable_method.dart index c482064c..22df0c3d 100644 --- a/packages/auto_mappr/lib/src/builder/methods/callable_method.dart +++ b/packages/auto_mappr/lib/src/builder/methods/callable_method.dart @@ -1,10 +1,8 @@ import 'package:code_builder/code_builder.dart'; // ignore: one_member_abstracts, it is implemented in builders -abstract class CallableMethod { +abstract interface class CallableMethod { const CallableMethod(); Expression methodCall({Map namedArguments = const {}}); - - } diff --git a/packages/auto_mappr/lib/src/builder/methods/callable_property.dart b/packages/auto_mappr/lib/src/builder/methods/callable_property.dart index bd7315b9..6a260121 100644 --- a/packages/auto_mappr/lib/src/builder/methods/callable_property.dart +++ b/packages/auto_mappr/lib/src/builder/methods/callable_property.dart @@ -1,7 +1,7 @@ import 'package:code_builder/code_builder.dart'; // ignore: one_member_abstracts, it is implemented in builders -abstract class CallableProperty { +abstract interface class CallableProperty { const CallableProperty(); Expression propertyCall({ diff --git a/packages/auto_mappr/lib/src/builder/methods/can_convert_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/can_convert_method_builder.dart index 82c404df..7cd79b23 100644 --- a/packages/auto_mappr/lib/src/builder/methods/can_convert_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/can_convert_method_builder.dart @@ -6,7 +6,7 @@ import 'package:built_collection/built_collection.dart'; import 'package:code_builder/code_builder.dart'; class CanConvertMethodBuilder extends MethodBuilderBase implements CallableMethod, CallableProperty { - CanConvertMethodBuilder(super.config); + const CanConvertMethodBuilder(super.config); @override Method buildMethod() { @@ -63,7 +63,7 @@ class CanConvertMethodBuilder extends MethodBuilderBase implements CallableMetho condition: refer('recursive'), ifBody: ExpressionExtension.forStatement( item: refer('mappr'), - iterable: refer('_modules'), + iterable: refer(MethodBuilderBase.delegatesField), body: ExpressionExtension.ifStatement( condition: CanConvertMethodBuilder(config).propertyCall(on: refer('mappr')), ifBody: literalTrue.returned.statement, diff --git a/packages/auto_mappr/lib/src/builder/methods/convert_iterable_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/convert_iterable_method_builder.dart index 19c38ca2..8cf7b7c6 100644 --- a/packages/auto_mappr/lib/src/builder/methods/convert_iterable_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/convert_iterable_method_builder.dart @@ -1,6 +1,7 @@ import 'package:auto_mappr/src/builder/methods/can_convert_method_builder.dart'; import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:built_collection/built_collection.dart'; import 'package:code_builder/code_builder.dart'; @@ -10,7 +11,7 @@ class ConvertIterableMethodBuilder extends MethodBuilderBase { final String wrapper; final String? iterableTransformer; - ConvertIterableMethodBuilder( + const ConvertIterableMethodBuilder( super.config, { required this.wrapper, this.iterableTransformer, @@ -31,10 +32,11 @@ class ConvertIterableMethodBuilder extends MethodBuilderBase { Parameter( (p) => p ..name = 'model' - ..type = Reference('Iterable<${MethodBuilderBase.nullableSourceTypeReference.accept(DartEmitter())}>'), + ..type = + Reference('Iterable<${MethodBuilderBase.nullableSourceTypeReference.accept(EmitterHelper.current.emitter)}>'), ), ) - ..returns = Reference('$wrapper<${MethodBuilderBase.targetTypeReference.accept(DartEmitter())}>') + ..returns = Reference('$wrapper<${MethodBuilderBase.targetTypeReference.accept(EmitterHelper.current.emitter)}>') ..body = buildBody(), ); } @@ -81,7 +83,7 @@ class ConvertIterableMethodBuilder extends MethodBuilderBase { block.statements.add( ExpressionExtension.forStatement( item: refer('mappr'), - iterable: refer('_modules'), + iterable: refer(MethodBuilderBase.delegatesField), body: ExpressionExtension.ifStatement( condition: CanConvertMethodBuilder(config).propertyCall(on: refer('mappr')), ifBody: refer('mappr').property('convert$wrapper').call([refer('model')], {}, []).returned.statement, diff --git a/packages/auto_mappr/lib/src/builder/methods/convert_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/convert_method_builder.dart index 8b59ff69..9c78a0c1 100644 --- a/packages/auto_mappr/lib/src/builder/methods/convert_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/convert_method_builder.dart @@ -5,7 +5,7 @@ import 'package:built_collection/built_collection.dart'; import 'package:code_builder/code_builder.dart'; class ConvertMethodBuilder extends MethodBuilderBase { - ConvertMethodBuilder(super.config); + const ConvertMethodBuilder(super.config); @override Method buildMethod() { @@ -56,7 +56,7 @@ class ConvertMethodBuilder extends MethodBuilderBase { block.statements.add( ExpressionExtension.forStatement( item: refer('mappr'), - iterable: refer('_modules'), + iterable: refer(MethodBuilderBase.delegatesField), body: ExpressionExtension.ifStatement( condition: CanConvertMethodBuilder(config).propertyCall(on: refer('mappr')), ifBody: refer('mappr').property('convert').call([refer('model')], {}, []).nullChecked.returned.statement, diff --git a/packages/auto_mappr/lib/src/builder/methods/mapping_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/mapping_method_builder.dart index 9ca9631b..de2cbaff 100644 --- a/packages/auto_mappr/lib/src/builder/methods/mapping_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/mapping_method_builder.dart @@ -1,5 +1,7 @@ import 'package:auto_mappr/src/builder/map_model_body_method_builder.dart'; import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; +import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:auto_mappr/src/models/type_mapping.dart'; import 'package:code_builder/code_builder.dart'; @@ -8,7 +10,7 @@ class MappingMethodBuilder extends MethodBuilderBase { final bool nullable; final void Function(TypeMapping? mapping)? usedNullableMethodCallback; - MappingMethodBuilder( + const MappingMethodBuilder( super.config, { required this.mapping, this.nullable = false, @@ -17,49 +19,35 @@ class MappingMethodBuilder extends MethodBuilderBase { @override Method buildMethod() { - // TODO(later): rework mapping to this builder + var returnType = EmitterHelper.current.typeRefer(type: mapping.target); + + if (nullable) { + returnType = returnType.nullabled(); + } return Method( (b) => b - ..name = mapping.mappingMethodName(config: config) + ..name = + nullable ? mapping.nullableMappingMethodName(config: config) : mapping.mappingMethodName(config: config) ..requiredParameters.addAll([ Parameter( (p) => p ..name = 'input' - ..type = refer('${mapping.source.getDisplayString(withNullability: false)}?'), + ..type = EmitterHelper.current.typeRefer(type: mapping.source).nullabled(), ), ]) - ..returns = refer(mapping.target.getDisplayString(withNullability: false)) - ..body = MapModelBodyMethodBuilder( - mapping: mapping, - mapperConfig: config, - usedNullableMethodCallback: usedNullableMethodCallback, - ).build(), + ..returns = returnType + ..body = buildBody(), ); - - // nullable - // return Method( - // (b) => b - // ..name = mapping.nullableMappingMethodName - // ..requiredParameters.addAll([ - // Parameter( - // (p) => p - // ..name = 'input' - // ..type = refer('${mapping.source.getDisplayString(withNullability: false)}?'), - // ) - // ]) - // ..returns = refer('${mapping.target.getDisplayString(withNullability: true)}?') - // ..body = MapModelBodyMethodBuilder( - // mapping: mapping, - // mapperConfig: config, - // nullable: true, - // ).build(), - // ); } @override Code buildBody() { - // TODO(later): rework - return const Code('T'); + return MapModelBodyMethodBuilder( + mapping: mapping, + mapperConfig: config, + usedNullableMethodCallback: usedNullableMethodCallback, + nullable: nullable, + ).build(); } } diff --git a/packages/auto_mappr/lib/src/builder/methods/method_builder_base.dart b/packages/auto_mappr/lib/src/builder/methods/method_builder_base.dart index f61d9240..ac2d1a7c 100644 --- a/packages/auto_mappr/lib/src/builder/methods/method_builder_base.dart +++ b/packages/auto_mappr/lib/src/builder/methods/method_builder_base.dart @@ -1,6 +1,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:auto_mappr/src/models/auto_mappr_config.dart'; import 'package:auto_mappr/src/models/type_mapping.dart'; import 'package:built_collection/built_collection.dart'; @@ -9,6 +10,8 @@ import 'package:meta/meta.dart'; /// Base class for method builders. abstract class MethodBuilderBase { + static const delegatesField = '_delegates'; + static const sourceKey = 'SOURCE'; static const sourceTypeReference = Reference(sourceKey); static const nullableSourceTypeReference = Reference('$sourceKey?'); @@ -22,19 +25,15 @@ abstract class MethodBuilderBase { static final ListBuilder overrideAnnotation = ListBuilder([const Reference('override')]); final AutoMapprConfig config; - final Set nullableMappings; - MethodBuilderBase(this.config) : nullableMappings = {}; + const MethodBuilderBase(this.config); static String constructConvertMethodName({ required DartType source, required DartType target, required AutoMapprConfig config, }) => - '_map__${source.toConvertMethodName(withNullability: false, config: config)}__To__${target.toConvertMethodName( - withNullability: false, - config: config, - )}'; + '_map_${source.toConvertMethodName()}_To_${target.toConvertMethodName()}'; static String constructNullableConvertMethodName({ required DartType source, @@ -47,16 +46,6 @@ abstract class MethodBuilderBase { config: config, )}_Nullable'; - bool shouldGenerateNullableMappingMethod(TypeMapping mapping) { - return nullableMappings.contains(mapping); - } - - void usedNullableMappingMethod(TypeMapping? mapping) { - if (mapping == null) return; - - final _ = nullableMappings.add(mapping); - } - Method buildMethod(); @protected @@ -78,8 +67,8 @@ abstract class MethodBuilderBase { required Reference targetTypeOfReference, required Spec inIfExpression, }) { - final sourceName = mapping.source.getDisplayStringWithLibraryAlias(config: config); - final targetName = mapping.target.getDisplayStringWithLibraryAlias(config: config); + final sourceName = EmitterHelper.current.typeReferEmitted(type: mapping.source); + final targetName = EmitterHelper.current.typeReferEmitted(type: mapping.target); final modelIsTypeExpression = sourceTypeOfReference .equalTo(refer('_typeOf<$sourceName>()')) diff --git a/packages/auto_mappr/lib/src/builder/methods/private_convert_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/private_convert_method_builder.dart index 56fe1ae0..28613960 100644 --- a/packages/auto_mappr/lib/src/builder/methods/private_convert_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/private_convert_method_builder.dart @@ -1,10 +1,10 @@ import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; -import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:code_builder/code_builder.dart'; class PrivateConvertMethodBuilder extends MethodBuilderBase { - PrivateConvertMethodBuilder(super.config); + const PrivateConvertMethodBuilder(super.config); @override Method buildMethod() { @@ -74,8 +74,7 @@ class PrivateConvertMethodBuilder extends MethodBuilderBase { refer(mapping.mappingMethodName(config: config)) .call( [ - refer('model') - .asA(refer('${mapping.source.getDisplayStringWithLibraryAlias(config: config)}?')), + refer('model').asA(EmitterHelper.current.typeRefer(type: mapping.source).nullabled()), ], ) .asA(MethodBuilderBase.targetTypeReference) @@ -89,7 +88,11 @@ class PrivateConvertMethodBuilder extends MethodBuilderBase { block.addExpression( refer('Exception').newInstance( - [refer("'No \${model.runtimeType} -> \$${targetTypeOfReference.accept(DartEmitter())} mapping.'")], + [ + refer( + "'No \${model.runtimeType} -> \$${targetTypeOfReference.accept(EmitterHelper.current.emitter)} mapping.'", + ), + ], ).thrown, ); diff --git a/packages/auto_mappr/lib/src/builder/methods/private_modules_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/private_modules_method_builder.dart index a01ad827..15963b97 100644 --- a/packages/auto_mappr/lib/src/builder/methods/private_modules_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/private_modules_method_builder.dart @@ -1,15 +1,19 @@ import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:auto_mappr/src/helpers/urls.dart'; import 'package:code_builder/code_builder.dart'; class PrivateModulesMethodBuilder extends MethodBuilderBase { - PrivateModulesMethodBuilder(super.config); + const PrivateModulesMethodBuilder(super.config); @override Method buildMethod() { + final interfaceRefer = EmitterHelper.current.referEmitted('AutoMapprInterface', Urls.annotationPackageUrl); + return Method( (builder) => builder - ..name = '_modules' - ..returns = refer('List') + ..name = MethodBuilderBase.delegatesField + ..returns = refer('List<$interfaceRefer>') ..lambda = true ..type = MethodType.getter ..body = buildBody(), @@ -18,6 +22,6 @@ class PrivateModulesMethodBuilder extends MethodBuilderBase { @override Code buildBody() { - return refer('const ${(config.modulesCode ?? literalList([])).accept(DartEmitter())}').code; + return refer('const ${(config.modulesCode ?? literalList([])).accept(EmitterHelper.current.emitter)}').code; } } diff --git a/packages/auto_mappr/lib/src/builder/methods/try_convert_iterable_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/try_convert_iterable_method_builder.dart index 90d72efe..7e49756c 100644 --- a/packages/auto_mappr/lib/src/builder/methods/try_convert_iterable_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/try_convert_iterable_method_builder.dart @@ -1,6 +1,7 @@ import 'package:auto_mappr/src/builder/methods/can_convert_method_builder.dart'; import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:built_collection/built_collection.dart'; import 'package:code_builder/code_builder.dart'; @@ -10,7 +11,7 @@ class TryConvertIterableMethodBuilder extends MethodBuilderBase { final String wrapper; final String? iterableTransformer; - TryConvertIterableMethodBuilder( + const TryConvertIterableMethodBuilder( super.config, { required this.wrapper, this.iterableTransformer, @@ -34,10 +35,12 @@ class TryConvertIterableMethodBuilder extends MethodBuilderBase { Parameter( (p) => p ..name = 'model' - ..type = Reference('Iterable<${MethodBuilderBase.nullableSourceTypeReference.accept(DartEmitter())}>'), + ..type = + Reference('Iterable<${MethodBuilderBase.nullableSourceTypeReference.accept(EmitterHelper.current.emitter)}>'), ), ) - ..returns = Reference('$wrapper<${MethodBuilderBase.nullableTargetTypeReference.accept(DartEmitter())}>') + ..returns = + Reference('$wrapper<${MethodBuilderBase.nullableTargetTypeReference.accept(EmitterHelper.current.emitter)}>') ..body = buildBody(), ); } @@ -90,7 +93,7 @@ class TryConvertIterableMethodBuilder extends MethodBuilderBase { block.statements.add( ExpressionExtension.forStatement( item: refer('mappr'), - iterable: refer('_modules'), + iterable: refer(MethodBuilderBase.delegatesField), body: ExpressionExtension.ifStatement( condition: CanConvertMethodBuilder(config).propertyCall(on: refer('mappr')), ifBody: refer('mappr').property('tryConvert$wrapper').call([refer('model')], {}, []).returned.statement, diff --git a/packages/auto_mappr/lib/src/builder/methods/try_convert_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/try_convert_method_builder.dart index 71809d4a..3f7b6d43 100644 --- a/packages/auto_mappr/lib/src/builder/methods/try_convert_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/try_convert_method_builder.dart @@ -7,7 +7,7 @@ import 'package:code_builder/code_builder.dart'; // modules OK // modules tests OK class TryConvertMethodBuilder extends MethodBuilderBase { - TryConvertMethodBuilder(super.config); + const TryConvertMethodBuilder(super.config); @override Method buildMethod() { @@ -58,7 +58,7 @@ class TryConvertMethodBuilder extends MethodBuilderBase { block.statements.add( ExpressionExtension.forStatement( item: refer('mappr'), - iterable: refer('_modules'), + iterable: refer(MethodBuilderBase.delegatesField), body: ExpressionExtension.ifStatement( condition: CanConvertMethodBuilder(config).propertyCall(on: refer('mappr')), ifBody: refer('mappr').property('tryConvert').call([refer('model')], {}, []).returned.statement, diff --git a/packages/auto_mappr/lib/src/builder/methods/type_of_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/type_of_method_builder.dart index 8cd1a31a..b5acc7fb 100644 --- a/packages/auto_mappr/lib/src/builder/methods/type_of_method_builder.dart +++ b/packages/auto_mappr/lib/src/builder/methods/type_of_method_builder.dart @@ -2,7 +2,7 @@ import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; import 'package:code_builder/code_builder.dart'; class TypeOfMethodBuilder extends MethodBuilderBase { - TypeOfMethodBuilder(super.config); + const TypeOfMethodBuilder(super.config); @override Method buildMethod() { diff --git a/packages/auto_mappr/lib/src/builder/value_assignment_builder.dart b/packages/auto_mappr/lib/src/builder/value_assignment_builder.dart index ffba90d2..e94bd41a 100644 --- a/packages/auto_mappr/lib/src/builder/value_assignment_builder.dart +++ b/packages/auto_mappr/lib/src/builder/value_assignment_builder.dart @@ -1,14 +1,10 @@ -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; +import 'package:auto_mappr/src/builder/assignments/assignments.dart'; import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; -import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:auto_mappr/src/models/models.dart'; -import 'package:build/build.dart'; import 'package:code_builder/code_builder.dart'; -import 'package:collection/collection.dart'; -import 'package:source_gen/source_gen.dart'; +/// Decides how values are assigned. class ValueAssignmentBuilder { final AutoMapprConfig mapperConfig; final TypeMapping mapping; @@ -39,34 +35,74 @@ class ValueAssignmentBuilder { return fieldMapping.apply(assignment); } - final assignNestedObject = !assignment.targetType.isPrimitiveType; + final rightSide = (sourceField.isStatic + // Static field. + ? EmitterHelper.current + .refer(sourceField.enclosingElement.name!, sourceField.enclosingElement.library?.identifier) + // Non static field. + : refer(sourceField.isStatic ? '${sourceField.enclosingElement.name}' : 'model')) + .property(sourceField.name); - if (assignment.shouldAssignIterable()) { - return _assignIterableValue(assignment); - } - - if (assignment.shouldAssignMap()) { - return _assignMapValue(assignment); - } - - final rightSide = - refer(sourceField.isStatic ? '${sourceField.enclosingElement.name}' : 'model').property(sourceField.name); - - if (assignNestedObject) { - return _assignNestedObject( + final assignmentBuilders = [ + // Iterable. + IterableAssignmentBuilder( + assignment: assignment, + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: usedNullableMethodCallback, + ), + // Map. + MapAssignmentBuilder( + assignment: assignment, + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: usedNullableMethodCallback, + ), + // Record. + RecordAssignmentBuilder( + assignment: assignment, + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: usedNullableMethodCallback, + ), + // Nested object. + NestedObjectAssignmentBuilder( + assignment: assignment, + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: usedNullableMethodCallback, source: assignment.sourceType!, target: assignment.targetType, + convertMethodArgument: rightSide, + ), + // Type converter for primitive type. + TypeConverterBuilder( assignment: assignment, + mapperConfig: mapperConfig, + mapping: mapping, + usedNullableMethodCallback: usedNullableMethodCallback, + source: assignment.sourceType!, + target: assignment.targetType, convertMethodArgument: rightSide, - ); + ), + ]; + + // Try to assign value using one of assignment builder. + for (final builder in assignmentBuilders) { + if (builder.canAssign()) { + return builder.buildAssignment(); + } } + // Primitive types (based on DartTypeExtension.isPrimitiveType) + + // When null. if (fieldMapping?.whenNullExpression != null) { return rightSide.ifNullThen(fieldMapping!.whenNullExpression!); } - final sourceNullable = assignment.sourceType!.nullabilitySuffix == NullabilitySuffix.question; - final targetNullable = assignment.targetType.nullabilitySuffix == NullabilitySuffix.question; + final sourceNullable = assignment.sourceType!.isNullable; + final targetNullable = assignment.targetType.isNullable; // BANG operator when Source is nullable and Target not final shouldIgnoreNull = fieldMapping?.ignoreNull ?? @@ -82,336 +118,4 @@ class ValueAssignmentBuilder { return rightSide; } - - Expression _assignIterableValue(SourceAssignment assignment) { - final sourceType = assignment.sourceType!; - final targetType = assignment.targetType; - - final sourceNullable = sourceType.nullabilitySuffix == NullabilitySuffix.question; - final targetNullable = targetType.nullabilitySuffix == NullabilitySuffix.question; - - final sourceIterableType = sourceType.genericParameterTypeOrSelf; - final targetIterableType = targetType.genericParameterTypeOrSelf; - - final shouldFilterNullInSource = sourceIterableType.nullabilitySuffix == NullabilitySuffix.question && - targetIterableType.nullabilitySuffix != NullabilitySuffix.question; - - final assignNestedObject = (!targetIterableType.isPrimitiveType && !targetIterableType.isSpecializedListType) && - (!targetIterableType.isSame(sourceIterableType)); - - // When [sourceIterableType] is nullable and [targetIterableType] is not, remove null values. - final sourceIterableExpression = refer('model').property(assignment.sourceField!.name).maybeWhereIterableNotNull( - condition: shouldFilterNullInSource, - isOnNullable: sourceNullable, - ); - - final defaultIterableValueExpression = targetType.defaultIterableExpression(config: mapperConfig); - - if (assignNestedObject) { - return sourceIterableExpression - // Map complex nested types. - .maybeNullSafeProperty('map', isOnNullable: sourceNullable) - .call( - [_nestedMapCallForIterable(assignment)], - {}, - [ - refer( - targetIterableType.getDisplayStringWithLibraryAlias( - withNullability: true, - config: mapperConfig, - ), - ), - ], - ) - // Call toList, toSet or nothing. - // isOnNullable is false, because if map() was called, the value is non-null - .maybeToIterableCall( - source: sourceType, - target: targetType, - forceCast: true, //map was used so we want force toIterable() call - isOnNullable: false, - ) - // When [sourceNullable], use default value. - .maybeIfNullThen(defaultIterableValueExpression, isOnNullable: sourceNullable && !targetNullable); - } - - return sourceIterableExpression - .maybeToIterableCall( - source: sourceType, - target: targetType, - forceCast: shouldFilterNullInSource, // if whereNotNull was used -> we want to force toIterable() call - isOnNullable: !targetNullable && sourceNullable, - ) - .maybeIfNullThen( - defaultIterableValueExpression, - isOnNullable: !targetNullable && sourceNullable, - ); - } - - Expression _assignMapValue(SourceAssignment assignment) { - final sourceType = assignment.sourceType!; - final targetType = assignment.targetType; - - final sourceNullable = sourceType.nullabilitySuffix == NullabilitySuffix.question; - - final sourceKeyType = (sourceType as ParameterizedType).typeArguments.firstOrNull; - final sourceValueType = sourceType.typeArguments.lastOrNull; - - final targetKeyType = (targetType as ParameterizedType).typeArguments.firstOrNull; - final targetValueType = targetType.typeArguments.lastOrNull; - - final sourceNullableKey = sourceKeyType?.nullabilitySuffix == NullabilitySuffix.question; - final sourceNullableValue = sourceValueType?.nullabilitySuffix == NullabilitySuffix.question; - final targetNullableKey = targetKeyType?.nullabilitySuffix == NullabilitySuffix.question; - final targetNullableValue = targetValueType?.nullabilitySuffix == NullabilitySuffix.question; - - if (targetKeyType == null || targetValueType == null) { - throw InvalidGenerationSourceError( - 'Target key or value type is null for ${targetType.getDisplayStringWithLibraryAlias(config: assignment.config)}', - ); - } - - if (sourceKeyType == null || sourceValueType == null) { - throw InvalidGenerationSourceError( - 'Source key or value type is null for ${sourceType.getDisplayStringWithLibraryAlias(config: assignment.config)}', - ); - } - - final keyMapping = mapperConfig.findMapping(source: sourceKeyType, target: targetKeyType); - final valueMapping = mapperConfig.findMapping(source: sourceValueType, target: targetValueType); - - // Keys: source is null, target is not null, and default value does not exist. - final shouldRemoveNullsKey = - sourceNullableKey && !targetNullableKey && (!(keyMapping?.hasWhenNullDefault() ?? false)); - - // Value: source is null, target is not null, and default value does not exist. - final shouldRemoveNullsValue = - sourceNullableValue && !targetNullableValue && (!(valueMapping?.hasWhenNullDefault() ?? false)); - - final sourceMapExpression = refer('model').property(assignment.sourceField!.name); - - final defaultMapValueExpression = literalMap( - {}, - refer(targetKeyType.getDisplayStringWithLibraryAlias(withNullability: true, config: mapperConfig)), - refer(targetValueType.getDisplayStringWithLibraryAlias(withNullability: true, config: mapperConfig)), - ); - - final assignNestedObjectKey = !targetKeyType.isPrimitiveType && (targetKeyType != sourceKeyType); - final assignNestedObjectValue = !targetValueType.isPrimitiveType && (targetValueType != sourceValueType); - - final shouldDoMapCall = assignNestedObjectKey || assignNestedObjectValue; - - return sourceMapExpression - // Filter nulls when source key/value is nullable and target is not. - .maybeWhereMapNotNull( - isOnNullable: sourceNullable, - keyIsNullable: shouldRemoveNullsKey, - valueIsNullable: shouldRemoveNullsValue, - keyType: sourceKeyType, - valueType: sourceValueType, - config: mapperConfig, - ) - .maybeCall( - 'map', - isOnNullable: sourceNullable, - // Call map only when actually some mapping is required. - condition: shouldDoMapCall, - positionalArguments: [_callMapForMap(assignment)], - typeArguments: [ - refer(targetKeyType.getDisplayStringWithLibraryAlias(withNullability: true, config: mapperConfig)), - refer(targetValueType.getDisplayStringWithLibraryAlias(withNullability: true, config: mapperConfig)), - ], - ) - // When [sourceNullable], use default value. - .maybeIfNullThen(defaultMapValueExpression, isOnNullable: sourceNullable); - } - - /// Assigns nested object as either: - /// - default value - /// - call to already generated mapping between two types - /// - /// If [convertMethodArgument] is null, uses a tear off call instead. - Expression _assignNestedObject({ - required DartType source, - required DartType target, - required SourceAssignment assignment, - Expression? convertMethodArgument, - bool includeGenericTypes = false, - }) { - if (source.isSame(target)) { - return refer('model').property(assignment.sourceField!.displayName); - } - - final nestedMapping = mapperConfig.findMapping( - source: source, - target: target, - ); - - if (nestedMapping == null) { - final targetTypeName = target.getDisplayStringWithLibraryAlias( - withNullability: true, - config: mapperConfig, - ); - final sourceName = assignment.sourceField?.getDisplayString(withNullability: true); - - if (target.nullabilitySuffix == NullabilitySuffix.question) { - log.warning("Can't find nested mapping '$assignment' but target is nullable. Setting null"); - - return literalNull; - } - - throw InvalidGenerationSourceError( - 'Trying to map nested object from "$assignment" but no mapping is configured.', - todo: 'Configure mapping from $sourceName to $targetTypeName', - ); - } - - final convertCallExpression = _mappingCall( - nestedMapping: nestedMapping, - source: source, - target: target, - convertMethodArgument: convertMethodArgument, - includeGenericTypes: includeGenericTypes, - ); - - // If source == null and target not nullable -> use whenNullDefault if possible - final fieldMapping = mapping.tryGetFieldMapping(assignment.targetName); - if (source.nullabilitySuffix == NullabilitySuffix.question && (fieldMapping?.whenNullExpression != null)) { - // Generates code like: - // - // model.name == null - // ? const Nested( - // id: 123, - // name: 'test', - // ) - // : _map_NestedDto_To_Nested(model.name), - return refer('model').property(assignment.sourceField!.displayName).equalTo(literalNull).conditional( - fieldMapping!.whenNullExpression!, - convertCallExpression, - ); - } - - // Generates code like: - // - // `_map_NestedDto_To_Nested(model.name)` - return convertCallExpression; - } - - /// Generates a mapping call `_mapAlphaDto_to_Alpha(convertMethodArgument)`. - /// When [convertMethodArgument] is null, then a tear off `_mapAlphaDto_to_Alpha` is generated. - /// - /// This function also marks nullable mapping to be generated - /// using the [usedNullableMethodCallback] callback. - Expression _mappingCall({ - required DartType source, - required DartType target, - required TypeMapping nestedMapping, - Expression? convertMethodArgument, - bool includeGenericTypes = false, - }) { - final targetNullable = target.nullabilitySuffix == NullabilitySuffix.question; - - final useNullableMethod = targetNullable && !mapping.hasWhenNullDefault(); - - // When target is nullable, use nullable convert method. - // But use non-nullable when the mapping has default value. - // - // Otherwise use non-nullable. - final convertMethod = refer( - useNullableMethod - ? MethodBuilderBase.constructNullableConvertMethodName( - source: source, - target: target, - config: mapperConfig, - ) - : MethodBuilderBase.constructConvertMethodName( - source: source, - target: target, - config: mapperConfig, - ), - ); - - if (useNullableMethod) { - usedNullableMethodCallback?.call(nestedMapping); - } - - return convertMethodArgument == null - ? convertMethod - : convertMethod.call( - [convertMethodArgument], - {}, - includeGenericTypes - ? [ - refer(source.getDisplayStringWithLibraryAlias(withNullability: true, config: mapperConfig)), - refer(target.getDisplayStringWithLibraryAlias(withNullability: true, config: mapperConfig)), - ] - : [], - ); - } - - Expression _nestedMapCallForIterable(SourceAssignment assignment) { - final targetListType = assignment.targetType.genericParameterTypeOrSelf; - final sourceListType = assignment.sourceType!.genericParameterTypeOrSelf; - - return _assignNestedObject( - assignment: assignment, - source: sourceListType, - target: targetListType, - ); - } - - Expression _callMapForMap(SourceAssignment assignment) { - final sourceKeyType = (assignment.sourceType! as ParameterizedType).typeArguments.firstOrNull; - final sourceValueType = (assignment.sourceType! as ParameterizedType).typeArguments.lastOrNull; - final targetKeyType = (assignment.targetType as ParameterizedType).typeArguments.firstOrNull; - final targetValueType = (assignment.targetType as ParameterizedType).typeArguments.lastOrNull; - - if (targetKeyType == null || targetValueType == null) { - throw InvalidGenerationSourceError( - 'Target key or value type is null for ${assignment.targetType.getDisplayStringWithLibraryAlias(config: assignment.config)}', - ); - } - - if (sourceKeyType == null || sourceValueType == null) { - throw InvalidGenerationSourceError( - 'Source key or value type is null for ${assignment.sourceType?.getDisplayStringWithLibraryAlias(config: assignment.config)}', - ); - } - - final assignNestedObjectKey = !targetKeyType.isPrimitiveType && (targetKeyType != sourceKeyType); - final assignNestedObjectValue = !targetValueType.isPrimitiveType && (targetValueType != sourceValueType); - - final keysAreSameType = sourceKeyType == targetKeyType; - final valuesAreSameType = sourceValueType == targetValueType; - - // Returns a tear off when no nested call is needed. - if (!assignNestedObjectKey && !assignNestedObjectValue) { - return refer('MapEntry.new'); - } - - final sourceMapExpression = refer('key'); - final targetMapExpression = refer('value'); - - final keyExpression = assignNestedObjectKey - ? _assignNestedObject( - assignment: assignment, - source: sourceKeyType, - target: targetKeyType, - convertMethodArgument: keysAreSameType ? null : sourceMapExpression, - ) - : sourceMapExpression; - - final valueExpression = assignNestedObjectValue - ? _assignNestedObject( - assignment: assignment, - source: sourceValueType, - target: targetValueType, - convertMethodArgument: valuesAreSameType ? null : targetMapExpression, - ) - : targetMapExpression; - - return refer( - '(key, value) => MapEntry(${keyExpression.accept(DartEmitter())}, ${valueExpression.accept(DartEmitter())})', - ); - } } diff --git a/packages/auto_mappr/lib/src/extensions/dart_object_extension.dart b/packages/auto_mappr/lib/src/extensions/dart_object_extension.dart index c1e7b0b4..4ea1550c 100644 --- a/packages/auto_mappr/lib/src/extensions/dart_object_extension.dart +++ b/packages/auto_mappr/lib/src/extensions/dart_object_extension.dart @@ -1,9 +1,8 @@ //ignore_for_file: no-object-declaration import 'package:analyzer/dart/constant/value.dart'; -import 'package:auto_mappr/src/extensions/element_extension.dart'; import 'package:auto_mappr/src/extensions/executable_element_extension.dart'; -import 'package:auto_mappr/src/models/auto_mappr_config.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:code_builder/code_builder.dart'; import 'package:collection/collection.dart'; import 'package:source_gen/source_gen.dart'; @@ -12,10 +11,7 @@ extension DartObjectExtension on DartObject { /// If the top most object is a function, then return its code expression. /// /// Otherwise return code expression of literals or objects. - Expression? toCodeExpression({ - required AutoMapprConfig config, - bool passModelArgument = false, - }) { + Expression? toCodeExpression({bool passModelArgument = false}) { if (isNull) { return null; } @@ -23,22 +19,19 @@ extension DartObjectExtension on DartObject { // If the top most object is function, call it. final asFunction = toFunctionValue(); if (asFunction != null) { - return refer(asFunction.referCallString).call([ + return EmitterHelper.current.refer(asFunction.referCallString, asFunction.library.identifier).call([ if (passModelArgument) refer('model'), ]); } - final emitter = DartEmitter(); - final output = _ToCodeExpressionConverter(config: config).convert(this).accept(emitter); + final output = const _ToCodeExpressionConverter().convert(this).accept(EmitterHelper.current.emitter); return CodeExpression(Code('$output')); } } class _ToCodeExpressionConverter { - final AutoMapprConfig config; - - const _ToCodeExpressionConverter({required this.config}); + const _ToCodeExpressionConverter(); Spec convert(DartObject dartObject) { return _toSpec(dartObject); @@ -91,13 +84,13 @@ class _ToCodeExpressionConverter { final revived = ConstantReader(dartObject).revive(); final location = revived.source.toString().split('#'); - final libraryAlias = dartObject.type!.element!.getLibraryAlias(config: config); + final libraryUrl = dartObject.type?.element?.library?.identifier; // Getters, Setters, Methods can't be declared as constants so this // literal must either be a top-level constant or a static constant and // can be directly accessed by `revived.accessor`. if (location.length <= 1) { - return refer('$libraryAlias${revived.accessor}'); + return EmitterHelper.current.refer(revived.accessor, libraryUrl); } // If this is a class instantiation then `location[1]` will be populated @@ -108,7 +101,7 @@ class _ToCodeExpressionConverter { final instantiation = location.elementAtOrNull(1); final useNamedConstructor = revived.accessor.isNotEmpty; - final revivedInstance = refer('$libraryAlias$instantiation'); + final revivedInstance = EmitterHelper.current.refer('$instantiation', libraryUrl); final positionalArguments = revived.positionalArguments.map((argument) => _toSpec(argument) as Expression); diff --git a/packages/auto_mappr/lib/src/extensions/dart_type_extension.dart b/packages/auto_mappr/lib/src/extensions/dart_type_extension.dart index a2f8fc50..a9e5baa6 100644 --- a/packages/auto_mappr/lib/src/extensions/dart_type_extension.dart +++ b/packages/auto_mappr/lib/src/extensions/dart_type_extension.dart @@ -1,7 +1,6 @@ import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/type.dart'; -import 'package:auto_mappr/src/extensions/element_extension.dart'; -import 'package:auto_mappr/src/models/auto_mappr_config.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:code_builder/code_builder.dart'; import 'package:collection/collection.dart'; @@ -15,6 +14,14 @@ extension DartTypeExtension on DartType { isDartCoreEnum || isDartCoreSymbol; + bool get isNullable { + return nullabilitySuffix == NullabilitySuffix.question; + } + + bool get isNotNullable { + return !isNullable; + } + /// Is special variant of integer. /// /// See `[Uint8List], [Uint16List], [Uint32List], [Uint64List]`. @@ -60,91 +67,31 @@ extension DartTypeExtension on DartType { } // Nullability matches. - final thisNullability = nullabilitySuffix == NullabilitySuffix.question; - final otherNullability = other.nullabilitySuffix == NullabilitySuffix.question; + final thisNullability = isNullable; + final otherNullability = other.isNullable; final isSameNullability = thisNullability == otherNullability; return isSameExceptNullability && isSameNullability; } - Expression defaultIterableExpression({required AutoMapprConfig config}) { + Expression defaultIterableExpression() { final itemType = genericParameterTypeOrSelf; return isDartCoreSet - ? literalSet( - {}, - refer( - itemType.getDisplayStringWithLibraryAlias( - config: config, - withNullability: true, - ), - ), - ) - : literalList( - [], - refer( - itemType.getDisplayStringWithLibraryAlias( - config: config, - withNullability: true, - ), - ), - ); - } - - String getDisplayStringWithLibraryAlias({ - required AutoMapprConfig config, - bool withNullability = false, - }) { - final alias = element!.getLibraryAlias(config: config); - final typeName = element!.name; - final buffer = StringBuffer('$alias$typeName'); - - if (this is ParameterizedType && (this as ParameterizedType).typeArguments.isNotEmpty) { - final arguments = (this as ParameterizedType) - .typeArguments - .map( - (argument) => argument.getDisplayStringWithLibraryAlias( - withNullability: withNullability, - config: config, - ), - ) - .join(','); - buffer.write('<$arguments>'); - } - - // Nullability - if (withNullability && nullabilitySuffix == NullabilitySuffix.question) { - buffer.write('?'); - } - - return buffer.toString(); + ? literalSet({}, EmitterHelper.current.typeRefer(type: itemType)) + : literalList([], EmitterHelper.current.typeRefer(type: itemType)); } - String toConvertMethodName({ - required bool withNullability, - required AutoMapprConfig config, - }) { - final alias = element!.getLibraryAlias(config: config, postfix: '_'); - final typeName = element!.name; - final buffer = StringBuffer()..write('$alias$typeName'); - - if (this is ParameterizedType && (this as ParameterizedType).typeArguments.isNotEmpty) { - final arguments = (this as ParameterizedType) - .typeArguments - .map( - (argument) => argument.toConvertMethodName( - withNullability: withNullability, - config: config, - ), - ) - .join(r'$'); - buffer.write('\$$arguments'); - } - - // Nullability - if (withNullability && nullabilitySuffix == NullabilitySuffix.question) { - buffer.write('?'); - } + String toConvertMethodName() { + final emittedThis = EmitterHelper.current + .typeReferEmitted(type: this) + .replaceAll(' ', '') + .replaceAll('?', '') + .replaceAll('.', r'$') + .replaceAll(',', r'_$') + .replaceAll('<', r'$') + .replaceAll('>', r'$'); + final buffer = StringBuffer()..write(emittedThis); return buffer.toString(); } diff --git a/packages/auto_mappr/lib/src/extensions/element_extension.dart b/packages/auto_mappr/lib/src/extensions/element_extension.dart deleted file mode 100644 index 4d75f685..00000000 --- a/packages/auto_mappr/lib/src/extensions/element_extension.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:analyzer/dart/element/element.dart'; -import 'package:auto_mappr/src/models/auto_mappr_config.dart'; - -extension ElementExtension on Element { - /// Returns an library alias with [postfix] (usually '.'), - /// or empty string if no alias detected. - String getLibraryAlias({ - required AutoMapprConfig config, - String postfix = '.', - }) { - final libraryUri = source?.uri.toString(); - final contains = config.libraryUriToAlias.containsKey(libraryUri); - - return contains ? '${config.libraryUriToAlias[libraryUri]!}$postfix' : ''; - } -} diff --git a/packages/auto_mappr/lib/src/extensions/expression_extension.dart b/packages/auto_mappr/lib/src/extensions/expression_extension.dart index 54a45bc2..bd45eeb1 100644 --- a/packages/auto_mappr/lib/src/extensions/expression_extension.dart +++ b/packages/auto_mappr/lib/src/extensions/expression_extension.dart @@ -1,6 +1,6 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; -import 'package:auto_mappr/src/models/auto_mappr_config.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:code_builder/code_builder.dart'; extension ExpressionExtension on Expression { @@ -19,7 +19,10 @@ extension ExpressionExtension on Expression { } if ((source.isDartCoreList || forceCast) && target.isSpecializedListType) { - return refer(target.getDisplayString(withNullability: false)).property('fromList').call([this]); + return EmitterHelper.current + .typeRefer(type: target, withNullabilitySuffix: false) + .property('fromList') + .call([this]); } // Keep iterable as is. @@ -82,7 +85,6 @@ extension ExpressionExtension on Expression { required bool valueIsNullable, required DartType keyType, required DartType valueType, - required AutoMapprConfig config, }) { if (!keyIsNullable && !valueIsNullable) return this; @@ -94,60 +96,52 @@ extension ExpressionExtension on Expression { [refer('(key, value) => $keyCondition $and $valueCondition')], {}, [ - refer(keyType.getDisplayStringWithLibraryAlias(config: config)), - refer(valueType.getDisplayStringWithLibraryAlias(config: config)), + EmitterHelper.current.typeRefer(type: keyType, withNullabilitySuffix: false), + EmitterHelper.current.typeRefer(type: valueType, withNullabilitySuffix: false), ], ); } - Expression maybeAsA(Expression expression, {required bool condition}) { - if (!condition) return this; - - return asA(expression); - } - - static Expression ifStatement({ + static Reference ifStatement({ required Spec condition, required Spec ifBody, Spec? elseBody, }) { - final dartEmitter = DartEmitter(); + final emitter = EmitterHelper.current.emitter; - final ifBlock = '{ ${ifBody.accept(dartEmitter)} }'; - final elseBlock = (elseBody != null) ? 'else { ${elseBody.accept(dartEmitter)} }' : ''; + final ifBlock = '{ ${ifBody.accept(emitter)} }'; + final elseBlock = (elseBody != null) ? 'else { ${elseBody.accept(emitter)} }' : ''; - return refer('''if ( ${condition.accept(dartEmitter)} ) $ifBlock $elseBlock'''); + return refer('''if ( ${condition.accept(emitter)} ) $ifBlock $elseBlock'''); } - Expression ifStatement2({required Spec ifBody, Spec? elseBody}) { - final dartEmitter = DartEmitter(); - - final ifBlock = '{ ${ifBody.accept(dartEmitter)} }'; - final elseBlock = (elseBody != null) ? 'else { ${elseBody.accept(dartEmitter)} }' : ''; + Reference ifStatement2({required Spec ifBody, Spec? elseBody}) { + final ifBlock = '{ ${ifBody.accept(EmitterHelper.current.emitter)} }'; + final elseBlock = (elseBody != null) ? 'else { ${elseBody.accept(EmitterHelper.current.emitter)} }' : ''; - return refer('''if ( ${accept(dartEmitter)} ) $ifBlock $elseBlock'''); + return refer('''if ( ${accept(EmitterHelper.current.emitter)} ) $ifBlock $elseBlock'''); } - static Expression forStatement({ + static Reference forStatement({ required Reference item, required Reference iterable, required Spec body, }) { - final dartEmitter = DartEmitter(); + final emitter = EmitterHelper.current.emitter; return refer(''' -for (final ${item.accept(dartEmitter)} in ${iterable.accept(dartEmitter)}) { - ${body.accept(dartEmitter)} +for (final ${item.accept(emitter)} in ${iterable.accept(emitter)}) { + ${body.accept(emitter)} } '''); } - Expression bracketed() { - return refer('(${accept(DartEmitter())})'); + Reference bracketed() { + return refer('(${accept(EmitterHelper.current.emitter)})'); } - Expression nullabled() { - return refer('${accept(DartEmitter())}?'); + Reference nullabled() { + return refer('${accept(EmitterHelper.current.emitter)}?'); } Expression equalToNull() => equalTo(literalNull); diff --git a/packages/auto_mappr/lib/src/extensions/reference_extension.dart b/packages/auto_mappr/lib/src/extensions/reference_extension.dart deleted file mode 100644 index cab8b802..00000000 --- a/packages/auto_mappr/lib/src/extensions/reference_extension.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:code_builder/code_builder.dart'; - -extension ReferenceExtension on Reference { - Reference get nullabled => refer('${accept(DartEmitter())}?'); -} diff --git a/packages/auto_mappr/lib/src/generator/auto_mappr_generator.dart b/packages/auto_mappr/lib/src/generator/auto_mappr_generator.dart index 8ca35929..bc73fcc8 100644 --- a/packages/auto_mappr/lib/src/generator/auto_mappr_generator.dart +++ b/packages/auto_mappr/lib/src/generator/auto_mappr_generator.dart @@ -5,172 +5,247 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:auto_mappr/src/builder/auto_mappr_builder.dart'; import 'package:auto_mappr/src/extensions/dart_object_extension.dart'; -import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; import 'package:auto_mappr/src/extensions/list_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; +import 'package:auto_mappr/src/helpers/run_zoned_auto_mappr.dart'; import 'package:auto_mappr/src/models/auto_mappr_options.dart'; import 'package:auto_mappr/src/models/models.dart'; -import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:auto_mappr/src/models/type_converter.dart'; +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart' as annotation; import 'package:build/build.dart'; -import 'package:code_builder/code_builder.dart'; import 'package:collection/collection.dart'; import 'package:source_gen/source_gen.dart'; /// Code generator to generate implemented mapping classes. -class AutoMapprGenerator extends GeneratorForAnnotation { +class AutoMapprGenerator extends GeneratorForAnnotation { final BuilderOptions builderOptions; + // Constants for AutoMappr. + static const String annotationAutoMappr = 'AutoMappr'; + static const String annotationFieldMappers = 'mappers'; + static const String annotationFieldConverters = 'converters'; + static const String annotationFieldDelegates = 'delegates'; + static const String annotationFieldIncludes = 'includes'; + + // Constants for MapType. + static const String mapTypeFieldFields = 'fields'; + static const String mapTypeFieldConverters = 'converters'; + static const String mapTypeFieldWhenSourceIsNull = 'whenSourceIsNull'; + static const String mapTypeFieldConstructor = 'constructor'; + static const String mapTypeFieldIgnoreFieldNull = 'ignoreFieldNull'; + static const String mapTypeFieldReverse = 'reverse'; + + // Constants for Field. + static const String fieldFieldField = 'field'; + static const String fieldFieldIgnore = 'ignore'; + static const String fieldFieldFrom = 'from'; + static const String fieldFieldCustom = 'custom'; + static const String fieldFieldWhenNull = 'whenNull'; + static const String fieldFieldIgnoreNull = 'ignoreNull'; + + // Constants for TypeConverter. + static const String typeConverterFieldConverter = 'converter'; + const AutoMapprGenerator({required this.builderOptions}); @override dynamic generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) { - if (element is! ClassElement) { - throw InvalidGenerationSourceError( - '${element.displayName} is not a class and cannot be annotated with @AutoMappr.', - element: element, - todo: 'Use @AutoMappr annotation on a class', - ); - } + final filePath = element.library?.identifier; + final fileUri = filePath != null ? Uri.parse(filePath) : null; - final libraryUriToAlias = _getLibraryAliases(element: element); - - final mapprOptions = AutoMapprOptions.fromJson(builderOptions.config); - - final tmpConfig = AutoMapprConfig( - mappers: [], - availableMappingsMacroId: 'tmp', - libraryUriToAlias: libraryUriToAlias, - mapprOptions: mapprOptions, - ); - - final constant = annotation.objectValue; - final mappersField = constant.getField('mappers')!; - final mappersList = mappersField.toListValue()!; - final modulesExpression = constant.getField('modules')!.toCodeExpression(config: tmpConfig); - final modulesList = constant.getField('modules')!.toListValue(); - - final mappers = _processMappers( - mappers: mappersList, - element: element, - config: tmpConfig, - ); - - final duplicates = mappers.duplicates; - if (duplicates.isNotEmpty) { - throw InvalidGenerationSourceError( - '@AutoMappr has configured duplicated mappings:\n\t${duplicates.map( - (e) => e.toStringWithLibraryAlias(config: tmpConfig), - ).join('\n\t')}', - ); - } + return runZonedAutoMappr(libraryUri: fileUri, () { + if (element is! ClassElement) { + throw InvalidGenerationSourceError( + '${element.displayName} is not a class and cannot be annotated with @AutoMappr.', + element: element, + todo: 'Use @AutoMappr annotation on a class', + ); + } + + final mapprOptions = AutoMapprOptions.fromJson(builderOptions.config); - final config = AutoMapprConfig( - mappers: mappers, - availableMappingsMacroId: element.library.identifier, - libraryUriToAlias: libraryUriToAlias, - modulesCode: modulesExpression, - modulesList: modulesList ?? [], - mapprOptions: mapprOptions, - ); + final constant = annotation.objectValue; + final mappersList = constant.getField(annotationFieldMappers)?.toListValue() ?? []; + final convertersList = constant.getField(annotationFieldConverters)?.toListValue() ?? []; + final delegatesExpression = constant.getField(annotationFieldDelegates)?.toCodeExpression(); + final delegatesList = constant.getField(annotationFieldDelegates)?.toListValue() ?? []; + final includesList = constant.getField(annotationFieldIncludes)?.toListValue() ?? []; - final builder = AutoMapprBuilder(mapperClassElement: element, config: config); + final allMappers = [...mappersList, ..._mappersFromRecursiveIncludes(includesList: includesList)]; + final allConverters = + _toTypeConverters([...convertersList, ..._convertersFromRecursiveIncludes(includesList: includesList)]); + final mappers = _processMappers(mappers: allMappers, globalConverters: allConverters, element: element); - final output = builder.build(); - final emitter = DartEmitter(orderDirectives: true, useNullSafetySyntax: true); + final duplicates = mappers.duplicates; + if (duplicates.isNotEmpty) { + throw InvalidGenerationSourceError( + '@AutoMappr has configured duplicated mappings:\n\t${duplicates.map( + (e) => e.toString(), + ).join('\n\t')}', + ); + } + + final config = AutoMapprConfig( + mappers: mappers, + availableMappingsMacroId: element.library.identifier, + modulesCode: delegatesExpression, + delegatesList: delegatesList, + mapprOptions: mapprOptions, + ); - return '${output.accept(emitter)}'; + final builder = AutoMapprBuilder(mapperClassElement: element, config: config); + + final output = builder.build(); + + return '${output.accept(EmitterHelper.current.emitter)}'; + }); } List _processMappers({ required List mappers, + required List globalConverters, required ClassElement element, - required AutoMapprConfig config, }) { - return mappers.map((mapper) { - final mapperType = mapper.type! as ParameterizedType; - - final sourceType = mapperType.typeArguments.firstOrNull; - final targetType = mapperType.typeArguments.lastOrNull; + return mappers + .map((mapper) { + final mapperType = mapper.type! as ParameterizedType; + + final sourceType = mapperType.typeArguments.firstOrNull; + final targetType = mapperType.typeArguments.lastOrNull; + + if (sourceType is! InterfaceType) { + final emittedSource = EmitterHelper.current.typeReferEmitted(type: sourceType); + + throw InvalidGenerationSourceError( + '$emittedSource is not a class and cannot be mapped from', + element: element, + todo: 'Use a class', + ); + } + if (targetType is! InterfaceType) { + final emittedTarget = EmitterHelper.current.typeReferEmitted(type: targetType); + + throw InvalidGenerationSourceError( + '$emittedTarget is not a class and cannot be mapped to', + element: element, + todo: 'Use a class', + ); + } + + final fields = mapper.getField(mapTypeFieldFields)?.toListValue(); + final mapTypeConverters = mapper.getField(mapTypeFieldConverters)?.toListValue() ?? []; + final whenSourceIsNull = mapper.getField(mapTypeFieldWhenSourceIsNull)?.toCodeExpression(); + final constructor = mapper.getField(mapTypeFieldConstructor)?.toStringValue(); + final ignoreFieldNull = mapper.getField(mapTypeFieldIgnoreFieldNull)?.toBoolValue(); + final reverse = mapper.getField(mapTypeFieldReverse)?.toBoolValue(); + + final fieldMappings = fields + ?.map( + (fieldMapping) => FieldMapping( + field: fieldMapping.getField(fieldFieldField)!.toStringValue()!, + ignore: fieldMapping.getField(fieldFieldIgnore)!.toBoolValue()!, + from: fieldMapping.getField(fieldFieldFrom)!.toStringValue(), + customExpression: fieldMapping.getField(fieldFieldCustom)!.toCodeExpression(passModelArgument: true), + whenNullExpression: fieldMapping.getField(fieldFieldWhenNull)!.toCodeExpression(), + ignoreNull: fieldMapping.getField(fieldFieldIgnoreNull)!.toBoolValue(), + ), + ) + .toList(); + + return [ + TypeMapping( + source: sourceType, + target: targetType, + fieldMappings: fieldMappings ?? [], + typeConverters: [..._toTypeConverters(mapTypeConverters), ...globalConverters], + whenSourceIsNullExpression: whenSourceIsNull, + constructor: constructor, + ignoreFieldNull: ignoreFieldNull, + ), + if (reverse ?? false) + TypeMapping( + source: targetType, + target: sourceType, + fieldMappings: fieldMappings ?? [], + whenSourceIsNullExpression: whenSourceIsNull, + constructor: constructor, + ignoreFieldNull: ignoreFieldNull, + ), + ]; + }) + .flattened + .toList(); + } - if (sourceType is! InterfaceType) { - throw InvalidGenerationSourceError( - '${sourceType?.getDisplayStringWithLibraryAlias(config: config, withNullability: true)} is not a class and cannot be mapped from', - element: element, - todo: 'Use a class', - ); - } - if (targetType is! InterfaceType) { - throw InvalidGenerationSourceError( - '${targetType?.getDisplayStringWithLibraryAlias(config: config, withNullability: true)} is not a class and cannot be mapped to', - element: element, - todo: 'Use a class', - ); + /// Recursively returns all mappings from includes. + Iterable _mappersFromRecursiveIncludes({required List includesList}) { + final mappings = []; + + for (final include in includesList) { + // For each include locate AutoMappr annotation. + if (include.type?.element?.metadata + .firstWhereOrNull((data) => data.element?.displayName == annotationAutoMappr) + ?.computeConstantValue() + case final includeConstant?) { + // This -- mappers. + final mappers = includeConstant.getField(annotationFieldMappers)?.toListValue(); + if (mappers != null) { + mappings.addAll(mappers); + } + + // Bellow -- recursive includes. + final includes = includeConstant.getField(annotationFieldIncludes)?.toListValue(); + if (includes != null) { + // ignore: avoid-recursive-calls, it's handled + mappings.addAll(_mappersFromRecursiveIncludes(includesList: includes)); + } } + } - final fields = mapper.getField('fields')?.toListValue(); - final whenSourceIsNull = mapper.getField('whenSourceIsNull')?.toCodeExpression(config: config); - final constructor = mapper.getField('constructor')?.toStringValue(); - final ignoreFieldNull = mapper.getField('ignoreFieldNull')?.toBoolValue(); - - final fieldMappings = fields - ?.map( - (fieldMapping) => FieldMapping( - field: fieldMapping.getField('field')!.toStringValue()!, - ignore: fieldMapping.getField('ignore')!.toBoolValue()!, - from: fieldMapping.getField('from')!.toStringValue(), - customExpression: - fieldMapping.getField('custom')!.toCodeExpression(passModelArgument: true, config: config), - whenNullExpression: fieldMapping.getField('whenNull')!.toCodeExpression(config: config), - ignoreNull: fieldMapping.getField('ignoreNull')?.toBoolValue(), - ), - ) - .toList(); + return mappings; + } - return TypeMapping( + List _toTypeConverters(List source) { + return source.map((converter) { + final converterType = converter.type! as ParameterizedType; + // ignore: avoid-unsafe-collection-methods, is checked by the TypeConverter interface + final sourceType = converterType.typeArguments.first; + // ignore: avoid-unsafe-collection-methods, is checked by the TypeConverter interface + final targetType = converterType.typeArguments.last; + + return TypeConverter( source: sourceType, target: targetType, - fieldMappings: fieldMappings, - whenSourceIsNullExpression: whenSourceIsNull, - constructor: constructor, - ignoreFieldNull: ignoreFieldNull, + converter: converter.getField(typeConverterFieldConverter)!.toFunctionValue()!, ); }).toList(); } - Map _getLibraryAliases({required ClassElement element}) { - final libraryUriToAlias = {}; - - final imports = element.library.libraryImports; - final aliases = imports.map((e) => e.prefix?.element.name).toList(); - final uris = imports.map((e) => e.importedLibrary!.identifier).toList(); - - for (var i = 0; i < imports.length; i++) { - final currentAlias = aliases.elementAtOrNull(i); - if (currentAlias == null) continue; - - final importedLibrary = imports.elementAtOrNull(i)?.importedLibrary; - final exports = importedLibrary == null ? [] : _getRecursiveLibraryExports(importedLibrary); - - final uri = uris.elementAtOrNull(i); - - libraryUriToAlias.addAll({ - // Current library. - if (uri != null) uri: currentAlias, - // It's exports. - for (final exported in exports) exported.identifier: currentAlias, - }); + /// Recursively returns all type converters from includes. + Iterable _convertersFromRecursiveIncludes({required List includesList}) { + final mappings = []; + + for (final include in includesList) { + // For each include locate AutoMappr annotation. + if (include.type?.element?.metadata + .firstWhereOrNull((data) => data.element?.displayName == annotationAutoMappr) + ?.computeConstantValue() + case final includeConstant?) { + // This -- converters. + final converters = includeConstant.getField(annotationFieldConverters)?.toListValue(); + if (converters != null) { + mappings.addAll(converters); + } + + // Bellow -- recursive includes. + final includes = includeConstant.getField(annotationFieldIncludes)?.toListValue(); + if (includes != null) { + // ignore: avoid-recursive-calls, it's handled + mappings.addAll(_convertersFromRecursiveIncludes(includesList: includes)); + } + } } - return libraryUriToAlias; - } - - /// Recursively returns all exports (even nested) for [library]. - Iterable _getRecursiveLibraryExports(LibraryElement library) { - final exports = library.libraryExports; - if (exports.isEmpty) return []; - - return [ - ...exports.map((e) => e.exportedLibrary!), - ...exports.map((e) => _getRecursiveLibraryExports(e.exportedLibrary!)).flattened, - ]; + return mappings; } } diff --git a/packages/auto_mappr/lib/src/helpers/emitter_helper.dart b/packages/auto_mappr/lib/src/helpers/emitter_helper.dart new file mode 100644 index 00000000..6ec87878 --- /dev/null +++ b/packages/auto_mappr/lib/src/helpers/emitter_helper.dart @@ -0,0 +1,118 @@ +import 'dart:async'; + +import 'package:analyzer/dart/element/type.dart'; +import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:code_builder/code_builder.dart' as cb; +import 'package:path/path.dart' as p; + +/// Helper class for emitting and package import uri resolution. +class EmitterHelper { + /// Global emitter so we can emit on the fly and all imports are preserved. + final cb.DartEmitter emitter = cb.DartEmitter( + allocator: cb.Allocator.simplePrefixing(), + orderDirectives: true, + useNullSafetySyntax: true, + ); + + final Uri? fileWithAnnotation; + + static Symbol get zoneSymbol => #autoMapprEmitter; + static EmitterHelper get current => Zone.current[zoneSymbol] as EmitterHelper; + + EmitterHelper({required this.fileWithAnnotation}); + + /// `refer` that is processed by helper. + cb.Reference refer(String symbol, String? url) { + final importUrl = + // TODO(module): can we check whether the url is from THIS package and therefore we can use relative, also in current project test must be relative + // type.isPrimitiveType || type.isDartCoreObject + // ? _resolveAssetImport(libraryPath) + // : + _relative(url, fileWithAnnotation); + + return cb.refer(symbol, importUrl); + } + + /// `refer` that is emitted to String using [emitter]. + String referEmitted(String symbol, [String? url]) { + return refer(symbol, url).accept(emitter).toString(); + } + + /// [typeRefer] that is also emitted to String using [emitter]. + String typeReferEmitted({ + required DartType? type, + // Uri? targetFile, + bool withNullabilitySuffix = true, + }) { + if (type == null) { + return '???'; + } + + return '${typeRefer(type: type, withNullabilitySuffix: withNullabilitySuffix).accept(emitter)}'; + } + + /// Produces a reference to [type] with an import alias prefix. + /// When [fileWithAnnotation] is also set, import is relative. + /// + /// Inspired by injectable. + cb.Reference typeRefer({ + required DartType type, + bool withNullabilitySuffix = true, + }) { + final libraryPath = type.element?.library?.identifier; + final importUrl = type.isPrimitiveType || type.isDartCoreObject + ? _resolveAssetImport(libraryPath) + : _relative(libraryPath, fileWithAnnotation); + + return cb.TypeReference((reference) { + reference + ..symbol = type.element?.name + ..url = importUrl + ..isNullable = withNullabilitySuffix && type.isNullable; + + if (type is ParameterizedType && type.typeArguments.isNotEmpty) { + reference.types.addAll( + // ignore: avoid-recursive-calls, it's handled + type.typeArguments.map((e) => typeRefer(type: e)), + ); + } + }); + } + + String? _relative(String? path, Uri? to) { + if (path == null || to == null) { + return null; + } + + final fileUri = Uri.parse(path); + final libName = to.pathSegments.firstOrNull; + + if (fileUri.scheme == 'dart') { + return 'dart:${fileUri.path}'; + } + + if ((to.scheme == 'package' && fileUri.scheme == 'package' && fileUri.pathSegments.firstOrNull == libName) || + (to.scheme == 'asset' && fileUri.scheme != 'package')) { + if (fileUri.path == to.path) { + return fileUri.pathSegments.lastOrNull; + } + + return p.posix.relative(fileUri.path, from: to.path).replaceFirst('../', ''); + } + + return path; + } + + String? _resolveAssetImport(String? path) { + if (path == null) { + return null; + } + + final fileUri = Uri.parse(path); + if (fileUri.scheme == 'asset') { + return '/${fileUri.path}'; + } + + return path; + } +} diff --git a/packages/auto_mappr/lib/src/helpers/run_zoned_auto_mappr.dart b/packages/auto_mappr/lib/src/helpers/run_zoned_auto_mappr.dart new file mode 100644 index 00000000..8d4dfa49 --- /dev/null +++ b/packages/auto_mappr/lib/src/helpers/run_zoned_auto_mappr.dart @@ -0,0 +1,14 @@ +import 'dart:async'; + +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; + +/// We need to use zones so we can easily have "scoped globals" for EmitterHelper. +// ignore: prefer-static-class, must be top level +R runZonedAutoMappr(R Function() body, {Uri? libraryUri}) { + return runZoned( + () { + return body(); + }, + zoneValues: {EmitterHelper.zoneSymbol: EmitterHelper(fileWithAnnotation: libraryUri)}, + ); +} diff --git a/packages/auto_mappr/lib/src/helpers/urls.dart b/packages/auto_mappr/lib/src/helpers/urls.dart new file mode 100644 index 00000000..42769fd1 --- /dev/null +++ b/packages/auto_mappr/lib/src/helpers/urls.dart @@ -0,0 +1,3 @@ +class Urls { + static const annotationPackageUrl = 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +} diff --git a/packages/auto_mappr/lib/src/models/auto_mappr_config.dart b/packages/auto_mappr/lib/src/models/auto_mappr_config.dart index 0a65e03c..20659be9 100644 --- a/packages/auto_mappr/lib/src/models/auto_mappr_config.dart +++ b/packages/auto_mappr/lib/src/models/auto_mappr_config.dart @@ -1,7 +1,7 @@ import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; -import 'package:auto_mappr/src/extensions/element_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:auto_mappr/src/models/auto_mappr_options.dart'; import 'package:auto_mappr/src/models/type_mapping.dart'; import 'package:code_builder/code_builder.dart'; @@ -11,24 +11,24 @@ class AutoMapprConfig { final List mappers; final String availableMappingsMacroId; final Expression? modulesCode; - final List modulesList; - final Map libraryUriToAlias; + final List includesList; + final List delegatesList; final AutoMapprOptions mapprOptions; String get availableMappingsDocComment { return [ '/// {@macro $availableMappingsMacroId}', - ..._modulesDocComment(), + ..._delegatesDocComment(), ].join('\n'); } const AutoMapprConfig({ required this.mappers, required this.availableMappingsMacroId, - required this.libraryUriToAlias, required this.mapprOptions, this.modulesCode, - this.modulesList = const [], + this.includesList = const [], + this.delegatesList = const [], }); TypeMapping? findMapping({ @@ -46,18 +46,32 @@ class AutoMapprConfig { '/// Available mappings:', for (final mapper in mappers) _getTypeMappingDocs(mapper), '/// {@endtemplate}', - ..._modulesDocComment(), + ..._includesDocComment(), + ..._delegatesDocComment(), ]; } - List _modulesDocComment() { + List _includesDocComment() { return [ - if (modulesList.isNotEmpty) ...[ + if (includesList.isNotEmpty) ...[ '///', - "/// Available modules: ${modulesList.map((e) { - final alias = e.type!.element!.getLibraryAlias(config: this); + "/// Used includes: ${includesList.map((e) { + final emittedInclude = EmitterHelper.current.typeReferEmitted(type: e.type); - return '[$alias\$${e.type!.getDisplayString(withNullability: false)}]'; + return '[$emittedInclude]'; + }).join(', ')}", + ], + ]; + } + + List _delegatesDocComment() { + return [ + if (delegatesList.isNotEmpty) ...[ + '///', + "/// Used delegates: ${delegatesList.map((e) { + final emittedDelegate = EmitterHelper.current.typeReferEmitted(type: e.type); + + return '[$emittedDelegate]'; }).join(', ')}", ], ]; @@ -65,12 +79,11 @@ class AutoMapprConfig { String _getTypeMappingDocs(TypeMapping typeMapping) { final trailingPart = typeMapping.hasWhenNullDefault() ? ' -- With default value.' : '.'; + // Display without import aliases. + final emittedSource = typeMapping.source.getDisplayString(withNullability: true); + final emittedTarget = typeMapping.target.getDisplayString(withNullability: true); // ignore: avoid-non-ascii-symbols, it is ok - return '/// - `${typeMapping.source.getDisplayStringWithLibraryAlias( - config: this, - )}` → `${typeMapping.target.getDisplayStringWithLibraryAlias( - config: this, - )}`$trailingPart'; + return '/// - `$emittedSource` → `$emittedTarget`$trailingPart'; } } diff --git a/packages/auto_mappr/lib/src/models/field_mapping.dart b/packages/auto_mappr/lib/src/models/field_mapping.dart index d98cc4ef..95ac3de9 100644 --- a/packages/auto_mappr/lib/src/models/field_mapping.dart +++ b/packages/auto_mappr/lib/src/models/field_mapping.dart @@ -30,10 +30,6 @@ class FieldMapping extends Equatable { this.whenNullExpression, }); - bool isRenamed() { - return from != null; - } - bool hasCustomMapping() { return customExpression != null; } diff --git a/packages/auto_mappr/lib/src/models/source_assignment.dart b/packages/auto_mappr/lib/src/models/source_assignment.dart index 8e12e118..be9447f1 100644 --- a/packages/auto_mappr/lib/src/models/source_assignment.dart +++ b/packages/auto_mappr/lib/src/models/source_assignment.dart @@ -3,8 +3,10 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; +import 'package:auto_mappr/src/helpers/emitter_helper.dart'; import 'package:auto_mappr/src/models/models.dart'; -import 'package:code_builder/code_builder.dart'; +import 'package:auto_mappr/src/models/type_converter.dart'; +import 'package:code_builder/code_builder.dart' show Expression, literalList, literalMap, literalNull, literalSet; class ConstructorAssignment { final ParameterElement param; @@ -21,20 +23,13 @@ class SourceAssignment { final ConstructorAssignment? targetConstructorParam; final PropertyAccessorElement? targetField; + final List typeConverters; + /// Field mapping. /// /// Like filed 'name' from 'userName' etc. final FieldMapping? fieldMapping; - /// Mapping of type. - /// - /// Like UserDto to User. - final TypeMapping typeMapping; - - final AutoMapprConfig config; - - bool get shouldBeIgnored => fieldMapping?.ignore ?? false; - DartType? get sourceType => sourceField?.returnType; String? get sourceName => sourceField?.displayName; @@ -46,30 +41,34 @@ class SourceAssignment { const SourceAssignment({ required this.sourceField, required this.targetField, - required this.typeMapping, - required this.config, this.targetConstructorParam, + this.typeConverters = const [], this.fieldMapping, }); - bool shouldAssignIterable() { + bool canAssignIterable() { // The source can be mapped to the target, if the source is mappable object and the target is an iterable. return (_isCoreIterable(targetType) || targetType.isSpecializedListType) && _isMappableIterable(sourceType!); } - bool shouldAssignMap() { + bool canAssignMap() { // The source can be mapped to the target, if the source is mappable object and the target is map. return targetType.isDartCoreMap && _isMappableMap(sourceType!); } - bool shouldAssignComplexObject() => !targetType.isPrimitiveType; + bool canAssignRecord() { + final isSourceRecord = sourceType is RecordType; + final isTargetRecord = targetType is RecordType; + + return isSourceRecord && isTargetRecord; + } @override String toString() { - final sourceTypeName = sourceType?.getDisplayStringWithLibraryAlias(withNullability: true, config: config); - final targetTypeName = targetType.getDisplayStringWithLibraryAlias(withNullability: true, config: config); + final emittedSource = EmitterHelper.current.typeReferEmitted(type: sourceType); + final emittedTarget = EmitterHelper.current.typeReferEmitted(type: targetType); - return '$sourceTypeName $sourceName -> $targetTypeName $targetName'; + return '$emittedSource $sourceName -> $emittedTarget $targetName'; } Expression getDefaultValue() { diff --git a/packages/auto_mappr/lib/src/models/type_converter.dart b/packages/auto_mappr/lib/src/models/type_converter.dart new file mode 100644 index 00000000..cbce2712 --- /dev/null +++ b/packages/auto_mappr/lib/src/models/type_converter.dart @@ -0,0 +1,43 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:equatable/equatable.dart'; + +class TypeConverter extends Equatable { + final DartType source; + final DartType target; + final ExecutableElement converter; + + @override + List get props => [source, target, converter]; + + const TypeConverter({ + required this.source, + required this.target, + required this.converter, + }); + + @override + String toString() { + final sourceX = source.getDisplayString(withNullability: true); + final targetX = target.getDisplayString(withNullability: true); + + return 'typeConverter $sourceX -> $targetX'; + } + + bool canBeUsed({ + required DartType mappingSource, + required DartType mappingTarget, + }) { + return _isConverterSubtype(source, mappingSource) && _isConverterSubtype(target, mappingTarget); + } + + bool _isConverterSubtype(DartType self, DartType other) { + // Same type. + if (self == other) return true; + + // Other must be subtype of self. + // + // When [self] is Object, it is always success. + return self.element?.library?.typeSystem.leastUpperBound(self, other) == self; + } +} diff --git a/packages/auto_mappr/lib/src/models/type_mapping.dart b/packages/auto_mappr/lib/src/models/type_mapping.dart index 59ee7d15..180859b6 100644 --- a/packages/auto_mappr/lib/src/models/type_mapping.dart +++ b/packages/auto_mappr/lib/src/models/type_mapping.dart @@ -1,9 +1,9 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:auto_mappr/src/builder/methods/method_builder_base.dart'; -import 'package:auto_mappr/src/extensions/dart_type_extension.dart'; import 'package:auto_mappr/src/models/auto_mappr_config.dart'; import 'package:auto_mappr/src/models/field_mapping.dart'; +import 'package:auto_mappr/src/models/type_converter.dart'; import 'package:code_builder/code_builder.dart'; import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; @@ -11,7 +11,8 @@ import 'package:equatable/equatable.dart'; class TypeMapping extends Equatable { final InterfaceType source; final InterfaceType target; - final List? fieldMappings; + final List fieldMappings; + final List typeConverters; final Expression? whenSourceIsNullExpression; final String? constructor; final bool? ignoreFieldNull; @@ -24,6 +25,7 @@ class TypeMapping extends Equatable { source, target, fieldMappings, + typeConverters, whenSourceIsNullExpression, constructor, ignoreFieldNull, @@ -34,7 +36,8 @@ class TypeMapping extends Equatable { required this.source, required this.target, required this.ignoreFieldNull, - this.fieldMappings, + this.fieldMappings = const [], + this.typeConverters = const [], this.whenSourceIsNullExpression, this.constructor, }); @@ -56,22 +59,21 @@ class TypeMapping extends Equatable { return whenSourceIsNullExpression != null; } - bool hasFieldMapping(String field) => fieldMappings?.any((x) => x.field == field) ?? false; + bool hasFieldMapping(String field) => fieldMappings.any((x) => x.field == field); - FieldMapping? getFieldMapping(String field) => fieldMappings?.firstWhereOrNull((x) => x.field == field); + FieldMapping? getFieldMapping(String field) => fieldMappings.firstWhereOrNull((x) => x.field == field); - FieldMapping? tryGetFieldMapping(String field) => fieldMappings?.firstWhereOrNull((x) => x.field == field); + FieldMapping? tryGetFieldMapping(String field) => fieldMappings.firstWhereOrNull((x) => x.field == field); bool fieldShouldBeIgnored(String field) => hasFieldMapping(field) && (getFieldMapping(field)?.ignore ?? false); @override String toString() { - // ignore: avoid-non-ascii-symbols, it is ok - return '$source → $target'; - } + // Without import aliases, used to display errors to user. + final sourceAsString = source.getDisplayString(withNullability: true); + final targetAsString = target.getDisplayString(withNullability: true); - String toStringWithLibraryAlias({required AutoMapprConfig config}) { // ignore: avoid-non-ascii-symbols, it is ok - return '${source.getDisplayStringWithLibraryAlias(config: config)} → ${target.getDisplayStringWithLibraryAlias(config: config)}'; + return '$sourceAsString → $targetAsString'; } } diff --git a/packages/auto_mappr/pubspec.yaml b/packages/auto_mappr/pubspec.yaml index d02e2840..c0e8cf32 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: 1.7.0 +version: 2.0.0 repository: https://github.com/netglade/auto_mappr issue_tracker: https://github.com/netglade/auto_mappr/issues screenshots: @@ -8,17 +8,18 @@ screenshots: path: doc/icon.png environment: - sdk: ">=2.18.7 <4.0.0" + sdk: ^3.0.0 dependencies: # TODO: Support analyzer same way as json_serializable https://github.com/google/json_serializable.dart/pull/1333 analyzer: ">=5.4.0 <7.0.0" - auto_mappr_annotation: ^1.2.0 + auto_mappr_annotation: ^2.0.0 build: ^2.3.1 built_collection: ^5.1.1 code_builder: ^4.4.0 collection: ^1.17.0 equatable: ^2.0.5 + get_it: ^7.6.4 meta: ^1.8.0 path: ^1.8.2 source_gen: ^1.2.6 @@ -27,6 +28,6 @@ dev_dependencies: build_runner: ^2.0.0 generator_test: ^0.2.0 mocktail: ^1.0.0 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 source_gen_test: ^1.0.4 test: ^1.16.0 diff --git a/packages/auto_mappr/test/builder/convert_method_builder_test.dart b/packages/auto_mappr/test/builder/convert_method_builder_test.dart index 11386c54..3a8036be 100644 --- a/packages/auto_mappr/test/builder/convert_method_builder_test.dart +++ b/packages/auto_mappr/test/builder/convert_method_builder_test.dart @@ -2,7 +2,8 @@ import 'package:auto_mappr/src/builder/methods/convert_method_builder.dart'; import 'package:auto_mappr/src/builder/methods/private_convert_method_builder.dart'; import 'package:auto_mappr/src/builder/methods/try_convert_method_builder.dart'; import 'package:auto_mappr/src/builder/methods/type_of_method_builder.dart'; -import 'package:auto_mappr/src/extensions/reference_extension.dart'; +import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:auto_mappr/src/helpers/run_zoned_auto_mappr.dart'; import 'package:auto_mappr/src/models/auto_mappr_config.dart'; import 'package:auto_mappr/src/models/auto_mappr_options.dart'; import 'package:code_builder/code_builder.dart'; @@ -11,101 +12,105 @@ import 'package:test/test.dart'; void main() { test('buildConvertMethod has the correct interface', () { - final result = ConvertMethodBuilder( - const AutoMapprConfig( - mappers: [], - availableMappingsMacroId: 'test', - libraryUriToAlias: {}, - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), - ), - ).buildMethod(); - - expect(result, isA()); - expect(result.name, equals('convert')); - expect(result.types.length, equals(2)); - expect(result.returns, equals(result.types.elementAtOrNull(1))); - expect(result.optionalParameters, isEmpty); - expect( - result.requiredParameters, - equals([ - Parameter( - (p) => p - ..name = 'model' - ..type = result.types.firstOrNull?.nullabled, + runZonedAutoMappr(() { + final result = const ConvertMethodBuilder( + AutoMapprConfig( + mappers: [], + availableMappingsMacroId: 'test', + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), ), - ]), - ); + ).buildMethod(); + + expect(result, isA()); + expect(result.name, equals('convert')); + expect(result.types.length, equals(2)); + expect(result.returns, equals(result.types.elementAtOrNull(1))); + expect(result.optionalParameters, isEmpty); + expect( + result.requiredParameters, + equals([ + Parameter( + (p) => p + ..name = 'model' + ..type = result.types.firstOrNull?.nullabled(), + ), + ]), + ); + }); }); test('buildTryConvertMethod has the correct interface', () { - final result = TryConvertMethodBuilder( - const AutoMapprConfig( - mappers: [], - availableMappingsMacroId: 'test', - libraryUriToAlias: {}, - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), - ), - ).buildMethod(); - - expect(result, isA()); - expect(result.name, equals('tryConvert')); - expect(result.types.length, equals(2)); - expect(result.returns, equals(result.types.elementAtOrNull(1)?.nullabled)); - expect(result.optionalParameters, isEmpty); - expect( - result.requiredParameters, - equals([ - Parameter( - (p) => p - ..name = 'model' - ..type = result.types.firstOrNull?.nullabled, + runZonedAutoMappr(() { + final result = const TryConvertMethodBuilder( + AutoMapprConfig( + mappers: [], + availableMappingsMacroId: 'test', + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), ), - ]), - ); + ).buildMethod(); + + expect(result, isA()); + expect(result.name, equals('tryConvert')); + expect(result.types.length, equals(2)); + expect(result.returns, equals(result.types.elementAtOrNull(1)?.nullabled())); + expect(result.optionalParameters, isEmpty); + expect( + result.requiredParameters, + equals([ + Parameter( + (p) => p + ..name = 'model' + ..type = result.types.firstOrNull?.nullabled(), + ), + ]), + ); + }); }); test('buildInternalConvertMethod has the correct interface', () { - final result = PrivateConvertMethodBuilder( - const AutoMapprConfig( - mappers: [], - availableMappingsMacroId: 'test', - libraryUriToAlias: {}, - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), - ), - ).buildMethod(); - - expect(result, isA()); - expect(result.name, equals('_convert')); - expect(result.types.length, equals(2)); - expect(result.returns, equals(result.types.elementAtOrNull(1)?.nullabled)); - expect(result.optionalParameters.isEmpty, isFalse); - expect( - result.requiredParameters, - equals([ - Parameter( - (p) => p - ..name = 'model' - ..type = result.types.firstOrNull?.nullabled, + runZonedAutoMappr(() { + final result = const PrivateConvertMethodBuilder( + AutoMapprConfig( + mappers: [], + availableMappingsMacroId: 'test', + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), ), - ]), - ); + ).buildMethod(); + + expect(result, isA()); + expect(result.name, equals('_convert')); + expect(result.types.length, equals(2)); + expect(result.returns, equals(result.types.elementAtOrNull(1)?.nullabled())); + expect(result.optionalParameters.isEmpty, isFalse); + expect( + result.requiredParameters, + equals([ + Parameter( + (p) => p + ..name = 'model' + ..type = result.types.firstOrNull?.nullabled(), + ), + ]), + ); + }); }); test('buildTypeOfHelperMethod has the correct interface', () { - final result = TypeOfMethodBuilder( - const AutoMapprConfig( - mappers: [], - availableMappingsMacroId: 'test', - libraryUriToAlias: {}, - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), - ), - ).buildMethod(); + runZonedAutoMappr(() { + final result = const TypeOfMethodBuilder( + AutoMapprConfig( + mappers: [], + availableMappingsMacroId: 'test', + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), + ), + ).buildMethod(); - expect(result, isA()); - expect(result.name, equals('_typeOf')); - expect(result.types.length, equals(1)); - expect(result.returns, equals(refer('Type'))); - expect(result.optionalParameters, isEmpty); - expect(result.requiredParameters, isEmpty); + expect(result, isA()); + expect(result.name, equals('_typeOf')); + expect(result.types.length, equals(1)); + expect(result.returns, equals(refer('Type'))); + expect(result.optionalParameters, isEmpty); + expect(result.requiredParameters, isEmpty); + }); }); } diff --git a/packages/auto_mappr/test/integration/modules_test.dart b/packages/auto_mappr/test/integration/delegates_test.dart similarity index 98% rename from packages/auto_mappr/test/integration/modules_test.dart rename to packages/auto_mappr/test/integration/delegates_test.dart index 639bb87f..93116737 100644 --- a/packages/auto_mappr/test/integration/modules_test.dart +++ b/packages/auto_mappr/test/integration/delegates_test.dart @@ -1,9 +1,9 @@ import 'package:test/test.dart'; -import 'fixture/modules.dart' as fixture_group; -import 'fixture/modules/module_alpha.dart' as fixture_alpha; -import 'fixture/modules/module_beta.dart' as fixture_beta; -import 'fixture/modules/module_gama.dart' as fixture_gama; +import 'fixture/delegates.dart' as fixture_group; +import 'fixture/delegates/module_alpha.dart' as fixture_alpha; +import 'fixture/delegates/module_beta.dart' as fixture_beta; +import 'fixture/delegates/module_gama.dart' as fixture_gama; // modules hierarchy: // diff --git a/packages/auto_mappr/test/integration/enum_mapping_test.dart b/packages/auto_mappr/test/integration/enum_mapping_test.dart index 1d50cd7a..67ba32fd 100644 --- a/packages/auto_mappr/test/integration/enum_mapping_test.dart +++ b/packages/auto_mappr/test/integration/enum_mapping_test.dart @@ -1,4 +1,5 @@ import 'package:auto_mappr/auto_mappr.dart'; +import 'package:auto_mappr/src/helpers/run_zoned_auto_mappr.dart'; import 'package:generator_test/generator_test.dart'; import 'package:source_gen/source_gen.dart'; import 'package:test/test.dart'; @@ -45,43 +46,47 @@ void main() { group('Error handling', () { test("Can't map source enum to target when target is not enum", () { - final generator = SuccessGenerator.fromBuilder( - 'enum_mapping_not_enum', - autoMapprBuilder, - inputDir: 'test/integration/error_fixture', - compareWithFixture: false, - ); - - expect( - generator.test, - throwsA( - isA().having( - (x) => x.message, - 'Match message', - 'Failed to map Source → Target because target Target is not an enum.', + runZonedAutoMappr(() { + final generator = SuccessGenerator.fromBuilder( + 'enum_mapping_not_enum', + autoMapprBuilder, + inputDir: 'test/integration/error_fixture', + compareWithFixture: false, + ); + + expect( + generator.test, + throwsA( + isA().having( + (x) => x.message, + 'Match message', + 'Failed to map Source → Target because target Target is not an enum.', + ), ), - ), - ); + ); + }); }); test("Can't map source enum to target when target is not superset of source", () { - final generator = SuccessGenerator.fromBuilder( - 'enum_mapping_subset', - autoMapprBuilder, - inputDir: 'test/integration/error_fixture', - compareWithFixture: false, - ); - - expect( - generator.test, - throwsA( - isA().having( - (x) => x.message, - 'Match message', - "Can't map enum Source into Target. Target enum is not superset of source enum.", + runZonedAutoMappr(() { + final generator = SuccessGenerator.fromBuilder( + 'enum_mapping_subset', + autoMapprBuilder, + inputDir: 'test/integration/error_fixture', + compareWithFixture: false, + ); + + expect( + generator.test, + throwsA( + isA().having( + (x) => x.message, + 'Match message', + "Can't map enum Source into Target. Target enum is not superset of source enum.", + ), ), - ), - ); + ); + }); }); }); } diff --git a/packages/auto_mappr/test/integration/error_fixture/enum_mapping_not_enum.dart b/packages/auto_mappr/test/integration/error_fixture/enum_mapping_not_enum.dart index d797631e..7bbc575d 100644 --- a/packages/auto_mappr/test/integration/error_fixture/enum_mapping_not_enum.dart +++ b/packages/auto_mappr/test/integration/error_fixture/enum_mapping_not_enum.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'enum_mapping_not_enum.g.dart'; +import 'enum_mapping_not_enum.auto_mappr.dart'; enum Source { a, b } diff --git a/packages/auto_mappr/test/integration/error_fixture/enum_mapping_subset.dart b/packages/auto_mappr/test/integration/error_fixture/enum_mapping_subset.dart index 0491f219..ff222e7e 100644 --- a/packages/auto_mappr/test/integration/error_fixture/enum_mapping_subset.dart +++ b/packages/auto_mappr/test/integration/error_fixture/enum_mapping_subset.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'enum_mapping_subset.g.dart'; +import 'enum_mapping_subset.auto_mappr.dart'; enum Source { a, b } diff --git a/packages/auto_mappr/test/integration/fixture/complex_types.dart b/packages/auto_mappr/test/integration/fixture/complex_types.dart index 5a885dc9..45802458 100644 --- a/packages/auto_mappr/test/integration/fixture/complex_types.dart +++ b/packages/auto_mappr/test/integration/fixture/complex_types.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'complex_types.g.dart'; +import 'complex_types.auto_mappr.dart'; @AutoMappr( [ diff --git a/packages/auto_mappr/test/integration/fixture/constructor_parameters.dart b/packages/auto_mappr/test/integration/fixture/constructor_parameters.dart index 7098f990..d21eec25 100644 --- a/packages/auto_mappr/test/integration/fixture/constructor_parameters.dart +++ b/packages/auto_mappr/test/integration/fixture/constructor_parameters.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'constructor_parameters.g.dart'; +import 'constructor_parameters.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/convert_iterable.dart b/packages/auto_mappr/test/integration/fixture/convert_iterable.dart index ea5acefa..e415e811 100644 --- a/packages/auto_mappr/test/integration/fixture/convert_iterable.dart +++ b/packages/auto_mappr/test/integration/fixture/convert_iterable.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'convert_iterable.g.dart'; +import 'convert_iterable.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/custom_mapping.dart b/packages/auto_mappr/test/integration/fixture/custom_mapping.dart index 21164069..5c5470e4 100644 --- a/packages/auto_mappr/test/integration/fixture/custom_mapping.dart +++ b/packages/auto_mappr/test/integration/fixture/custom_mapping.dart @@ -3,7 +3,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'custom_mapping.g.dart'; +import 'custom_mapping.auto_mappr.dart'; @AutoMappr([ // custom type diff --git a/packages/auto_mappr/test/integration/fixture/default_field.dart b/packages/auto_mappr/test/integration/fixture/default_field.dart index 9175bfb9..36871b23 100644 --- a/packages/auto_mappr/test/integration/fixture/default_field.dart +++ b/packages/auto_mappr/test/integration/fixture/default_field.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'default_field.g.dart'; +import 'default_field.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/default_target.dart b/packages/auto_mappr/test/integration/fixture/default_target.dart index 3ebdad5e..255570e2 100644 --- a/packages/auto_mappr/test/integration/fixture/default_target.dart +++ b/packages/auto_mappr/test/integration/fixture/default_target.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'default_target.g.dart'; +import 'default_target.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/modules.dart b/packages/auto_mappr/test/integration/fixture/delegates.dart similarity index 72% rename from packages/auto_mappr/test/integration/fixture/modules.dart rename to packages/auto_mappr/test/integration/fixture/delegates.dart index da0df0ba..d8baa72c 100644 --- a/packages/auto_mappr/test/integration/fixture/modules.dart +++ b/packages/auto_mappr/test/integration/fixture/delegates.dart @@ -1,11 +1,10 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -import 'modules/module_alpha.dart'; +import 'delegates.auto_mappr.dart'; +import 'delegates/module_alpha.dart'; -part 'modules.g.dart'; - -@AutoMappr([MapType()], modules: [MapprAlpha()]) +@AutoMappr([MapType()], delegates: [MapprAlpha()]) class MapprGroup extends $MapprGroup { const MapprGroup(); } diff --git a/packages/auto_mappr/test/integration/fixture/delegates/module_alpha.dart b/packages/auto_mappr/test/integration/fixture/delegates/module_alpha.dart new file mode 100644 index 00000000..1dd873a5 --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/delegates/module_alpha.dart @@ -0,0 +1,25 @@ +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:equatable/equatable.dart'; + +import 'module_alpha.auto_mappr.dart'; +import 'module_beta.dart'; + +@AutoMappr([MapType()], delegates: [MapprBeta()]) +class MapprAlpha extends $MapprAlpha { + const MapprAlpha(); +} + +class AlphaDto { + final int value; + + const AlphaDto(this.value); +} + +class Alpha with EquatableMixin { + final int value; + + @override + List get props => [value]; + + const Alpha(this.value); +} diff --git a/packages/auto_mappr/test/integration/fixture/modules/module_beta.dart b/packages/auto_mappr/test/integration/fixture/delegates/module_beta.dart similarity index 79% rename from packages/auto_mappr/test/integration/fixture/modules/module_beta.dart rename to packages/auto_mappr/test/integration/fixture/delegates/module_beta.dart index 95f5d516..1cd48ac3 100644 --- a/packages/auto_mappr/test/integration/fixture/modules/module_beta.dart +++ b/packages/auto_mappr/test/integration/fixture/delegates/module_beta.dart @@ -1,11 +1,10 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; +import 'module_beta.auto_mappr.dart'; import 'module_gama.dart'; -part 'module_beta.g.dart'; - -@AutoMappr([MapType()], modules: [MapprGama()]) +@AutoMappr([MapType()], delegates: [MapprGama()]) class MapprBeta extends $MapprBeta { const MapprBeta(); } diff --git a/packages/auto_mappr/test/integration/fixture/modules/module_gama.dart b/packages/auto_mappr/test/integration/fixture/delegates/module_gama.dart similarity index 91% rename from packages/auto_mappr/test/integration/fixture/modules/module_gama.dart rename to packages/auto_mappr/test/integration/fixture/delegates/module_gama.dart index fee4a6d6..e00e7f7a 100644 --- a/packages/auto_mappr/test/integration/fixture/modules/module_gama.dart +++ b/packages/auto_mappr/test/integration/fixture/delegates/module_gama.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'module_gama.g.dart'; +import 'module_gama.auto_mappr.dart'; @AutoMappr([MapType()]) class MapprGama extends $MapprGama { diff --git a/packages/auto_mappr/test/integration/fixture/enum_mapping.dart b/packages/auto_mappr/test/integration/fixture/enum_mapping.dart index 74509e4d..2e9fafad 100644 --- a/packages/auto_mappr/test/integration/fixture/enum_mapping.dart +++ b/packages/auto_mappr/test/integration/fixture/enum_mapping.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'enum_mapping.g.dart'; +import 'enum_mapping.auto_mappr.dart'; enum Person { employee, parent, student } @@ -53,12 +53,13 @@ enum EnhancedTarget { String get row => '$id+$name'; } -LocalPerson _localRemoteUnknownDefault() => LocalPerson.unknown; +// ignore: prefer-static-class, for testing +LocalPerson localRemoteUnknownDefault() => LocalPerson.unknown; @AutoMappr([ MapType(), MapType( - whenSourceIsNull: _localRemoteUnknownDefault, + whenSourceIsNull: localRemoteUnknownDefault, ), MapType(), MapType( diff --git a/packages/auto_mappr/test/integration/fixture/equatable.dart b/packages/auto_mappr/test/integration/fixture/equatable.dart index 662754b5..723f1c5f 100644 --- a/packages/auto_mappr/test/integration/fixture/equatable.dart +++ b/packages/auto_mappr/test/integration/fixture/equatable.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'equatable.g.dart'; +import 'equatable.auto_mappr.dart'; @AutoMappr([MapType()]) class Mappr extends $Mappr { diff --git a/packages/auto_mappr/test/integration/fixture/forced_null_source_field.dart b/packages/auto_mappr/test/integration/fixture/forced_null_source_field.dart index 937d51bf..068b8f91 100644 --- a/packages/auto_mappr/test/integration/fixture/forced_null_source_field.dart +++ b/packages/auto_mappr/test/integration/fixture/forced_null_source_field.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'forced_null_source_field.g.dart'; +import 'forced_null_source_field.auto_mappr.dart'; @AutoMappr([ MapType( diff --git a/packages/auto_mappr/test/integration/fixture/generics.dart b/packages/auto_mappr/test/integration/fixture/generics.dart index 542e67b4..96bfa463 100644 --- a/packages/auto_mappr/test/integration/fixture/generics.dart +++ b/packages/auto_mappr/test/integration/fixture/generics.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'generics.g.dart'; +import 'generics.auto_mappr.dart'; @AutoMappr([ // simple diff --git a/packages/auto_mappr/test/integration/fixture/import_alias.dart b/packages/auto_mappr/test/integration/fixture/import_alias.dart index 84308046..87f84fee 100644 --- a/packages/auto_mappr/test/integration/fixture/import_alias.dart +++ b/packages/auto_mappr/test/integration/fixture/import_alias.dart @@ -1,12 +1,11 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; +import 'import_alias.auto_mappr.dart'; import 'import_alias/import_alias_1.dart' as a1; import 'import_alias/import_alias_2.dart' as a2; import 'import_alias/import_alias_module.dart' as module; -part 'import_alias.g.dart'; - @AutoMappr( [ MapType(), @@ -27,7 +26,7 @@ part 'import_alias.g.dart'; MapType, ListHolder>(), MapType, MapHolder>(), ], - modules: [module.ImportAliasModule()], + delegates: [module.ImportAliasModule()], ) class Mappr extends $Mappr { const Mappr(); diff --git a/packages/auto_mappr/test/integration/fixture/import_alias/import_alias_module.dart b/packages/auto_mappr/test/integration/fixture/import_alias/import_alias_module.dart index db1a6de6..8aaa412f 100644 --- a/packages/auto_mappr/test/integration/fixture/import_alias/import_alias_module.dart +++ b/packages/auto_mappr/test/integration/fixture/import_alias/import_alias_module.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'import_alias_module.g.dart'; +import 'import_alias_module.auto_mappr.dart'; @AutoMappr([MapType()]) class ImportAliasModule extends $ImportAliasModule { diff --git a/packages/auto_mappr/test/integration/fixture/includes.dart b/packages/auto_mappr/test/integration/fixture/includes.dart new file mode 100644 index 00000000..e3e64025 --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/includes.dart @@ -0,0 +1,31 @@ +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:equatable/equatable.dart'; + +import 'includes.auto_mappr.dart'; +import 'includes/module_alpha.dart' as alpha_feature; +import 'includes/module_beta.dart'; +import 'includes/module_gama.dart'; + +@AutoMappr([MapType()], includes: [alpha_feature.MapprAlpha()]) +class MapprGroup extends $MapprGroup { + const MapprGroup(); +} + +class GroupDto { + final alpha_feature.AlphaDto alpha; + final BetaDto beta; + final GamaDto gama; + + const GroupDto(this.alpha, this.beta, this.gama); +} + +class Group with EquatableMixin { + final alpha_feature.Alpha alpha; + final Beta beta; + final Gama gama; + + @override + List get props => [alpha, beta, gama]; + + const Group(this.alpha, this.beta, this.gama); +} diff --git a/packages/auto_mappr/test/integration/fixture/modules/module_alpha.dart b/packages/auto_mappr/test/integration/fixture/includes/module_alpha.dart similarity index 79% rename from packages/auto_mappr/test/integration/fixture/modules/module_alpha.dart rename to packages/auto_mappr/test/integration/fixture/includes/module_alpha.dart index d4ab7cb9..d4390764 100644 --- a/packages/auto_mappr/test/integration/fixture/modules/module_alpha.dart +++ b/packages/auto_mappr/test/integration/fixture/includes/module_alpha.dart @@ -1,11 +1,10 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; +import 'module_alpha.auto_mappr.dart'; import 'module_beta.dart'; -part 'module_alpha.g.dart'; - -@AutoMappr([MapType()], modules: [MapprBeta()]) +@AutoMappr([MapType()], includes: [MapprBeta()]) class MapprAlpha extends $MapprAlpha { const MapprAlpha(); } diff --git a/packages/auto_mappr/test/integration/fixture/includes/module_beta.dart b/packages/auto_mappr/test/integration/fixture/includes/module_beta.dart new file mode 100644 index 00000000..1f00211f --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/includes/module_beta.dart @@ -0,0 +1,25 @@ +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:equatable/equatable.dart'; + +import 'module_beta.auto_mappr.dart'; +import 'module_gama.dart'; + +@AutoMappr([MapType()], includes: [MapprGama()]) +class MapprBeta extends $MapprBeta { + const MapprBeta(); +} + +class BetaDto { + final int value; + + const BetaDto(this.value); +} + +class Beta with EquatableMixin { + final int value; + + @override + List get props => [value]; + + const Beta(this.value); +} diff --git a/packages/auto_mappr/test/integration/fixture/includes/module_gama.dart b/packages/auto_mappr/test/integration/fixture/includes/module_gama.dart new file mode 100644 index 00000000..e00e7f7a --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/includes/module_gama.dart @@ -0,0 +1,24 @@ +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:equatable/equatable.dart'; + +import 'module_gama.auto_mappr.dart'; + +@AutoMappr([MapType()]) +class MapprGama extends $MapprGama { + const MapprGama(); +} + +class GamaDto { + final int value; + + const GamaDto(this.value); +} + +class Gama with EquatableMixin { + final int value; + + @override + List get props => [value]; + + const Gama(this.value); +} diff --git a/packages/auto_mappr/test/integration/fixture/iterable.dart b/packages/auto_mappr/test/integration/fixture/iterable.dart index aa3844c0..b2f9c82b 100644 --- a/packages/auto_mappr/test/integration/fixture/iterable.dart +++ b/packages/auto_mappr/test/integration/fixture/iterable.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'iterable.g.dart'; +import 'iterable.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/list.dart b/packages/auto_mappr/test/integration/fixture/list.dart index 4a99430d..1c592926 100644 --- a/packages/auto_mappr/test/integration/fixture/list.dart +++ b/packages/auto_mappr/test/integration/fixture/list.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'list.g.dart'; +import 'list.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/map.dart b/packages/auto_mappr/test/integration/fixture/map.dart index 541ba39b..736855c2 100644 --- a/packages/auto_mappr/test/integration/fixture/map.dart +++ b/packages/auto_mappr/test/integration/fixture/map.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'map.g.dart'; +import 'map.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/mapping_from_source.dart b/packages/auto_mappr/test/integration/fixture/mapping_from_source.dart index 921cfe83..9c6edfbf 100644 --- a/packages/auto_mappr/test/integration/fixture/mapping_from_source.dart +++ b/packages/auto_mappr/test/integration/fixture/mapping_from_source.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'mapping_from_source.g.dart'; +import 'mapping_from_source.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/mapping_to_target.dart b/packages/auto_mappr/test/integration/fixture/mapping_to_target.dart index 91d56590..79e92a29 100644 --- a/packages/auto_mappr/test/integration/fixture/mapping_to_target.dart +++ b/packages/auto_mappr/test/integration/fixture/mapping_to_target.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'mapping_to_target.g.dart'; +import 'mapping_to_target.auto_mappr.dart'; @AutoMappr([MapType()]) class Mappr extends $Mappr { diff --git a/packages/auto_mappr/test/integration/fixture/multiple_annotations.dart b/packages/auto_mappr/test/integration/fixture/multiple_annotations.dart index 0c397738..fa11e821 100644 --- a/packages/auto_mappr/test/integration/fixture/multiple_annotations.dart +++ b/packages/auto_mappr/test/integration/fixture/multiple_annotations.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'multiple_annotations.g.dart'; +import 'multiple_annotations.auto_mappr.dart'; class AnnotationA { const AnnotationA(); diff --git a/packages/auto_mappr/test/integration/fixture/primitive_types.dart b/packages/auto_mappr/test/integration/fixture/primitive_types.dart index 73b320f1..1a92c855 100644 --- a/packages/auto_mappr/test/integration/fixture/primitive_types.dart +++ b/packages/auto_mappr/test/integration/fixture/primitive_types.dart @@ -2,7 +2,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'primitive_types.g.dart'; +import 'primitive_types.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/record.dart b/packages/auto_mappr/test/integration/fixture/record.dart new file mode 100644 index 00000000..faca3e81 --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/record.dart @@ -0,0 +1,124 @@ +// ignore_for_file: move-records-to-typedefs + +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:equatable/equatable.dart'; + +import 'record.auto_mappr.dart'; + +@AutoMappr([ + // simple + MapType(), + MapType(), + MapType(), + MapType(), + MapType(), + // complex + MapType(), + MapType(), +]) +class Mappr extends $Mappr { + const Mappr(); +} + +// positional + +class Positional extends Equatable { + final (int, bool, String) value; + + @override + List get props => [value]; + + const Positional(this.value); +} + +class PositionalDto extends Equatable { + final (int, bool, String) value; + + @override + List get props => [value]; + + const PositionalDto(this.value); +} + +class PositionalNullable extends Equatable { + final (int?, bool?, String?, int?, bool?) value; + + @override + List get props => [value]; + + const PositionalNullable(this.value); +} + +class PositionalNullableDto extends Equatable { + final (int?, bool?, String?) value; + + @override + List get props => [value]; + + const PositionalNullableDto(this.value); +} + +class Named extends Equatable { + final ({int alpha, bool beta, String gama}) value; + + @override + List get props => [value]; + + const Named(this.value); +} + +class NamedNullable extends Equatable { + final ({int alpha, bool beta, String gama, int? delta, bool? epsilon}) value; + + @override + List get props => [value]; + + const NamedNullable(this.value); +} + +class NamedDto extends Equatable { + final ({int alpha, bool beta, String gama}) value; + + @override + List get props => [value]; + + const NamedDto(this.value); +} + +// complex + +class ComplexPositional extends Equatable { + final List value; + + @override + List get props => [value]; + + const ComplexPositional(this.value); +} + +class ComplexPositionalDto extends Equatable { + final List value; + + @override + List get props => [value]; + + const ComplexPositionalDto(this.value); +} + +class ComplexNamed extends Equatable { + final List value; + + @override + List get props => [value]; + + const ComplexNamed(this.value); +} + +class ComplexNamedDto extends Equatable { + final List value; + + @override + List get props => [value]; + + const ComplexNamedDto(this.value); +} diff --git a/packages/auto_mappr/test/integration/fixture/regression/uint8_issue_77.dart b/packages/auto_mappr/test/integration/fixture/regression/uint8_issue_77.dart index 3de6ca91..cebc91ad 100644 --- a/packages/auto_mappr/test/integration/fixture/regression/uint8_issue_77.dart +++ b/packages/auto_mappr/test/integration/fixture/regression/uint8_issue_77.dart @@ -3,7 +3,7 @@ import 'dart:typed_data'; import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'uint8_issue_77.g.dart'; +import 'uint8_issue_77.auto_mappr.dart'; class Source { final Uint8List int8List; diff --git a/packages/auto_mappr/test/integration/fixture/rename.dart b/packages/auto_mappr/test/integration/fixture/rename.dart index 90cfedcc..32bfa30d 100644 --- a/packages/auto_mappr/test/integration/fixture/rename.dart +++ b/packages/auto_mappr/test/integration/fixture/rename.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'rename.g.dart'; +import 'rename.auto_mappr.dart'; @AutoMappr([ // nested diff --git a/packages/auto_mappr/test/integration/fixture/reverse.dart b/packages/auto_mappr/test/integration/fixture/reverse.dart new file mode 100644 index 00000000..58acf2d5 --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/reverse.dart @@ -0,0 +1,79 @@ +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:equatable/equatable.dart'; + +import 'reverse.auto_mappr.dart'; + +@AutoMappr([ + MapType(reverse: true), + MapType(reverse: true), + MapType(reverse: true), +]) +class Mappr extends $Mappr { + const Mappr(); +} + +// Primitive + +class Primitive extends Equatable { + final int number; + final String string; + + @override + List get props => [number, string]; + + const Primitive({required this.number, required this.string}); +} + +class PrimitiveDto extends Equatable { + final int number; + final String string; + + @override + List get props => [number, string]; + + const PrimitiveDto({required this.number, required this.string}); +} + +// User + +class User extends Equatable { + final int id; + final String name; + final Address address; + + @override + List get props => [id, name, address]; + + const User({required this.id, required this.name, required this.address}); +} + +class UserDto extends Equatable { + final int id; + final String name; + final AddressDto address; + + @override + List get props => [id, name, address]; + + const UserDto(this.id, {required this.name, required this.address}); +} + +class Address extends Equatable { + final String street; + final String city; + + @override + List get props => [street, city]; + + const Address({required this.street, required this.city}); +} + +class AddressDto extends Equatable { + final String street; + final String city; + + @override + List get props => [street, city]; + + const AddressDto({required this.street, required this.city}); +} diff --git a/packages/auto_mappr/test/integration/fixture/selecting_constructor.dart b/packages/auto_mappr/test/integration/fixture/selecting_constructor.dart index 2c2bb7fa..f3fbcff5 100644 --- a/packages/auto_mappr/test/integration/fixture/selecting_constructor.dart +++ b/packages/auto_mappr/test/integration/fixture/selecting_constructor.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'selecting_constructor.g.dart'; +import 'selecting_constructor.auto_mappr.dart'; @AutoMappr([ // empty diff --git a/packages/auto_mappr/test/integration/fixture/set.dart b/packages/auto_mappr/test/integration/fixture/set.dart index 1fb5e99b..2f40bee8 100644 --- a/packages/auto_mappr/test/integration/fixture/set.dart +++ b/packages/auto_mappr/test/integration/fixture/set.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'set.g.dart'; +import 'set.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/special_characters.dart b/packages/auto_mappr/test/integration/fixture/special_characters.dart index 24bb41d1..8a7b6663 100644 --- a/packages/auto_mappr/test/integration/fixture/special_characters.dart +++ b/packages/auto_mappr/test/integration/fixture/special_characters.dart @@ -3,7 +3,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'special_characters.g.dart'; +import 'special_characters.auto_mappr.dart'; @AutoMappr([ // in class diff --git a/packages/auto_mappr/test/integration/fixture/super_class.dart b/packages/auto_mappr/test/integration/fixture/super_class.dart index 6f0a1071..403c369c 100644 --- a/packages/auto_mappr/test/integration/fixture/super_class.dart +++ b/packages/auto_mappr/test/integration/fixture/super_class.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'super_class.g.dart'; +import 'super_class.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/try_convert.dart b/packages/auto_mappr/test/integration/fixture/try_convert.dart index 8339278b..54cf9e81 100644 --- a/packages/auto_mappr/test/integration/fixture/try_convert.dart +++ b/packages/auto_mappr/test/integration/fixture/try_convert.dart @@ -1,7 +1,7 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; import 'package:equatable/equatable.dart'; -part 'try_convert.g.dart'; +import 'try_convert.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr/test/integration/fixture/type_converters.dart b/packages/auto_mappr/test/integration/fixture/type_converters.dart new file mode 100644 index 00000000..3b893270 --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/type_converters.dart @@ -0,0 +1,163 @@ +// ignore_for_file: prefer-named-boolean-parameters, avoid_positional_boolean_parameters + +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; +import 'package:equatable/equatable.dart'; + +import 'type_converters.auto_mappr.dart'; +import 'type_converters/module_alpha.dart'; + +@AutoMappr( + [ + MapType( + converters: [TypeConverter(Mappr.objectToString)], + ), + MapType( + converters: [TypeConverter>(Mappr.intToValueInt)], + ), + MapType(), + MapType(), + MapType(), + ], + converters: [ + TypeConverter>(Mappr.stringToValueString), + TypeConverter>(Mappr.objectToValueObject2), + ], + includes: [MapprAlpha()], +) +class Mappr extends $Mappr { + const Mappr(); + + static String objectToString(T source) { + return '--- $source ---'; + } + + static Value objectToValueObject(T source) { + return Value(source); + } + + static Value objectToValueObject2(Object source) { + if (source is int) { + return Value(source); + } + + return Value(source); + } + + static Value intToValueInt(int source) { + return Value(source); + } + + static Value stringToValueString(String source) { + return Value(source); + } +} + +// Primitives. + +class PrimitivesDto { + final int alpha; + final bool beta; + + const PrimitivesDto({required this.alpha, required this.beta}); +} + +class Primitives with EquatableMixin { + final String alpha; + final String beta; + + @override + List get props => [alpha, beta]; + + const Primitives(this.alpha, this.beta); +} + +// Fields. + +class NormalFieldDto { + final int xInt; + final String xString; + final bool normalBool; + + const NormalFieldDto({required this.xInt, required this.xString, required this.normalBool}); +} + +class NormalField with EquatableMixin { + final Value xInt; + final Value xString; + final bool normalBool; + + @override + List get props => [xInt, xString, normalBool]; + + const NormalField(this.xInt, this.xString, this.normalBool); +} + +// In list. + +class InListDto { + final List xInt; + final String xString; + final bool normalBool; + + const InListDto({required this.xInt, required this.xString, required this.normalBool}); +} + +class InList with EquatableMixin { + final List> xInt; + final Value xString; + final bool normalBool; + + @override + List get props => [xInt, xString, normalBool]; + + const InList(this.xInt, this.xString, this.normalBool); +} + +// In map. + +class InMapDto { + final Map alpha; + final Map beta; + final Map gama; + + const InMapDto({required this.alpha, required this.beta, required this.gama}); +} + +class InMap with EquatableMixin { + final Map, int> alpha; + final Map> beta; + final Map, Value> gama; + + @override + List get props => [alpha, beta, gama]; + + const InMap(this.alpha, this.beta, this.gama); +} + +// Includes. + +class IncludesDto { + final int alpha; + + const IncludesDto({required this.alpha}); +} + +class Includes with EquatableMixin { + final bool alpha; + + @override + List get props => [alpha]; + + const Includes(this.alpha); +} + +// Box + +class Value with EquatableMixin { + final T value; + + @override + List get props => [value]; + + const Value(this.value); +} diff --git a/packages/auto_mappr/test/integration/fixture/type_converters/module_alpha.dart b/packages/auto_mappr/test/integration/fixture/type_converters/module_alpha.dart new file mode 100644 index 00000000..31a52d59 --- /dev/null +++ b/packages/auto_mappr/test/integration/fixture/type_converters/module_alpha.dart @@ -0,0 +1,12 @@ +import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; + +import 'module_alpha.auto_mappr.dart'; + +@AutoMappr([], converters: [TypeConverter(MapprAlpha.intToBool)]) +class MapprAlpha extends $MapprAlpha { + const MapprAlpha(); + + static bool intToBool(int source) { + return source == 1; + } +} diff --git a/packages/auto_mappr/test/integration/fixture/when_source_is_null.dart b/packages/auto_mappr/test/integration/fixture/when_source_is_null.dart index e13d284b..2555e2db 100644 --- a/packages/auto_mappr/test/integration/fixture/when_source_is_null.dart +++ b/packages/auto_mappr/test/integration/fixture/when_source_is_null.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'when_source_is_null.g.dart'; +import 'when_source_is_null.auto_mappr.dart'; class SourceValue { final String? name; @@ -25,18 +25,19 @@ class Target { @AutoMappr([ MapType(fields: [Field('name', whenNull: 'static')]), MapType( - fields: [Field('name', whenNull: Mappr._whenSourceIsNull)], + fields: [Field('name', whenNull: Mappr.whenSourceIsNull)], ), MapType( - fields: [Field('name', whenNull: _whenSourceIsNullTopLevel)], + fields: [Field('name', whenNull: whenSourceIsNullTopLevel)], ), ]) class Mappr extends $Mappr { - static String _whenSourceIsNull() { + static String whenSourceIsNull() { return 'whenSourceIsNull'; } } -String _whenSourceIsNullTopLevel() { +// ignore: prefer-static-class, for testing +String whenSourceIsNullTopLevel() { return 'whenSourceIsNullTopLevel'; } diff --git a/packages/auto_mappr/test/integration/includes_test.dart b/packages/auto_mappr/test/integration/includes_test.dart new file mode 100644 index 00000000..c4d563f0 --- /dev/null +++ b/packages/auto_mappr/test/integration/includes_test.dart @@ -0,0 +1,660 @@ +import 'package:test/test.dart'; + +import 'fixture/includes.dart' as fixture_group; +import 'fixture/includes/module_alpha.dart' as fixture_alpha; +import 'fixture/includes/module_beta.dart' as fixture_beta; +import 'fixture/includes/module_gama.dart' as fixture_gama; + +// includes hierarchy: +// +// - Group (this one) +// - Alpha +// - Beta +// - Gama +void main() { + late final fixture_group.MapprGroup mappr; + + setUpAll(() { + mappr = const fixture_group.MapprGroup(); + }); + + group('convert', () { + test('group', () { + const dto = fixture_group.GroupDto(fixture_alpha.AlphaDto(1), fixture_beta.BetaDto(2), fixture_gama.GamaDto(3)); + final converted = mappr.convert(dto); + + expect( + converted, + equals(const fixture_group.Group(fixture_alpha.Alpha(1), fixture_beta.Beta(2), fixture_gama.Gama(3))), + ); + }); + + test('alpha', () { + const dto = fixture_alpha.AlphaDto(4); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture_alpha.Alpha(4))); + }); + + test('beta', () { + const dto = fixture_beta.BetaDto(5); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture_beta.Beta(5))); + }); + + test('gama', () { + const dto = fixture_gama.GamaDto(6); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture_gama.Gama(6))); + }); + }); + + group('tryConvert', () { + test('group', () { + const dto = fixture_group.GroupDto(fixture_alpha.AlphaDto(1), fixture_beta.BetaDto(2), fixture_gama.GamaDto(3)); + final converted = mappr.tryConvert(dto); + + expect( + converted, + equals(const fixture_group.Group(fixture_alpha.Alpha(1), fixture_beta.Beta(2), fixture_gama.Gama(3))), + ); + }); + + test('group w/ null', () { + const fixture_group.GroupDto? dto = null; + final converted = mappr.tryConvert(dto); + + expect(converted, isNull); + }); + + test('alpha', () { + const dto = fixture_alpha.AlphaDto(2); + final converted = mappr.tryConvert(dto); + + expect(converted, equals(const fixture_alpha.Alpha(2))); + }); + + test('alpha w/ null', () { + const fixture_alpha.AlphaDto? dto = null; + final converted = mappr.tryConvert(dto); + + expect(converted, isNull); + }); + + test('beta', () { + const dto = fixture_beta.BetaDto(3); + final converted = mappr.tryConvert(dto); + + expect(converted, equals(const fixture_beta.Beta(3))); + }); + + test('beta w/ null', () { + const fixture_beta.BetaDto? dto = null; + final converted = mappr.tryConvert(dto); + + expect(converted, isNull); + }); + + test('gama', () { + const dto = fixture_gama.GamaDto(4); + final converted = mappr.tryConvert(dto); + + expect(converted, equals(const fixture_gama.Gama(4))); + }); + + test('gama w/ null', () { + const fixture_gama.GamaDto? dto = null; + final converted = mappr.tryConvert(dto); + + expect(converted, isNull); + }); + }); + + group('convertIterable', () { + test('group', () { + final dto = [ + const fixture_group.GroupDto(fixture_alpha.AlphaDto(1), fixture_beta.BetaDto(2), fixture_gama.GamaDto(3)), + const fixture_group.GroupDto(fixture_alpha.AlphaDto(4), fixture_beta.BetaDto(5), fixture_gama.GamaDto(6)), + const fixture_group.GroupDto(fixture_alpha.AlphaDto(7), fixture_beta.BetaDto(8), fixture_gama.GamaDto(9)), + ].map((e) => e); + final converted = mappr.convertIterable(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(1), fixture_beta.Beta(2), fixture_gama.Gama(3)), + fixture_group.Group(fixture_alpha.Alpha(4), fixture_beta.Beta(5), fixture_gama.Gama(6)), + fixture_group.Group(fixture_alpha.Alpha(7), fixture_beta.Beta(8), fixture_gama.Gama(9)), + ]), + ); + }); + + test('alpha', () { + final dto = [ + const fixture_alpha.AlphaDto(4), + const fixture_alpha.AlphaDto(5), + ].map((e) => e); + final converted = mappr.convertIterable(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5)]), + ); + }); + + test('beta', () { + final dto = [ + const fixture_beta.BetaDto(6), + const fixture_beta.BetaDto(7), + const fixture_beta.BetaDto(8), + ].map((e) => e); + final converted = mappr.convertIterable(dto); + + expect( + converted, + equals(const [ + fixture_beta.Beta(6), + fixture_beta.Beta(7), + fixture_beta.Beta(8), + ]), + ); + }); + + test('gama', () { + final dto = [ + const fixture_gama.GamaDto(9), + const fixture_gama.GamaDto(10), + ].map((e) => e); + final converted = mappr.convertIterable(dto); + + expect(converted, equals(const [fixture_gama.Gama(9), fixture_gama.Gama(10)])); + }); + }); + + group('tryConvertIterable', () { + test('group', () { + final dto = const [ + fixture_group.GroupDto(fixture_alpha.AlphaDto(11), fixture_beta.BetaDto(12), fixture_gama.GamaDto(13)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(14), fixture_beta.BetaDto(15), fixture_gama.GamaDto(16)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(17), fixture_beta.BetaDto(18), fixture_gama.GamaDto(19)), + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(11), fixture_beta.Beta(12), fixture_gama.Gama(13)), + fixture_group.Group(fixture_alpha.Alpha(14), fixture_beta.Beta(15), fixture_gama.Gama(16)), + fixture_group.Group(fixture_alpha.Alpha(17), fixture_beta.Beta(18), fixture_gama.Gama(19)), + ]), + ); + }); + + test('group w/ null', () { + final dto = [ + const fixture_group.GroupDto(fixture_alpha.AlphaDto(101), fixture_beta.BetaDto(102), fixture_gama.GamaDto(103)), + null, + const fixture_group.GroupDto(fixture_alpha.AlphaDto(201), fixture_beta.BetaDto(202), fixture_gama.GamaDto(203)), + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(101), fixture_beta.Beta(102), fixture_gama.Gama(103)), + null, + fixture_group.Group(fixture_alpha.Alpha(201), fixture_beta.Beta(202), fixture_gama.Gama(203)), + ]), + ); + }); + + test('alpha', () { + final dto = [ + const fixture_alpha.AlphaDto(4), + const fixture_alpha.AlphaDto(5), + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5)]), + ); + }); + + test('alpha w/ null', () { + final dto = [ + const fixture_alpha.AlphaDto(4), + const fixture_alpha.AlphaDto(5), + null, + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5), null]), + ); + }); + + test('beta', () { + final dto = [ + const fixture_beta.BetaDto(6), + const fixture_beta.BetaDto(7), + const fixture_beta.BetaDto(8), + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect( + converted, + equals(const [ + fixture_beta.Beta(6), + fixture_beta.Beta(7), + fixture_beta.Beta(8), + ]), + ); + }); + + test('beta w/ null', () { + final dto = [ + null, + const fixture_beta.BetaDto(7), + const fixture_beta.BetaDto(8), + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect( + converted, + equals(const [null, fixture_beta.Beta(7), fixture_beta.Beta(8)]), + ); + }); + + test('gama', () { + final dto = [ + const fixture_gama.GamaDto(9), + const fixture_gama.GamaDto(10), + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect(converted, equals(const [fixture_gama.Gama(9), fixture_gama.Gama(10)])); + }); + + test('gama w/ null', () { + final dto = [ + null, + const fixture_gama.GamaDto(9), + const fixture_gama.GamaDto(10), + ].map((e) => e); + final converted = mappr.tryConvertIterable(dto); + + expect( + converted, + equals(const [null, fixture_gama.Gama(9), fixture_gama.Gama(10)]), + ); + }); + }); + + group('convertList', () { + test('group', () { + const dto = [ + fixture_group.GroupDto(fixture_alpha.AlphaDto(21), fixture_beta.BetaDto(22), fixture_gama.GamaDto(23)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(24), fixture_beta.BetaDto(25), fixture_gama.GamaDto(26)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(27), fixture_beta.BetaDto(28), fixture_gama.GamaDto(29)), + ]; + final converted = mappr.convertList(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(21), fixture_beta.Beta(22), fixture_gama.Gama(23)), + fixture_group.Group(fixture_alpha.Alpha(24), fixture_beta.Beta(25), fixture_gama.Gama(26)), + fixture_group.Group(fixture_alpha.Alpha(27), fixture_beta.Beta(28), fixture_gama.Gama(29)), + ]), + ); + }); + + test('alpha', () { + const dto = [ + fixture_alpha.AlphaDto(4), + fixture_alpha.AlphaDto(5), + ]; + final converted = mappr.convertList(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5)]), + ); + }); + + test('beta', () { + const dto = [ + fixture_beta.BetaDto(6), + fixture_beta.BetaDto(7), + fixture_beta.BetaDto(8), + ]; + final converted = mappr.convertList(dto); + + expect( + converted, + equals(const [ + fixture_beta.Beta(6), + fixture_beta.Beta(7), + fixture_beta.Beta(8), + ]), + ); + }); + + test('gama', () { + const dto = [ + fixture_gama.GamaDto(9), + fixture_gama.GamaDto(10), + ]; + final converted = mappr.convertList(dto); + + expect(converted, equals(const [fixture_gama.Gama(9), fixture_gama.Gama(10)])); + }); + }); + + group('tryConvertList', () { + test('group', () { + const dto = [ + fixture_group.GroupDto(fixture_alpha.AlphaDto(31), fixture_beta.BetaDto(32), fixture_gama.GamaDto(33)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(34), fixture_beta.BetaDto(35), fixture_gama.GamaDto(36)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(37), fixture_beta.BetaDto(38), fixture_gama.GamaDto(39)), + ]; + final converted = mappr.tryConvertList(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(31), fixture_beta.Beta(32), fixture_gama.Gama(33)), + fixture_group.Group(fixture_alpha.Alpha(34), fixture_beta.Beta(35), fixture_gama.Gama(36)), + fixture_group.Group(fixture_alpha.Alpha(37), fixture_beta.Beta(38), fixture_gama.Gama(39)), + ]), + ); + }); + + test('group w/ null', () { + const dto = [ + fixture_group.GroupDto(fixture_alpha.AlphaDto(221), fixture_beta.BetaDto(222), fixture_gama.GamaDto(223)), + null, + fixture_group.GroupDto(fixture_alpha.AlphaDto(231), fixture_beta.BetaDto(232), fixture_gama.GamaDto(233)), + ]; + final converted = mappr.tryConvertList(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(221), fixture_beta.Beta(222), fixture_gama.Gama(223)), + null, + fixture_group.Group(fixture_alpha.Alpha(231), fixture_beta.Beta(232), fixture_gama.Gama(233)), + ]), + ); + }); + + test('alpha', () { + const dto = [ + fixture_alpha.AlphaDto(4), + fixture_alpha.AlphaDto(5), + ]; + final converted = mappr.tryConvertList(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5)]), + ); + }); + + test('alpha w/ null', () { + const dto = [ + fixture_alpha.AlphaDto(4), + fixture_alpha.AlphaDto(5), + null, + ]; + final converted = mappr.tryConvertList(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5), null]), + ); + }); + + test('beta', () { + const dto = [ + fixture_beta.BetaDto(6), + fixture_beta.BetaDto(7), + fixture_beta.BetaDto(8), + ]; + final converted = mappr.tryConvertList(dto); + + expect( + converted, + equals(const [ + fixture_beta.Beta(6), + fixture_beta.Beta(7), + fixture_beta.Beta(8), + ]), + ); + }); + + test('beta w/ null', () { + const dto = [ + null, + fixture_beta.BetaDto(7), + fixture_beta.BetaDto(8), + ]; + final converted = mappr.tryConvertList(dto); + + expect( + converted, + equals(const [null, fixture_beta.Beta(7), fixture_beta.Beta(8)]), + ); + }); + + test('gama', () { + const dto = [ + fixture_gama.GamaDto(9), + fixture_gama.GamaDto(10), + ]; + final converted = mappr.tryConvertList(dto); + + expect(converted, equals(const [fixture_gama.Gama(9), fixture_gama.Gama(10)])); + }); + + test('gama w/ null', () { + const dto = [ + null, + fixture_gama.GamaDto(9), + fixture_gama.GamaDto(10), + ]; + final converted = mappr.tryConvertList(dto); + + expect( + converted, + equals(const [null, fixture_gama.Gama(9), fixture_gama.Gama(10)]), + ); + }); + }); + + group('convertSet', () { + test('group', () { + const dto = { + fixture_group.GroupDto(fixture_alpha.AlphaDto(41), fixture_beta.BetaDto(42), fixture_gama.GamaDto(43)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(44), fixture_beta.BetaDto(45), fixture_gama.GamaDto(46)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(47), fixture_beta.BetaDto(48), fixture_gama.GamaDto(49)), + }; + final converted = mappr.convertSet(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(41), fixture_beta.Beta(42), fixture_gama.Gama(43)), + fixture_group.Group(fixture_alpha.Alpha(44), fixture_beta.Beta(45), fixture_gama.Gama(46)), + fixture_group.Group(fixture_alpha.Alpha(47), fixture_beta.Beta(48), fixture_gama.Gama(49)), + ]), + ); + }); + + test('alpha', () { + const dto = { + fixture_alpha.AlphaDto(4), + fixture_alpha.AlphaDto(5), + }; + final converted = mappr.convertSet(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5)]), + ); + }); + + test('beta', () { + const dto = { + fixture_beta.BetaDto(6), + fixture_beta.BetaDto(7), + fixture_beta.BetaDto(8), + }; + final converted = mappr.convertSet(dto); + + expect( + converted, + equals(const [ + fixture_beta.Beta(6), + fixture_beta.Beta(7), + fixture_beta.Beta(8), + ]), + ); + }); + + test('gama', () { + const dto = { + fixture_gama.GamaDto(9), + fixture_gama.GamaDto(10), + }; + final converted = mappr.convertSet(dto); + + expect(converted, equals(const [fixture_gama.Gama(9), fixture_gama.Gama(10)])); + }); + }); + + group('tryConvertSet', () { + test('group', () { + const dto = { + fixture_group.GroupDto(fixture_alpha.AlphaDto(51), fixture_beta.BetaDto(52), fixture_gama.GamaDto(53)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(54), fixture_beta.BetaDto(55), fixture_gama.GamaDto(56)), + fixture_group.GroupDto(fixture_alpha.AlphaDto(57), fixture_beta.BetaDto(58), fixture_gama.GamaDto(59)), + }; + final converted = mappr.tryConvertSet(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(51), fixture_beta.Beta(52), fixture_gama.Gama(53)), + fixture_group.Group(fixture_alpha.Alpha(54), fixture_beta.Beta(55), fixture_gama.Gama(56)), + fixture_group.Group(fixture_alpha.Alpha(57), fixture_beta.Beta(58), fixture_gama.Gama(59)), + ]), + ); + }); + + test('group w/ null', () { + const dto = { + fixture_group.GroupDto(fixture_alpha.AlphaDto(421), fixture_beta.BetaDto(422), fixture_gama.GamaDto(423)), + null, + fixture_group.GroupDto(fixture_alpha.AlphaDto(431), fixture_beta.BetaDto(432), fixture_gama.GamaDto(433)), + }; + final converted = mappr.tryConvertSet(dto); + + expect( + converted, + equals(const [ + fixture_group.Group(fixture_alpha.Alpha(421), fixture_beta.Beta(422), fixture_gama.Gama(423)), + null, + fixture_group.Group(fixture_alpha.Alpha(431), fixture_beta.Beta(432), fixture_gama.Gama(433)), + ]), + ); + }); + + test('alpha', () { + const dto = { + fixture_alpha.AlphaDto(4), + fixture_alpha.AlphaDto(5), + }; + final converted = mappr.tryConvertSet(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5)]), + ); + }); + + test('alpha w/ null', () { + const dto = { + fixture_alpha.AlphaDto(4), + fixture_alpha.AlphaDto(5), + null, + }; + final converted = mappr.tryConvertSet(dto); + + expect( + converted, + equals(const [fixture_alpha.Alpha(4), fixture_alpha.Alpha(5), null]), + ); + }); + + test('beta', () { + const dto = { + fixture_beta.BetaDto(6), + fixture_beta.BetaDto(7), + fixture_beta.BetaDto(8), + }; + final converted = mappr.tryConvertSet(dto); + + expect( + converted, + equals(const [ + fixture_beta.Beta(6), + fixture_beta.Beta(7), + fixture_beta.Beta(8), + ]), + ); + }); + + test('beta w/ null', () { + const dto = { + null, + fixture_beta.BetaDto(7), + fixture_beta.BetaDto(8), + }; + final converted = mappr.tryConvertSet(dto); + + expect( + converted, + equals(const [null, fixture_beta.Beta(7), fixture_beta.Beta(8)]), + ); + }); + + test('gama', () { + const dto = { + fixture_gama.GamaDto(9), + fixture_gama.GamaDto(10), + }; + final converted = mappr.tryConvertSet(dto); + + expect(converted, equals(const [fixture_gama.Gama(9), fixture_gama.Gama(10)])); + }); + + test('gama w/ null', () { + const dto = { + null, + fixture_gama.GamaDto(9), + fixture_gama.GamaDto(10), + }; + final converted = mappr.tryConvertSet(dto); + + expect( + converted, + equals(const [null, fixture_gama.Gama(9), fixture_gama.Gama(10)]), + ); + }); + }); +} diff --git a/packages/auto_mappr/test/integration/record_test.dart b/packages/auto_mappr/test/integration/record_test.dart new file mode 100644 index 00000000..9df9caac --- /dev/null +++ b/packages/auto_mappr/test/integration/record_test.dart @@ -0,0 +1,83 @@ +import 'package:test/test.dart'; + +import 'fixture/record.dart' as fixture; + +void main() { + late final fixture.Mappr mappr; + + setUpAll(() { + mappr = const fixture.Mappr(); + }); + + group('primitive', () { + test('positional', () { + const dto = fixture.PositionalDto((1, true, 'Brasil')); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.Positional((1, true, 'Brasil')))); + }); + + test('positional nullable', () { + const dto = fixture.PositionalNullableDto((2, false, 'New York')); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.PositionalNullable((2, false, 'New York', null, null)))); + }); + + test('positional to positional nullable', () { + const dto = fixture.PositionalDto((3, false, 'Prague')); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.PositionalNullable((3, false, 'Prague', null, null)))); + }); + + test('named', () { + const dto = fixture.NamedDto((alpha: 5, beta: true, gama: 'Paris')); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.Named((alpha: 5, beta: true, gama: 'Paris')))); + }); + }); + + group('complex', () { + test('positional', () { + const dto = fixture.ComplexPositionalDto([ + fixture.PositionalDto((33, true, 'qwerty')), + fixture.PositionalDto((123, true, 'qwertz')), + fixture.PositionalDto((999, true, 'dvorak')), + ]); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + const fixture.ComplexPositional([ + fixture.Positional((33, true, 'qwerty')), + fixture.Positional((123, true, 'qwertz')), + fixture.Positional((999, true, 'dvorak')), + ]), + ), + ); + }); + + test('named', () { + const dto = fixture.ComplexNamedDto([ + fixture.NamedDto((alpha: 12333, beta: true, gama: 'qwerty')), + fixture.NamedDto((alpha: 123123, beta: true, gama: 'qwertz')), + fixture.NamedDto((alpha: 123999, beta: true, gama: 'dvorak')), + ]); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + const fixture.ComplexNamed([ + fixture.Named((alpha: 12333, beta: true, gama: 'qwerty')), + fixture.Named((alpha: 123123, beta: true, gama: 'qwertz')), + fixture.Named((alpha: 123999, beta: true, gama: 'dvorak')), + ]), + ), + ); + }); + }); +} diff --git a/packages/auto_mappr/test/integration/reverse_test.dart b/packages/auto_mappr/test/integration/reverse_test.dart new file mode 100644 index 00000000..05d1e643 --- /dev/null +++ b/packages/auto_mappr/test/integration/reverse_test.dart @@ -0,0 +1,96 @@ +import 'package:test/test.dart'; + +import 'fixture/reverse.dart' as fixture; + +void main() { + late final fixture.Mappr mappr; + + setUpAll(() { + mappr = const fixture.Mappr(); + }); + + group('converting primitives', () { + test('PrimitiveDto to Primitive', () { + const dto = fixture.PrimitiveDto( + number: 111, + string: 'Uzumaki Naruto', + ); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + const fixture.Primitive(number: 111, string: 'Uzumaki Naruto'), + ), + ); + }); + + test('Primitive to PrimitiveDto', () { + const dto = fixture.Primitive(number: 111, string: 'Uzumaki Naruto'); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + const fixture.PrimitiveDto(number: 111, string: 'Uzumaki Naruto'), + ), + ); + }); + }); + + group('converting nested objects', () { + test('AddressDto to Address', () { + const dto = fixture.AddressDto(street: 'Alpha', city: 'Beta'); + final converted = mappr.convert(dto); + + expect( + converted, + equals(const fixture.Address(street: 'Alpha', city: 'Beta')), + ); + }); + + test('Address to AddressDto', () { + const dto = fixture.Address(street: 'Gama', city: 'Delta'); + final converted = mappr.convert(dto); + + expect( + converted, + equals(const fixture.AddressDto(street: 'Gama', city: 'Delta')), + ); + }); + + test('UserDto to User', () { + const dto = + fixture.UserDto(1, name: 'Xxx 1', address: fixture.AddressDto(street: 'test street 1', city: 'test city 1')); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + const fixture.User( + id: 1, + name: 'Xxx 1', + address: fixture.Address(street: 'test street 1', city: 'test city 1'), + ), + ), + ); + }); + + test('User to UserDto', () { + const dto = + fixture.User(id: 2, name: 'Xxx 2', address: fixture.Address(street: 'test street 2', city: 'test city 2')); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + const fixture.UserDto( + 2, + name: 'Xxx 2', + address: fixture.AddressDto(street: 'test street 2', city: 'test city 2'), + ), + ), + ); + }); + }); +} diff --git a/packages/auto_mappr/test/integration/type_converters_test.dart b/packages/auto_mappr/test/integration/type_converters_test.dart new file mode 100644 index 00000000..c4f3725c --- /dev/null +++ b/packages/auto_mappr/test/integration/type_converters_test.dart @@ -0,0 +1,75 @@ +import 'package:test/test.dart'; + +import 'fixture/type_converters.dart' as fixture; + +void main() { + late final fixture.Mappr mappr; + + setUpAll(() { + mappr = const fixture.Mappr(); + }); + + test('primitives', () { + const dto = fixture.PrimitivesDto(alpha: 123456, beta: false); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.Primitives('--- 123456 ---', '--- false ---'))); + }); + + test('fields', () { + const dto = fixture.NormalFieldDto(xInt: 5, xString: 'Command', normalBool: true); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.NormalField(fixture.Value(5), fixture.Value('Command'), true))); + }); + + test('list', () { + const dto = fixture.InListDto(xInt: [789, 5, 1], xString: 'Dunno', normalBool: false); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + const fixture.InList( + [fixture.Value(789), fixture.Value(5), fixture.Value(1)], + fixture.Value('Dunno'), + false, + ), + ), + ); + }); + + test('map', () { + const dto = fixture.InMapDto( + alpha: {'aaa': 123, 'bbb': 456}, + beta: {'ccc': 789, 'ddd': 741}, + gama: {'eee': 852, 'fff': 963}, + ); + final converted = mappr.convert(dto); + + expect( + converted, + equals( + fixture.InMap( + {const fixture.Value('aaa'): 123, const fixture.Value('bbb'): 456}, + {'ccc': const fixture.Value(789), 'ddd': const fixture.Value(741)}, + {const fixture.Value('eee'): const fixture.Value(852), const fixture.Value('fff'): const fixture.Value(963)}, + ), + ), + ); + }); + + test('includes', () { + const dto = fixture.IncludesDto(alpha: 1); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.Includes(true))); + }); + + test('includes 2', () { + const dto = fixture.IncludesDto(alpha: 2); + final converted = mappr.convert(dto); + + expect(converted, equals(const fixture.Includes(false))); + }); +} diff --git a/packages/auto_mappr_annotation/CHANGELOG.md b/packages/auto_mappr_annotation/CHANGELOG.md index aaa90ecb..c9cff628 100644 --- a/packages/auto_mappr_annotation/CHANGELOG.md +++ b/packages/auto_mappr_annotation/CHANGELOG.md @@ -1,11 +1,20 @@ [//]: # (## Unreleased) -## Unreleased -- Adhere to netglade_analysis 4.0.0 +## 2.0.0 +- Adhere to netglade_analysis 4.0.0. [#111](https://github.com/netglade/auto_mappr/pull/111) +- Add `reverse` option to `MapType`. [#115](https://github.com/netglade/auto_mappr/pull/115) +- Add type converters, use `converters` on `AutoMappr` or `MapType`. [#119](https://github.com/netglade/auto_mappr/pull/119) + +## 2.0.0-beta2 +- Add type converters, use `converters` on `AutoMappr` or `MapType`. [#119](https://github.com/netglade/auto_mappr/pull/119) + +## 2.0.0-beta1 +- Adhere to netglade_analysis 4.0.0. [#111](https://github.com/netglade/auto_mappr/pull/111) +- Add `reverse` option to `MapType`. [#115](https://github.com/netglade/auto_mappr/pull/115) ## 1.2.0 -- Add `ignoreFieldNull` in MapType to force non-nullable field for when source's field is nullable -- Add `ignoreNull` in Field to force non-nullable field for when source's field is nullable +- Add `ignoreFieldNull` in `MapType` to force non-nullable field for when source's field is nullable +- Add `ignoreNull` in `Field`` to force non-nullable field for when source's field is nullable ## 1.1.1 - Adhere to netglade_analysis 2.0.0. diff --git a/packages/auto_mappr_annotation/README.md b/packages/auto_mappr_annotation/README.md index 889d2292..0d023843 100644 --- a/packages/auto_mappr_annotation/README.md +++ b/packages/auto_mappr_annotation/README.md @@ -21,7 +21,7 @@ Developed with 💚 by [netglade][netglade_link] ```dart import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -part 'main.g.dart'; +import 'main.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr_annotation/example/lib/main.dart b/packages/auto_mappr_annotation/example/lib/main.dart index d8d842d6..d6b539ce 100644 --- a/packages/auto_mappr_annotation/example/lib/main.dart +++ b/packages/auto_mappr_annotation/example/lib/main.dart @@ -1,6 +1,6 @@ import 'package:auto_mappr_annotation/auto_mappr_annotation.dart'; -// part 'main.g.dart'; +// part 'main.auto_mappr.dart'; @AutoMappr([ MapType(), diff --git a/packages/auto_mappr_annotation/example/pubspec.yaml b/packages/auto_mappr_annotation/example/pubspec.yaml index 3a603837..cba7aa45 100644 --- a/packages/auto_mappr_annotation/example/pubspec.yaml +++ b/packages/auto_mappr_annotation/example/pubspec.yaml @@ -4,13 +4,12 @@ version: 1.0.0 publish_to: none environment: - sdk: ">=2.18.7 <4.0.0" + sdk: ^3.0.0 dependencies: - auto_mappr_annotation: - path: ../ + auto_mappr_annotation: ^2.0.0 dev_dependencies: build_runner: ^2.0.0 - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/packages/auto_mappr_annotation/lib/auto_mappr_annotation.dart b/packages/auto_mappr_annotation/lib/auto_mappr_annotation.dart index 22784c00..28ba0560 100644 --- a/packages/auto_mappr_annotation/lib/auto_mappr_annotation.dart +++ b/packages/auto_mappr_annotation/lib/auto_mappr_annotation.dart @@ -7,3 +7,4 @@ export 'src/field.dart'; export 'src/iterable_nullable_extension.dart'; export 'src/map_extension.dart'; export 'src/map_type.dart'; +export 'src/type_converter.dart'; diff --git a/packages/auto_mappr_annotation/lib/src/auto_mappr.dart b/packages/auto_mappr_annotation/lib/src/auto_mappr.dart index bd22d593..0a2b2aa2 100644 --- a/packages/auto_mappr_annotation/lib/src/auto_mappr.dart +++ b/packages/auto_mappr_annotation/lib/src/auto_mappr.dart @@ -1,17 +1,31 @@ import 'package:auto_mappr_annotation/src/auto_mappr_interface.dart'; import 'package:auto_mappr_annotation/src/map_type.dart'; +import 'package:auto_mappr_annotation/src/type_converter.dart'; /// Annotates class which will be used as base for generated mappr. -class AutoMappr { +final class AutoMappr { /// List of mapprs. - final List> mappers; + final List> mappers; - /// List of other AutoMappr classes to use as modules. + /// List of type converters. + final List> converters; + + /// List of mapprs that should be included to this mappr. + /// + /// Imagine copy-pasting mappings from included mappr to this one. + final List includes; + + /// List of mapprs used as delegates. /// /// When current mappr cannot convert source to target, /// it lets modules in order try to convert it instead. - final List? modules; + final List delegates; /// Constructs AutoMappr. - const AutoMappr(this.mappers, {this.modules}); + const AutoMappr( + this.mappers, { + this.converters = const [], + this.includes = const [], + this.delegates = const [], + }); } diff --git a/packages/auto_mappr_annotation/lib/src/auto_mappr_interface.dart b/packages/auto_mappr_annotation/lib/src/auto_mappr_interface.dart index 3ba549a8..eef65470 100644 --- a/packages/auto_mappr_annotation/lib/src/auto_mappr_interface.dart +++ b/packages/auto_mappr_annotation/lib/src/auto_mappr_interface.dart @@ -1,7 +1,7 @@ /// AutoMappr interface for converting source objects into target objects. /// /// Users should not implement or extend this. -abstract class AutoMapprInterface { +abstract interface class AutoMapprInterface { /// const AutoMapprInterface(); diff --git a/packages/auto_mappr_annotation/lib/src/field.dart b/packages/auto_mappr_annotation/lib/src/field.dart index f2c12ae9..e7f56503 100644 --- a/packages/auto_mappr_annotation/lib/src/field.dart +++ b/packages/auto_mappr_annotation/lib/src/field.dart @@ -1,5 +1,5 @@ /// Mapping configuration of target object's field. -class Field { +final class Field { /// Which field is mapped. /// /// It should be either name of TARGET's field OR name of TARGET's constructor. diff --git a/packages/auto_mappr_annotation/lib/src/map_type.dart b/packages/auto_mappr_annotation/lib/src/map_type.dart index f8c627c7..7292111c 100644 --- a/packages/auto_mappr_annotation/lib/src/map_type.dart +++ b/packages/auto_mappr_annotation/lib/src/map_type.dart @@ -1,10 +1,14 @@ import 'package:auto_mappr_annotation/src/field.dart'; +import 'package:auto_mappr_annotation/src/type_converter.dart'; /// Configured mapping from [SOURCE] to [TARGET]. -class MapType { +final class MapType { /// Configuration for [TARGET]'s fields. final List fields; + /// List of type converters. + final List> converters; + /// Provides default value if SOURCE is null. /// /// Additionally if mapping an enum "unknown" values in SOURCE will be mapped @@ -25,11 +29,20 @@ class MapType { /// Ignores if [SOURCE]'s field is nullable and [TARGET]'s field non-nullable. final bool? ignoreFieldNull; + /// Includes reverse mapping. + /// + /// Warning: reverse warning might be suitable only for specific objects. + /// Reverse mapping might not work properly when additional configuration + /// such as [whenSourceIsNull] or [constructor] is used. + final bool reverse; + /// Constructs mapping between [SOURCE] and [TARGET] types. const MapType({ this.fields = const [], + this.converters = const [], this.whenSourceIsNull, this.constructor, this.ignoreFieldNull, + this.reverse = false, }); } diff --git a/packages/auto_mappr_annotation/lib/src/type_converter.dart b/packages/auto_mappr_annotation/lib/src/type_converter.dart new file mode 100644 index 00000000..caca4217 --- /dev/null +++ b/packages/auto_mappr_annotation/lib/src/type_converter.dart @@ -0,0 +1,8 @@ +/// Configured type converter from [SOURCE] to [TARGET]. +final class TypeConverter { + /// A function that does type converter. + final TARGET Function(SOURCE source) converter; + + /// Constructs type converter between [SOURCE] and [TARGET]. + const TypeConverter(this.converter); +} diff --git a/packages/auto_mappr_annotation/pubspec.yaml b/packages/auto_mappr_annotation/pubspec.yaml index 8e1b4a7c..838c72da 100644 --- a/packages/auto_mappr_annotation/pubspec.yaml +++ b/packages/auto_mappr_annotation/pubspec.yaml @@ -1,12 +1,12 @@ name: auto_mappr_annotation description: Annotations for the auto_mappr code-generator of mapping between objects with ease. -version: 1.2.0 +version: 2.0.0 repository: https://github.com/netglade/auto_mappr issue_tracker: https://github.com/netglade/auto_mappr/issues environment: - sdk: ">=2.18.7 <4.0.0" + sdk: ^3.0.0 dev_dependencies: - netglade_analysis: ^4.0.0 + netglade_analysis: ^4.2.0 test: ^1.16.0 diff --git a/pubspec.yaml b/pubspec.yaml index d3eabbd2..b51f93a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: auto_mappr_workspace publish_to: "none" environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ^3.0.0 dev_dependencies: melos: ^3.0.0