diff --git a/examples/drift/lib/db.drift.dart b/examples/drift/lib/db.drift.dart index 8d0d128b..1880bdb6 100644 --- a/examples/drift/lib/db.drift.dart +++ b/examples/drift/lib/db.drift.dart @@ -38,9 +38,10 @@ class $TodosTable extends Todos with TableInfo<$TodosTable, Todo> { @override List get $columns => [id, title, content, category]; @override - String get aliasedName => _alias ?? 'todos'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'todos'; + String get actualTableName => $name; + static const String $name = 'todos'; @override VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { @@ -278,9 +279,10 @@ class $CategoriesTable extends Categories @override List get $columns => [id, description]; @override - String get aliasedName => _alias ?? 'categories'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'categories'; + String get actualTableName => $name; + static const String $name = 'categories'; @override VerificationContext validateIntegrity(Insertable instance, {bool isInserting = false}) { diff --git a/examples/drift/lib/mappr.auto_mappr.dart b/examples/drift/lib/mappr.auto_mappr.dart index a31ec4b5..5a5a8764 100644 --- a/examples/drift/lib/mappr.auto_mappr.dart +++ b/examples/drift/lib/mappr.auto_mappr.dart @@ -20,6 +20,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -63,16 +64,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_drift/mappr.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -102,13 +110,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_drift/mappr.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -137,13 +152,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_drift/mappr.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -172,13 +197,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_drift/mappr.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -203,6 +238,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_drift/mappr.dart} + @override + bool useSafeMapping() { + return false; + } + _i3.TodoItem _map__i2$Todo_To__i3$TodoItem(_i2.Todo? input) { final model = input; if (model == null) { diff --git a/examples/example/lib/enum.auto_mappr.dart b/examples/example/lib/enum.auto_mappr.dart index c1c90492..8510b11a 100644 --- a/examples/example/lib/enum.auto_mappr.dart +++ b/examples/example/lib/enum.auto_mappr.dart @@ -21,6 +21,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -76,16 +77,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_example/enum.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -115,13 +123,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_example/enum.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -150,13 +165,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/enum.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -185,13 +210,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/enum.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -237,6 +272,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_example/enum.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.PersonType _map__i2$UserType_To__i2$PersonType(_i2.UserType? input) { final model = input; if (model == null) { diff --git a/examples/example/lib/equatable.auto_mappr.dart b/examples/example/lib/equatable.auto_mappr.dart index 018399df..c7ae4b89 100644 --- a/examples/example/lib/equatable.auto_mappr.dart +++ b/examples/example/lib/equatable.auto_mappr.dart @@ -19,6 +19,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -62,16 +63,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_example/equatable.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -101,13 +109,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_example/equatable.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -136,13 +151,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/equatable.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -171,13 +196,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/equatable.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -202,6 +237,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_example/equatable.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { diff --git a/examples/example/lib/nested.auto_mappr.dart b/examples/example/lib/nested.auto_mappr.dart index 7bc1de54..44c88ded 100644 --- a/examples/example/lib/nested.auto_mappr.dart +++ b/examples/example/lib/nested.auto_mappr.dart @@ -21,6 +21,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -76,16 +77,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_example/nested.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -115,13 +123,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_example/nested.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -150,13 +165,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/nested.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -185,13 +210,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/nested.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -236,6 +271,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_example/nested.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { diff --git a/examples/example/lib/nullable.auto_mappr.dart b/examples/example/lib/nullable.auto_mappr.dart index 15855f48..19b9e8fc 100644 --- a/examples/example/lib/nullable.auto_mappr.dart +++ b/examples/example/lib/nullable.auto_mappr.dart @@ -20,6 +20,7 @@ class $Mappr implements _i2.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i2.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -69,16 +70,23 @@ class $Mappr implements _i2.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_example/nullable.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -108,13 +116,20 @@ class $Mappr implements _i2.AutoMapprInterface { /// {@macro package:examples_example/nullable.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -143,13 +158,23 @@ class $Mappr implements _i2.AutoMapprInterface { /// /// {@macro package:examples_example/nullable.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -178,13 +203,23 @@ class $Mappr implements _i2.AutoMapprInterface { /// /// {@macro package:examples_example/nullable.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -225,6 +260,35 @@ class $Mappr implements _i2.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_example/nullable.dart} + @override + bool useSafeMapping() { + return false; + } + _i1.User _map__i1$UserDto_To__i1$User(_i1.UserDto? input) { final model = input; if (model == null) { diff --git a/examples/example/lib/rename.auto_mappr.dart b/examples/example/lib/rename.auto_mappr.dart index c1c49985..ea3f9806 100644 --- a/examples/example/lib/rename.auto_mappr.dart +++ b/examples/example/lib/rename.auto_mappr.dart @@ -19,6 +19,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -62,16 +63,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_example/rename.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -101,13 +109,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_example/rename.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -136,13 +151,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/rename.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -171,13 +196,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_example/rename.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -202,6 +237,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_example/rename.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { diff --git a/examples/freezed/lib/freezed_example.auto_mappr.dart b/examples/freezed/lib/freezed_example.auto_mappr.dart index 569c0bd9..5b22fd4e 100644 --- a/examples/freezed/lib/freezed_example.auto_mappr.dart +++ b/examples/freezed/lib/freezed_example.auto_mappr.dart @@ -19,6 +19,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -62,16 +63,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_freezed/freezed_example.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -101,13 +109,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_freezed/freezed_example.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -136,13 +151,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_freezed/freezed_example.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -171,13 +196,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_freezed/freezed_example.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -203,6 +238,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_freezed/freezed_example.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.UserInfoCompanion _map__i2$UserInfo_To__i2$UserInfoCompanion( _i2.UserInfo? input) { final model = input; diff --git a/examples/freezed/lib/freezed_example.freezed.dart b/examples/freezed/lib/freezed_example.freezed.dart index 84a2c90d..7477ddfc 100644 --- a/examples/freezed/lib/freezed_example.freezed.dart +++ b/examples/freezed/lib/freezed_example.freezed.dart @@ -12,7 +12,7 @@ part of 'freezed_example.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$UserInfoUnion { @@ -79,11 +79,11 @@ class _$UserInfoUnionCopyWithImpl<$Res, $Val extends UserInfoUnion> } /// @nodoc -abstract class _$$UserInfoCopyWith<$Res> +abstract class _$$UserInfoImplCopyWith<$Res> implements $UserInfoUnionCopyWith<$Res> { - factory _$$UserInfoCopyWith( - _$UserInfo value, $Res Function(_$UserInfo) then) = - __$$UserInfoCopyWithImpl<$Res>; + factory _$$UserInfoImplCopyWith( + _$UserInfoImpl value, $Res Function(_$UserInfoImpl) then) = + __$$UserInfoImplCopyWithImpl<$Res>; @override @useResult $Res call( @@ -94,10 +94,11 @@ abstract class _$$UserInfoCopyWith<$Res> } /// @nodoc -class __$$UserInfoCopyWithImpl<$Res> - extends _$UserInfoUnionCopyWithImpl<$Res, _$UserInfo> - implements _$$UserInfoCopyWith<$Res> { - __$$UserInfoCopyWithImpl(_$UserInfo _value, $Res Function(_$UserInfo) _then) +class __$$UserInfoImplCopyWithImpl<$Res> + extends _$UserInfoUnionCopyWithImpl<$Res, _$UserInfoImpl> + implements _$$UserInfoImplCopyWith<$Res> { + __$$UserInfoImplCopyWithImpl( + _$UserInfoImpl _value, $Res Function(_$UserInfoImpl) _then) : super(_value, _then); @pragma('vm:prefer-inline') @@ -108,7 +109,7 @@ class __$$UserInfoCopyWithImpl<$Res> Object? updatedAt = null, Object? primarySectionId = null, }) { - return _then(_$UserInfo( + return _then(_$UserInfoImpl( email: null == email ? _value.email : email // ignore: cast_nullable_to_non_nullable @@ -131,8 +132,8 @@ class __$$UserInfoCopyWithImpl<$Res> /// @nodoc -class _$UserInfo implements UserInfo { - _$UserInfo( +class _$UserInfoImpl implements UserInfo { + _$UserInfoImpl( {required this.email, required this.loginIdentifier, required this.updatedAt, @@ -154,10 +155,10 @@ class _$UserInfo implements UserInfo { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$UserInfo && + other is _$UserInfoImpl && (identical(other.email, email) || other.email == email) && (identical(other.loginIdentifier, loginIdentifier) || other.loginIdentifier == loginIdentifier) && @@ -174,8 +175,8 @@ class _$UserInfo implements UserInfo { @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$UserInfoCopyWith<_$UserInfo> get copyWith => - __$$UserInfoCopyWithImpl<_$UserInfo>(this, _$identity); + _$$UserInfoImplCopyWith<_$UserInfoImpl> get copyWith => + __$$UserInfoImplCopyWithImpl<_$UserInfoImpl>(this, _$identity); } abstract class UserInfo implements UserInfoUnion { @@ -183,7 +184,7 @@ abstract class UserInfo implements UserInfoUnion { {required final String email, required final String loginIdentifier, required final DateTime updatedAt, - final int primarySectionId}) = _$UserInfo; + final int primarySectionId}) = _$UserInfoImpl; @override String get email; @@ -195,6 +196,6 @@ abstract class UserInfo implements UserInfoUnion { int get primarySectionId; @override @JsonKey(ignore: true) - _$$UserInfoCopyWith<_$UserInfo> get copyWith => + _$$UserInfoImplCopyWith<_$UserInfoImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/examples/injectable/lib/getit.config.dart b/examples/injectable/lib/getit.config.dart index bd6ea92c..9818a2fc 100644 --- a/examples/injectable/lib/getit.config.dart +++ b/examples/injectable/lib/getit.config.dart @@ -4,8 +4,7 @@ // InjectableConfigGenerator // ************************************************************************** -// ignore_for_file: unnecessary_lambdas -// ignore_for_file: lines_longer_than_80_chars +// ignore_for_file: type=lint // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes diff --git a/examples/injectable/lib/mappr.auto_mappr.dart b/examples/injectable/lib/mappr.auto_mappr.dart index eac09338..6a080faf 100644 --- a/examples/injectable/lib/mappr.auto_mappr.dart +++ b/examples/injectable/lib/mappr.auto_mappr.dart @@ -19,6 +19,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -62,16 +63,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_injectable/mappr.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -101,13 +109,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_injectable/mappr.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -136,13 +151,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_injectable/mappr.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -171,13 +196,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_injectable/mappr.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -202,6 +237,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_injectable/mappr.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { diff --git a/examples/json_serializable/lib/serializable.auto_mappr.dart b/examples/json_serializable/lib/serializable.auto_mappr.dart index 9d58f240..6ac9e197 100644 --- a/examples/json_serializable/lib/serializable.auto_mappr.dart +++ b/examples/json_serializable/lib/serializable.auto_mappr.dart @@ -20,6 +20,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -69,16 +70,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro package:examples_json_serializable/serializable.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -108,13 +116,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro package:examples_json_serializable/serializable.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -143,13 +158,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_json_serializable/serializable.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -178,13 +203,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro package:examples_json_serializable/serializable.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -219,6 +254,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro package:examples_json_serializable/serializable.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { diff --git a/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart b/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart index 9ebc5566..c3a26714 100644 --- a/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart +++ b/examples/json_serializable/test/fixture/json_serializable.auto_mappr.dart @@ -20,6 +20,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -69,16 +70,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -108,13 +116,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -143,13 +158,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -178,13 +203,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -219,6 +254,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro asset:examples_json_serializable/test/fixture/json_serializable.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { diff --git a/packages/auto_mappr/CHANGELOG.md b/packages/auto_mappr/CHANGELOG.md index 79ea9248..8b6f69a2 100644 --- a/packages/auto_mappr/CHANGELOG.md +++ b/packages/auto_mappr/CHANGELOG.md @@ -1,5 +1,8 @@ [//]: # (## Unreleased) +## 2.5.0 +- Add support for safe mapping (mapping exceptions handling). [#216](https://github.com/netglade/auto_mappr/pull/216) + ## 2.4.0 - Fix nested types ignores settings about null values (whenNull and ignoreNull). [#191](https://github.com/netglade/auto_mappr/pull/191) - Fix not considering renaming ("from") when mapping using setters. [#192](https://github.com/netglade/auto_mappr/pull/192) diff --git a/packages/auto_mappr/README.md b/packages/auto_mappr/README.md index 02c3c5c0..db7afcbf 100644 --- a/packages/auto_mappr/README.md +++ b/packages/auto_mappr/README.md @@ -41,6 +41,7 @@ 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) + - [Safe mapping](#safe-mapping) - [Generics](#generics) - [Library import aliases](#library-import-aliases) - [Modules](#modules) @@ -479,7 +480,7 @@ This is by design and it is up to developer decide if bang operator is appropiat ```yaml $default: builders: - :auto_mappr: + auto_mappr: options: ignoreNullableSourceField: true ``` @@ -488,6 +489,44 @@ This is by design and it is up to developer decide if bang operator is appropiat Precedense is `global configuration` -> `MapType` -> `Field` -> defaults to false. +### Safe mapping +Safe mapping is a auto_mapr feature that automatically handles exceptions thrown during the mapping. +Typically these exceptions can be caused by mapping from source with a nullable variable +to a target with a corresponding non-nullable variable. +If you want to avoid propagating the exception through the app, you can enable safe mapping and +auto_mappr can catch the exception and return null as a result of mapping instead. +In case you want to know that something went wrong during the mapping +there is a `onMappingError` callback that is invoked each time error occurs. +Enabling safe mapping can be done +either on the global level in `build.yaml` with `safeMapping` option + + ```yaml + $default: + builders: + auto_mappr: + options: + safeMapping: true + ``` + +or individually for each `MapType` with `safeMapping` option: + +```dart +@AutoMappr([ + MapType( + safeMapping: true, + ), +]) +class Mappr extends $Mappr {} +``` +Precedense is `global configuration` -> `MapType` -> defaults to false. + +Safe mapping can be done only through +`tryConvert`, `tryConvertIterable`, `tryConvertList` or `tryConvertSet` methods. +All of them have an optional parameter +`void Function(Object error, StackTrace stackTrace, SOURCE? source)? onMappingError`, +that is a callback function triggered whenever an exception occurs during the mapping process. +This can be used fo example for logging the error. + ### Generics AutoMappr can handle generics, so you can map objects with type arguments with ease. diff --git a/packages/auto_mappr/example/lib/mappr.auto_mappr.dart b/packages/auto_mappr/example/lib/mappr.auto_mappr.dart index 586a8e9a..bd23fe64 100644 --- a/packages/auto_mappr/example/lib/mappr.auto_mappr.dart +++ b/packages/auto_mappr/example/lib/mappr.auto_mappr.dart @@ -20,6 +20,7 @@ class $Mappr implements _i1.AutoMapprInterface { const $Mappr(); Type _typeOf() => T; + List<_i1.AutoMapprInterface> get _delegates => const []; /// {@macro AutoMapprInterface:canConvert} @@ -69,16 +70,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro AutoMapprInterface:tryConvert} /// {@macro asset:auto_mappr/example/lib/mappr.dart} @override - TARGET? tryConvert(SOURCE? model) { + TARGET? tryConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return _convert( + return _safeConvert( model, - canReturnNull: true, + onMappingError: onMappingError, ); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvert(model); + return mappr.tryConvert( + model, + onMappingError: onMappingError, + ); } } @@ -108,13 +116,20 @@ class $Mappr implements _i1.AutoMapprInterface { /// {@macro asset:auto_mappr/example/lib/mappr.dart} @override Iterable tryConvertIterable( - Iterable model) { + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return model.map((item) => _convert(item, canReturnNull: true)); + return model.map( + (item) => _safeConvert(item, onMappingError: onMappingError)); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertIterable(model); + return mappr.tryConvertIterable( + model, + onMappingError: onMappingError, + ); } } @@ -143,13 +158,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro asset:auto_mappr/example/lib/mappr.dart} @override - List tryConvertList(Iterable model) { + List tryConvertList( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toList(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toList(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertList(model); + return mappr.tryConvertList( + model, + onMappingError: onMappingError, + ); } } @@ -178,13 +203,23 @@ class $Mappr implements _i1.AutoMapprInterface { /// /// {@macro asset:auto_mappr/example/lib/mappr.dart} @override - Set tryConvertSet(Iterable model) { + Set tryConvertSet( + Iterable model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { if (canConvert(recursive: false)) { - return tryConvertIterable(model).toSet(); + return tryConvertIterable( + model, + onMappingError: onMappingError, + ).toSet(); } for (final mappr in _delegates) { if (mappr.canConvert()) { - return mappr.tryConvertSet(model); + return mappr.tryConvertSet( + model, + onMappingError: onMappingError, + ); } } @@ -219,6 +254,35 @@ class $Mappr implements _i1.AutoMapprInterface { throw Exception('No ${model.runtimeType} -> $targetTypeOf mapping.'); } + TARGET? _safeConvert( + SOURCE? model, { + void Function(Object error, StackTrace stackTrace, SOURCE? source)? + onMappingError, + }) { + if (!useSafeMapping()) { + return _convert( + model, + canReturnNull: true, + ); + } + try { + return _convert( + model, + canReturnNull: true, + ); + } catch (e, s) { + onMappingError?.call(e, s, model); + return null; + } + } + + /// {@macro AutoMapprInterface:useSafeMapping} + /// {@macro asset:auto_mappr/example/lib/mappr.dart} + @override + bool useSafeMapping() { + return false; + } + _i2.User _map__i2$UserDto_To__i2$User(_i2.UserDto? input) { final model = input; if (model == null) { 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 617531e5..378ae6f1 100644 --- a/packages/auto_mappr/lib/src/builder/auto_mappr_builder.dart +++ b/packages/auto_mappr/lib/src/builder/auto_mappr_builder.dart @@ -107,6 +107,12 @@ class AutoMapprBuilder { // Internal convert method. PrivateConvertMethodBuilder(config).buildMethod(), + // Internal safe convert method. + SafeConvertMethodBuilder(config).buildMethod(), + + // Use safe mapping method. + UseSafeMappingMethodBuilder(config).buildMethod(), + // Generate non-nullable mapping method. for (final mapping in config.mappers) MappingMethodBuilder( 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 512363d7..173737e2 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 @@ -84,4 +84,15 @@ abstract class MethodBuilderBase { return ifConditionExpression.ifStatement2(ifBody: inIfExpression); } + + Expression buildSourceAndTargetEquals({required TypeMapping mapping}) { + final sourceName = EmitterHelper.current.typeReferEmitted(type: mapping.source); + final targetName = EmitterHelper.current.typeReferEmitted(type: mapping.target); + + final sourceEquation = refer('sourceTypeOf').equalTo(refer('_typeOf<$sourceName>()')); + + final targetEquation = refer('targetTypeOf').equalTo(refer('_typeOf<$targetName>()')); + + return sourceEquation.and(targetEquation); + } } diff --git a/packages/auto_mappr/lib/src/builder/methods/methods.dart b/packages/auto_mappr/lib/src/builder/methods/methods.dart index 0cb05ab5..b4d5a6f8 100644 --- a/packages/auto_mappr/lib/src/builder/methods/methods.dart +++ b/packages/auto_mappr/lib/src/builder/methods/methods.dart @@ -7,6 +7,8 @@ export 'mapping_method_builder.dart'; export 'method_builder_base.dart'; export 'private_convert_method_builder.dart'; export 'private_modules_method_builder.dart'; +export 'safe_convert_method_builder.dart'; export 'try_convert_iterable_method_builder.dart'; export 'try_convert_method_builder.dart'; export 'type_of_method_builder.dart'; +export 'use_safe_mapping_method_builder.dart'; 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 028605b1..30f40fa4 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 @@ -74,7 +74,8 @@ class PrivateConvertMethodBuilder extends MethodBuilderBase { refer(mapping.mappingMethodName(config: config)) .call( [ - MethodBuilderBase.modelReference.asA(EmitterHelper.current.typeRefer(type: mapping.source).nullabled()), + MethodBuilderBase.modelReference + .asA(EmitterHelper.current.typeRefer(type: mapping.source).nullabled()), ], ) .asA(MethodBuilderBase.targetTypeReference) diff --git a/packages/auto_mappr/lib/src/builder/methods/safe_convert_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/safe_convert_method_builder.dart new file mode 100644 index 00000000..f38c8b7f --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/methods/safe_convert_method_builder.dart @@ -0,0 +1,75 @@ +import 'package:auto_mappr/src/builder/methods/methods.dart'; +import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:code_builder/code_builder.dart'; + +class SafeConvertMethodBuilder extends MethodBuilderBase { + const SafeConvertMethodBuilder(super.config); + + @override + Method buildMethod() { + return Method( + (b) => b + ..name = '_safeConvert' + ..types.addAll([MethodBuilderBase.sourceTypeReference, MethodBuilderBase.targetTypeReference]) + ..requiredParameters.add( + Parameter( + (p) => p + ..name = 'model' + ..type = MethodBuilderBase.nullableSourceTypeReference, + ), + ) + ..optionalParameters.add( + Parameter( + (p) => p + ..name = 'onMappingError' + ..type = refer('void Function(Object error, StackTrace stackTrace, SOURCE? source)?') + ..named = true, + ), + ) + ..returns = MethodBuilderBase.nullableTargetTypeReference + ..body = buildBody(), + ); + } + + @override + Code buildBody() { + final block = BlockBuilder(); + + // Generates code like: + // + // if (!useSafeMapping()) { + // return _convert( + // model, + // canReturnNull: true, + // ); + // } + block.statements.add( + ExpressionExtension.ifStatement( + condition: UseSafeMappingMethodBuilder(config).methodCall(), + ifBody: refer('_convert').call([refer('model')], {'canReturnNull': refer('true')}, []).returned.statement, + negation: true, + ).code, + ); + + // Generates code like: + // + // try { + // return _convert( + // model, + // canReturnNull: true, + // ); + // } catch (e, s) { + // onMappingError?.call(e, s, model); + // return null; + // } + block.statements.add( + refer('_convert') + .call([refer('model')], {'canReturnNull': refer('true')}, []) + .returned + .tryCatchWrapped(catchBody: refer('onMappingError?.call(e, s, model)').statement) + .code, + ); + + return block.build(); + } +} 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 aa4a6ff1..ddc97e49 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 @@ -41,6 +41,14 @@ class TryConvertIterableMethodBuilder extends MethodBuilderBase { ), ), ) + ..optionalParameters.add( + Parameter( + (p) => p + ..name = 'onMappingError' + ..type = refer('void Function(Object error, StackTrace stackTrace, SOURCE? source)?') + ..named = true, + ), + ) ..returns = Reference( '$wrapper<${MethodBuilderBase.nullableTargetTypeReference.accept(EmitterHelper.current.emitter)}>', ) @@ -61,14 +69,14 @@ class TryConvertIterableMethodBuilder extends MethodBuilderBase { // tryConvertIterable(model).toList() final convertIterableCall = iterableTransformer == null ? MethodBuilderBase.modelReference.property('map').call( - [refer('(item) => _convert(item, canReturnNull: true)')], + [refer('(item) => _safeConvert(item, onMappingError: onMappingError)')], {}, [MethodBuilderBase.nullableTargetTypeReference], ) : refer('tryConvertIterable') .call( [MethodBuilderBase.modelReference], - {}, + {'onMappingError': refer('onMappingError')}, [MethodBuilderBase.sourceTypeReference, MethodBuilderBase.targetTypeReference], ) .property(iterableTransformer!) @@ -102,7 +110,7 @@ class TryConvertIterableMethodBuilder extends MethodBuilderBase { condition: CanConvertMethodBuilder(config).propertyCall(on: mapprReference), ifBody: mapprReference .property('tryConvert$wrapper') - .call([MethodBuilderBase.modelReference], {}, []) + .call([MethodBuilderBase.modelReference], {'onMappingError': refer('onMappingError')}, []) .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 174c28d8..2b03900d 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 @@ -4,8 +4,6 @@ import 'package:auto_mappr/src/extensions/expression_extension.dart'; import 'package:built_collection/built_collection.dart'; import 'package:code_builder/code_builder.dart'; -// modules OK -// modules tests OK class TryConvertMethodBuilder extends MethodBuilderBase { const TryConvertMethodBuilder(super.config); @@ -27,6 +25,14 @@ class TryConvertMethodBuilder extends MethodBuilderBase { ..type = MethodBuilderBase.nullableSourceTypeReference, ), ) + ..optionalParameters.add( + Parameter( + (p) => p + ..name = 'onMappingError' + ..type = refer('void Function(Object error, StackTrace stackTrace, SOURCE? source)?') + ..named = true, + ), + ) ..returns = MethodBuilderBase.nullableTargetTypeReference ..body = buildBody(), ); @@ -38,21 +44,24 @@ class TryConvertMethodBuilder extends MethodBuilderBase { // Generates code like: // - // if (canConvert(model)) { - // return _convert(model)!; + // if (canConvert(recursive: false)) { + // return _safeConvert(model, onMappingError: onMappingError); // } block.statements.add( ExpressionExtension.ifStatement( condition: CanConvertMethodBuilder(config).methodCall(namedArguments: {'recursive': literalFalse}), - ifBody: refer('_convert').call([refer('model')], {'canReturnNull': refer('true')}, []).returned.statement, + ifBody: refer('_safeConvert') + .call([refer('model')], {'onMappingError': refer('onMappingError')}) + .returned + .statement, ).code, ); // Generates code like: // - // for (final mappr in mappers) { - // if (mappr.canConvert(model)) { - // return mappr.convert(model)!; + // for (final mappr in _delegates) { + // if (mappr.canConvert()) { + // return mappr.tryConvert(model, onMappingError: onMappingError); // } // } final mapprReference = refer('mappr'); @@ -62,7 +71,11 @@ class TryConvertMethodBuilder extends MethodBuilderBase { iterable: refer(MethodBuilderBase.delegatesField), body: ExpressionExtension.ifStatement( condition: CanConvertMethodBuilder(config).propertyCall(on: mapprReference), - ifBody: mapprReference.property('tryConvert').call([MethodBuilderBase.modelReference], {}, []).returned.statement, + ifBody: mapprReference + .property('tryConvert') + .call([MethodBuilderBase.modelReference], {'onMappingError': refer('onMappingError')}, []) + .returned + .statement, ), ).code, ); diff --git a/packages/auto_mappr/lib/src/builder/methods/use_safe_mapping_method_builder.dart b/packages/auto_mappr/lib/src/builder/methods/use_safe_mapping_method_builder.dart new file mode 100644 index 00000000..3ec76c5f --- /dev/null +++ b/packages/auto_mappr/lib/src/builder/methods/use_safe_mapping_method_builder.dart @@ -0,0 +1,109 @@ +import 'package:auto_mappr/src/builder/methods/methods.dart'; +import 'package:auto_mappr/src/extensions/expression_extension.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:code_builder/code_builder.dart'; + +class UseSafeMappingMethodBuilder extends MethodBuilderBase implements CallableMethod, CallableProperty { + const UseSafeMappingMethodBuilder(super.config); + + @override + Method buildMethod() { + return Method( + (b) => b + ..name = 'useSafeMapping' + ..docs = ListBuilder([ + '/// {@macro AutoMapprInterface:useSafeMapping}', + config.availableMappingsDocComment, + ]) + ..annotations = MethodBuilderBase.overrideAnnotation + ..types.addAll([MethodBuilderBase.sourceTypeReference, MethodBuilderBase.targetTypeReference]) + ..returns = refer('bool') + ..body = buildBody(), + ); + } + + @override + Code buildBody() { + final block = BlockBuilder(); + + if (config.mapprOptions.safeMapping ?? false) { + block.statements.add(literalTrue.returned.statement); + + return block.build(); + } + + final safeMappers = config.mappers.where((m) => m.safeMapping ?? false); + + if (safeMappers.isEmpty) { + block.statements.add(literalFalse.returned.statement); + + return block.build(); + } + + // Generates code like: + // + // final sourceTypeOf = _typeOf(); + // final targetTypeOf = _typeOf(); + // if (sourceTypeOf == _typeOf() && + // targetTypeOf == _typeOf()) { + // return true; + // } + final sourceTypeOfVariable = declareFinal('sourceTypeOf').assign(MethodBuilderBase.sourceTypeOf); + block.addExpression(sourceTypeOfVariable); + + final targetTypeOfVariable = declareFinal('targetTypeOf').assign(MethodBuilderBase.targetTypeOf); + block.addExpression(targetTypeOfVariable); + + for (final mapper in safeMappers) { + final ifStatement = ExpressionExtension.ifStatement( + condition: buildSourceAndTargetEquals(mapping: mapper), + ifBody: literalTrue.returned.statement, + ).code; + + block.statements.add(ifStatement); + } + + // Generates code like: + // + // for (final mappr in _delegates) { + // if (mappr.useSafeMapping()) { + // return true; + // } + // } + block.statements.add( + ExpressionExtension.forStatement( + item: refer('mappr'), + iterable: refer(MethodBuilderBase.delegatesField), + body: ExpressionExtension.ifStatement( + condition: UseSafeMappingMethodBuilder(config).propertyCall(on: refer('mappr')), + ifBody: literalTrue.returned.statement, + ), + ).code, + ); + + block.statements.add(literalFalse.returned.statement); + + return block.build(); + } + + @override + Expression methodCall({Map namedArguments = const {}}) { + return refer('useSafeMapping').call( + const [], + namedArguments, + [MethodBuilderBase.sourceTypeReference, MethodBuilderBase.targetTypeReference], + ); + } + + @override + Expression propertyCall({ + required Reference on, + Map namedArguments = const {}, + }) { + return on.property('useSafeMapping').call( + const [], + namedArguments, + [MethodBuilderBase.sourceTypeReference, MethodBuilderBase.targetTypeReference], + ); + } +} diff --git a/packages/auto_mappr/lib/src/extensions/expression_extension.dart b/packages/auto_mappr/lib/src/extensions/expression_extension.dart index bd45eeb1..111b1bbc 100644 --- a/packages/auto_mappr/lib/src/extensions/expression_extension.dart +++ b/packages/auto_mappr/lib/src/extensions/expression_extension.dart @@ -106,13 +106,14 @@ extension ExpressionExtension on Expression { required Spec condition, required Spec ifBody, Spec? elseBody, + bool negation = false, }) { final emitter = EmitterHelper.current.emitter; final ifBlock = '{ ${ifBody.accept(emitter)} }'; final elseBlock = (elseBody != null) ? 'else { ${elseBody.accept(emitter)} }' : ''; - return refer('''if ( ${condition.accept(emitter)} ) $ifBlock $elseBlock'''); + return refer('''if ( ${negation ? '!' : ''}${condition.accept(emitter)} ) $ifBlock $elseBlock'''); } Reference ifStatement2({required Spec ifBody, Spec? elseBody}) { @@ -144,5 +145,16 @@ for (final ${item.accept(emitter)} in ${iterable.accept(emitter)}) { return refer('${accept(EmitterHelper.current.emitter)}?'); } + Reference tryCatchWrapped({Spec? catchBody}) { + return refer(''' + try { + ${accept(EmitterHelper.current.emitter)}; + } catch(e, s) { + ${catchBody?.accept(EmitterHelper.current.emitter) ?? ''} + return null; + } + '''); + } + Expression equalToNull() => equalTo(literalNull); } 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 6737eaab..63ac56bd 100644 --- a/packages/auto_mappr/lib/src/generator/auto_mappr_generator.dart +++ b/packages/auto_mappr/lib/src/generator/auto_mappr_generator.dart @@ -34,6 +34,7 @@ class AutoMapprGenerator extends GeneratorForAnnotation { static const String mapTypeFieldConstructor = 'constructor'; static const String mapTypeFieldIgnoreFieldNull = 'ignoreFieldNull'; static const String mapTypeFieldReverse = 'reverse'; + static const String mapTypeSafeMapping = 'safeMapping'; // Constants for Field. static const String fieldFieldField = 'field'; @@ -138,6 +139,7 @@ class AutoMapprGenerator extends GeneratorForAnnotation { final constructor = mapper.getField(mapTypeFieldConstructor)?.toStringValue(); final ignoreFieldNull = mapper.getField(mapTypeFieldIgnoreFieldNull)?.toBoolValue(); final reverse = mapper.getField(mapTypeFieldReverse)?.toBoolValue(); + final safeMapping = mapper.getField(mapTypeSafeMapping)?.toBoolValue(); final fieldMappings = fields ?.map( @@ -162,6 +164,7 @@ class AutoMapprGenerator extends GeneratorForAnnotation { whenSourceIsNullExpression: whenSourceIsNull, constructor: constructor, ignoreFieldNull: ignoreFieldNull, + safeMapping: safeMapping, ), if (reverse ?? false) TypeMapping( @@ -186,6 +189,7 @@ class AutoMapprGenerator extends GeneratorForAnnotation { whenSourceIsNullExpression: whenSourceIsNull, constructor: constructor, ignoreFieldNull: ignoreFieldNull, + safeMapping: safeMapping, ), ]; }) diff --git a/packages/auto_mappr/lib/src/models/auto_mappr_options.dart b/packages/auto_mappr/lib/src/models/auto_mappr_options.dart index 2088095b..77bb8860 100644 --- a/packages/auto_mappr/lib/src/models/auto_mappr_options.dart +++ b/packages/auto_mappr/lib/src/models/auto_mappr_options.dart @@ -1,11 +1,13 @@ class AutoMapprOptions { final bool? ignoreNullableSourceField; + final bool? safeMapping; - const AutoMapprOptions({required this.ignoreNullableSourceField}); + const AutoMapprOptions({required this.ignoreNullableSourceField, required this.safeMapping}); factory AutoMapprOptions.fromJson(Map json) { final ignoreNullableSourceField = json['ignoreNullableSourceField'] as bool? ?? false; + final safeMapping = json['safeMapping'] as bool? ?? false; - return AutoMapprOptions(ignoreNullableSourceField: ignoreNullableSourceField); + return AutoMapprOptions(ignoreNullableSourceField: ignoreNullableSourceField, safeMapping: safeMapping); } } diff --git a/packages/auto_mappr/lib/src/models/type_mapping.dart b/packages/auto_mappr/lib/src/models/type_mapping.dart index edc02c68..2d5447bf 100644 --- a/packages/auto_mappr/lib/src/models/type_mapping.dart +++ b/packages/auto_mappr/lib/src/models/type_mapping.dart @@ -16,6 +16,7 @@ class TypeMapping extends Equatable { final Expression? whenSourceIsNullExpression; final String? constructor; final bool? ignoreFieldNull; + final bool? safeMapping; bool get isEnumMapping => source.element is EnumElement || target.element is EnumElement; @@ -29,6 +30,7 @@ class TypeMapping extends Equatable { whenSourceIsNullExpression, constructor, ignoreFieldNull, + safeMapping, ]; } @@ -40,6 +42,7 @@ class TypeMapping extends Equatable { this.typeConverters = const [], this.whenSourceIsNullExpression, this.constructor, + this.safeMapping, }); String mappingMethodName({required AutoMapprConfig config}) => MethodBuilderBase.constructConvertMethodName( diff --git a/packages/auto_mappr/pubspec.yaml b/packages/auto_mappr/pubspec.yaml index 3e8233dd..58ef2c9d 100644 --- a/packages/auto_mappr/pubspec.yaml +++ b/packages/auto_mappr/pubspec.yaml @@ -1,6 +1,6 @@ name: auto_mappr description: Code generation for mapping between different objects with ease. -version: 2.4.0 +version: 2.5.0 repository: https://github.com/netglade/auto_mappr issue_tracker: https://github.com/netglade/auto_mappr/issues screenshots: 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 efd568f8..d638303e 100644 --- a/packages/auto_mappr/test/builder/convert_method_builder_test.dart +++ b/packages/auto_mappr/test/builder/convert_method_builder_test.dart @@ -17,7 +17,7 @@ void main() { AutoMapprConfig( mappers: [], availableMappingsMacroId: 'test', - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false, safeMapping: false), ), ).buildMethod(); @@ -45,7 +45,7 @@ void main() { AutoMapprConfig( mappers: [], availableMappingsMacroId: 'test', - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false, safeMapping: false), ), ).buildMethod(); @@ -53,7 +53,17 @@ void main() { 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.optionalParameters.toList(), + equals([ + Parameter( + (p) => p + ..name = 'onMappingError' + ..named = true + ..type = refer('void Function(Object error, StackTrace stackTrace, SOURCE? source)?'), + ), + ]), + ); expect( result.requiredParameters.toList(), equals([ @@ -73,7 +83,7 @@ void main() { AutoMapprConfig( mappers: [], availableMappingsMacroId: 'test', - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false, safeMapping: false), ), ).buildMethod(); @@ -101,7 +111,7 @@ void main() { AutoMapprConfig( mappers: [], availableMappingsMacroId: 'test', - mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false), + mapprOptions: AutoMapprOptions(ignoreNullableSourceField: false, safeMapping: false), ), ).buildMethod(); diff --git a/packages/auto_mappr/test/integration/delegates_test.dart b/packages/auto_mappr/test/integration/delegates_test.dart index c3f3c5a0..0e74465b 100644 --- a/packages/auto_mappr/test/integration/delegates_test.dart +++ b/packages/auto_mappr/test/integration/delegates_test.dart @@ -91,6 +91,15 @@ void main() { expect(converted, isNull); }); + test('beta w/ null value', () { + const dto = fixture_beta.BetaDto(null); + + expect( + () => mappr.tryConvert(dto), + throwsA(predicate((e) => e is TypeError)), + ); + }); + test('gama', () { const dto = fixture_gama.GamaDto(4); final converted = mappr.tryConvert(dto); @@ -104,6 +113,13 @@ void main() { expect(converted, isNull); }); + + test('gama w/ null value (safe mapping on)', () { + const dto = fixture_gama.GamaDto(null); + final converted = mappr.tryConvert(dto); + + expect(converted, isNull); + }); }); group('convertIterable', () { @@ -286,7 +302,7 @@ void main() { group('convertList', () { test('group', () { - const dto = [ + const dto = [ fixture_group.GroupDto(1), fixture_group.GroupDto(2), fixture_group.GroupDto(3), @@ -304,10 +320,7 @@ void main() { }); test('alpha', () { - const dto = [ - fixture_alpha.AlphaDto(4), - fixture_alpha.AlphaDto(5), - ]; + const dto = [fixture_alpha.AlphaDto(4), fixture_alpha.AlphaDto(5)]; final converted = mappr.convertList(dto); expect( @@ -317,7 +330,7 @@ void main() { }); test('beta', () { - const dto = [ + const dto = [ fixture_beta.BetaDto(6), fixture_beta.BetaDto(7), fixture_beta.BetaDto(8), @@ -335,10 +348,7 @@ void main() { }); test('gama', () { - const dto = [ - fixture_gama.GamaDto(9), - fixture_gama.GamaDto(10), - ]; + 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)])); @@ -464,7 +474,7 @@ void main() { group('convertSet', () { test('group', () { - const dto = { + const dto = { fixture_group.GroupDto(1), fixture_group.GroupDto(2), fixture_group.GroupDto(3), @@ -482,10 +492,7 @@ void main() { }); test('alpha', () { - const dto = { - fixture_alpha.AlphaDto(4), - fixture_alpha.AlphaDto(5), - }; + const dto = {fixture_alpha.AlphaDto(4), fixture_alpha.AlphaDto(5)}; final converted = mappr.convertSet(dto); expect( @@ -495,7 +502,7 @@ void main() { }); test('beta', () { - const dto = { + const dto = { fixture_beta.BetaDto(6), fixture_beta.BetaDto(7), fixture_beta.BetaDto(8), @@ -513,10 +520,7 @@ void main() { }); test('gama', () { - const dto = { - fixture_gama.GamaDto(9), - fixture_gama.GamaDto(10), - }; + const dto = {fixture_gama.GamaDto(9), fixture_gama.GamaDto(10)}; final converted = mappr.convertSet(dto); expect(converted, equals({const fixture_gama.Gama(9), const fixture_gama.Gama(10)})); diff --git a/packages/auto_mappr/test/integration/fixture/delegates/module_beta.dart b/packages/auto_mappr/test/integration/fixture/delegates/module_beta.dart index 1cd48ac3..fbf7a1e7 100644 --- a/packages/auto_mappr/test/integration/fixture/delegates/module_beta.dart +++ b/packages/auto_mappr/test/integration/fixture/delegates/module_beta.dart @@ -10,7 +10,7 @@ class MapprBeta extends $MapprBeta { } class BetaDto { - final int value; + final int? value; const BetaDto(this.value); } diff --git a/packages/auto_mappr/test/integration/fixture/delegates/module_gama.dart b/packages/auto_mappr/test/integration/fixture/delegates/module_gama.dart index e00e7f7a..c5e4b38f 100644 --- a/packages/auto_mappr/test/integration/fixture/delegates/module_gama.dart +++ b/packages/auto_mappr/test/integration/fixture/delegates/module_gama.dart @@ -3,13 +3,13 @@ import 'package:equatable/equatable.dart'; import 'module_gama.auto_mappr.dart'; -@AutoMappr([MapType()]) +@AutoMappr([MapType(safeMapping: true)]) class MapprGama extends $MapprGama { const MapprGama(); } class GamaDto { - final int value; + final int? value; const GamaDto(this.value); } diff --git a/packages/auto_mappr/test/integration/fixture/includes/module_beta.dart b/packages/auto_mappr/test/integration/fixture/includes/module_beta.dart index 1f00211f..428506bf 100644 --- a/packages/auto_mappr/test/integration/fixture/includes/module_beta.dart +++ b/packages/auto_mappr/test/integration/fixture/includes/module_beta.dart @@ -10,7 +10,7 @@ class MapprBeta extends $MapprBeta { } class BetaDto { - final int value; + final int? value; const BetaDto(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 index e00e7f7a..c5e4b38f 100644 --- a/packages/auto_mappr/test/integration/fixture/includes/module_gama.dart +++ b/packages/auto_mappr/test/integration/fixture/includes/module_gama.dart @@ -3,13 +3,13 @@ import 'package:equatable/equatable.dart'; import 'module_gama.auto_mappr.dart'; -@AutoMappr([MapType()]) +@AutoMappr([MapType(safeMapping: true)]) class MapprGama extends $MapprGama { const MapprGama(); } class GamaDto { - final int value; + final int? value; const GamaDto(this.value); } diff --git a/packages/auto_mappr/test/integration/fixture/try_convert.dart b/packages/auto_mappr/test/integration/fixture/try_convert.dart index 54cf9e81..b922715b 100644 --- a/packages/auto_mappr/test/integration/fixture/try_convert.dart +++ b/packages/auto_mappr/test/integration/fixture/try_convert.dart @@ -7,13 +7,32 @@ import 'try_convert.auto_mappr.dart'; MapType(), // value MapType( - whenSourceIsNull: ComplexValue(99, Nested(id: 123, name: 'test qwerty')), + safeMapping: true, + whenSourceIsNull: ComplexValue(99, Nested(id: 123, name: 'test qwerty'), 'male'), ), + MapType(ignoreFieldNull: true), ]) class Mappr extends $Mappr { const Mappr(); } +// Other object + +class Example extends Equatable { + final int id; + + @override + List get props => [id]; + + const Example(this.id); +} + +class ExampleDto { + final int? id; + + const ExampleDto({this.id}); +} + // Nested object class Nested extends Equatable { @@ -39,17 +58,19 @@ class NestedDto { class ComplexValue extends Equatable { final int age; + final String gender; final Nested name; @override - List get props => [age, name]; + List get props => [age, name, gender]; - const ComplexValue(this.age, this.name); + const ComplexValue(this.age, this.name, this.gender); } class ComplexValueDto { final int age; final NestedDto? name; + final String? gender; - const ComplexValueDto(this.age, this.name); + const ComplexValueDto(this.age, this.name, this.gender); } diff --git a/packages/auto_mappr/test/integration/includes_test.dart b/packages/auto_mappr/test/integration/includes_test.dart index 73420bb1..d33628e8 100644 --- a/packages/auto_mappr/test/integration/includes_test.dart +++ b/packages/auto_mappr/test/integration/includes_test.dart @@ -20,7 +20,11 @@ void main() { group('convert', () { test('group', () { - const dto = fixture_group.GroupDto(fixture_alpha.AlphaDto(1), fixture_beta.BetaDto(2), fixture_gama.GamaDto(3)); + const dto = fixture_group.GroupDto( + fixture_alpha.AlphaDto(1), + fixture_beta.BetaDto(2), + fixture_gama.GamaDto(3), + ); final converted = mappr.convert(dto); expect( @@ -97,6 +101,15 @@ void main() { expect(converted, isNull); }); + test('beta w/ null value', () { + const dto = fixture_beta.BetaDto(null); + + expect( + () => mappr.tryConvert(dto), + throwsA(predicate((e) => e is TypeError)), + ); + }); + test('gama', () { const dto = fixture_gama.GamaDto(4); final converted = mappr.tryConvert(dto); @@ -110,6 +123,13 @@ void main() { expect(converted, isNull); }); + + test('gama w/ null value', () { + const dto = fixture_gama.GamaDto(null); + final converted = mappr.tryConvert(dto); + + expect(converted, isNull); + }); }); group('convertIterable', () { @@ -296,7 +316,7 @@ void main() { group('convertList', () { test('group', () { - const dto = [ + 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)), @@ -314,10 +334,7 @@ void main() { }); test('alpha', () { - const dto = [ - fixture_alpha.AlphaDto(4), - fixture_alpha.AlphaDto(5), - ]; + const dto = [fixture_alpha.AlphaDto(4), fixture_alpha.AlphaDto(5)]; final converted = mappr.convertList(dto); expect( @@ -327,7 +344,7 @@ void main() { }); test('beta', () { - const dto = [ + const dto = [ fixture_beta.BetaDto(6), fixture_beta.BetaDto(7), fixture_beta.BetaDto(8), @@ -345,10 +362,7 @@ void main() { }); test('gama', () { - const dto = [ - fixture_gama.GamaDto(9), - fixture_gama.GamaDto(10), - ]; + 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)])); @@ -478,7 +492,7 @@ void main() { group('convertSet', () { test('group', () { - const dto = { + 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)), @@ -496,10 +510,7 @@ void main() { }); test('alpha', () { - const dto = { - fixture_alpha.AlphaDto(4), - fixture_alpha.AlphaDto(5), - }; + const dto = {fixture_alpha.AlphaDto(4), fixture_alpha.AlphaDto(5)}; final converted = mappr.convertSet(dto); expect( @@ -509,7 +520,7 @@ void main() { }); test('beta', () { - const dto = { + const dto = { fixture_beta.BetaDto(6), fixture_beta.BetaDto(7), fixture_beta.BetaDto(8), @@ -527,10 +538,7 @@ void main() { }); test('gama', () { - const dto = { - fixture_gama.GamaDto(9), - fixture_gama.GamaDto(10), - }; + const dto = {fixture_gama.GamaDto(9), fixture_gama.GamaDto(10)}; final converted = mappr.convertSet(dto); expect(converted, equals({const fixture_gama.Gama(9), const fixture_gama.Gama(10)})); diff --git a/packages/auto_mappr/test/integration/try_convert_test.dart b/packages/auto_mappr/test/integration/try_convert_test.dart index 91fec872..c373c549 100644 --- a/packages/auto_mappr/test/integration/try_convert_test.dart +++ b/packages/auto_mappr/test/integration/try_convert_test.dart @@ -1,12 +1,24 @@ +import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; import 'fixture/try_convert.dart' as fixture; +class _LogService { + const _LogService(); + + // ignore: no-empty-block, for testing purpose only, avoid-unused-parameters + void log(Object error, T? source) {} +} + +class _MockLogService extends Mock implements _LogService {} + void main() { late final fixture.Mappr mappr; + final logService = _MockLogService(); setUpAll(() { mappr = const fixture.Mappr(); + reset(logService); }); group('convert', () { @@ -17,7 +29,7 @@ void main() { test('Returns default when configured', () { expect( mappr.convert(null), - equals(const fixture.ComplexValue(99, fixture.Nested(id: 123, name: 'test qwerty'))), + equals(const fixture.ComplexValue(99, fixture.Nested(id: 123, name: 'test qwerty'), 'male')), ); }); }); @@ -30,7 +42,29 @@ void main() { test('Returns default when configured', () { expect( mappr.tryConvert(null), - equals(const fixture.ComplexValue(99, fixture.Nested(id: 123, name: 'test qwerty'))), + equals(const fixture.ComplexValue(99, fixture.Nested(id: 123, name: 'test qwerty'), 'male')), + ); + }); + + test('Source gender is null with safe mapping', () { + const dto = fixture.ComplexValueDto(99, fixture.NestedDto(1, name: 'Joe'), null); + + expect( + mappr.tryConvert( + dto, + onMappingError: (error, stacktrace, source) => logService.log(error, source), + ), + isNull, + ); + + final captured = verify(() => logService.log(any(), captureAny()))..called(1); + expect(captured.captured.firstOrNull, equals(dto)); + }); + + test('Source gender is null', () { + expect( + () => mappr.tryConvert(const fixture.ExampleDto()), + throwsA(predicate((e) => e is TypeError)), ); }); }); diff --git a/packages/auto_mappr_annotation/CHANGELOG.md b/packages/auto_mappr_annotation/CHANGELOG.md index 04940a44..66c92c70 100644 --- a/packages/auto_mappr_annotation/CHANGELOG.md +++ b/packages/auto_mappr_annotation/CHANGELOG.md @@ -1,5 +1,7 @@ [//]: # (## Unreleased) +## 2.2.0 +- Add `safeMapping` option to `MapType`. [#204](https://github.com/netglade/auto_mappr/pull/216) ## 2.1.0 - Allow `TypeConverter` to have ``. [#130](https://github.com/netglade/auto_mappr/pull/130) 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 eef65470..1f675566 100644 --- a/packages/auto_mappr_annotation/lib/src/auto_mappr_interface.dart +++ b/packages/auto_mappr_annotation/lib/src/auto_mappr_interface.dart @@ -21,8 +21,10 @@ abstract interface class AutoMapprInterface { /// Converts from SOURCE to TARGET if such mapping is configured. /// /// When source model is null, returns `whenSourceIsNull` if defined or null. + /// + /// If safeMapping is set to true and any exception is thrown during the mapping, the onMappingError callback is invoked. /// {@endtemplate} - TARGET? tryConvert(SOURCE? model); + TARGET? tryConvert(SOURCE? model, {OnMappingError? onMappingError}); /// {@template AutoMapprInterface:convertIterable} /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Iterable. @@ -35,8 +37,13 @@ abstract interface class AutoMapprInterface { /// 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. + /// + /// If safeMapping is set to true and any exception is thrown during the mapping, the onMappingError callback is invoked. /// {@endtemplate} - Iterable tryConvertIterable(Iterable model); + Iterable tryConvertIterable( + Iterable model, { + OnMappingError? onMappingError, + }); /// {@template AutoMapprInterface:convertList} /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into List. @@ -49,8 +56,10 @@ abstract interface class AutoMapprInterface { /// 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. + /// + /// If safeMapping is set to true and any exception is thrown during the mapping, the onMappingError callback is invoked. /// {@endtemplate} - List tryConvertList(Iterable model); + List tryConvertList(Iterable model, {OnMappingError? onMappingError}); /// {@template AutoMapprInterface:convertSet} /// For iterable items, converts from SOURCE to TARGET if such mapping is configured, into Set. @@ -63,6 +72,16 @@ abstract interface class AutoMapprInterface { /// 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. + /// + /// If safeMapping is set to true and any exception is thrown during the mapping, the onMappingError callback is invoked. /// {@endtemplate} - Set tryConvertSet(Iterable model); + Set tryConvertSet(Iterable model, {OnMappingError? onMappingError}); + + /// {@template AutoMapprInterface:useSafeMapping} + /// Determines if safe mapping is used between the SOURCE and TARGET. + /// {@endtemplate} + bool useSafeMapping(); } + +/// Callback invoked when error in mapping. +typedef OnMappingError = void Function(Object e, StackTrace stackTrace, SOURCE? source); diff --git a/packages/auto_mappr_annotation/lib/src/map_type.dart b/packages/auto_mappr_annotation/lib/src/map_type.dart index cd0d295f..68e13f12 100644 --- a/packages/auto_mappr_annotation/lib/src/map_type.dart +++ b/packages/auto_mappr_annotation/lib/src/map_type.dart @@ -36,6 +36,10 @@ final class MapType { /// such as [whenSourceIsNull] or [constructor] is used. final bool reverse; + /// If set to true and any exception is thrown during the mapping using tryConvert/tryConvertIterable/tryConvertSet/tryConverlList methods, it is caught and methods return null. + /// The default is false. + final bool safeMapping; + /// Constructs mapping between [SOURCE] and [TARGET] types. const MapType({ this.fields = const [], @@ -44,5 +48,6 @@ final class MapType { this.constructor, this.ignoreFieldNull, this.reverse = false, + this.safeMapping = false, }); } diff --git a/packages/auto_mappr_annotation/pubspec.yaml b/packages/auto_mappr_annotation/pubspec.yaml index 39f1c854..b8a6c422 100644 --- a/packages/auto_mappr_annotation/pubspec.yaml +++ b/packages/auto_mappr_annotation/pubspec.yaml @@ -1,6 +1,6 @@ name: auto_mappr_annotation description: Annotations for the auto_mappr code-generator of mapping between objects with ease. -version: 2.1.0 +version: 2.2.0 repository: https://github.com/netglade/auto_mappr issue_tracker: https://github.com/netglade/auto_mappr/issues