diff --git a/moor/lib/src/runtime/types/custom_type.dart b/moor/lib/src/runtime/types/custom_type.dart index df646817..c3abc56e 100644 --- a/moor/lib/src/runtime/types/custom_type.dart +++ b/moor/lib/src/runtime/types/custom_type.dart @@ -39,3 +39,34 @@ class EnumIndexConverter extends TypeConverter { return (value as dynamic)?.index as int; } } + +/// A type converter automatically mapping `null` values to `null` in both +/// directions. +/// +/// Instead of overriding [mapToDart] and [mapToSql], subclasses of this +/// converter should implement [requireMapToDart] and [requireMapToSql], which +/// are used to map non-null values to and from sql values, respectively. +/// +/// Apart from the implementation changes, subclasses of this converter can be +/// used just like all other type converters. +abstract class NullAwareTypeConverter extends TypeConverter { + /// Constant default constructor. + const NullAwareTypeConverter(); + + @override + D? mapToDart(S? fromDb) { + return fromDb == null ? null : requireMapToDart(fromDb); + } + + /// Map a non-null value from an object in Dart into something that will be + /// understood by the database. + D requireMapToDart(S fromDb); + + @override + S? mapToSql(D? value) { + return value == null ? null : requireMapToSql(value); + } + + /// Maps a non-null column from the database back to Dart. + S requireMapToSql(D value); +} diff --git a/moor/test/data/tables/converter.dart b/moor/test/data/tables/converter.dart index bed86f60..4eb8d6f4 100644 --- a/moor/test/data/tables/converter.dart +++ b/moor/test/data/tables/converter.dart @@ -21,3 +21,21 @@ class SyncTypeConverter extends TypeConverter { return value?.index; } } + +class NullAwareSyncTypeConverter extends NullAwareTypeConverter { + const NullAwareSyncTypeConverter(); + + @override + SyncType requireMapToDart(int fromDb) { + const values = SyncType.values; + if (fromDb < 0 || fromDb >= values.length) { + return SyncType.locallyCreated; + } + return values[fromDb]; + } + + @override + int requireMapToSql(SyncType value) { + return value.index; + } +} diff --git a/moor/test/types/non_nullable_type_converter_test.dart b/moor/test/types/non_nullable_type_converter_test.dart new file mode 100644 index 00000000..bb60a898 --- /dev/null +++ b/moor/test/types/non_nullable_type_converter_test.dart @@ -0,0 +1,25 @@ +import 'package:test/test.dart'; + +import '../data/tables/converter.dart'; + +void main() { + test('test null in null aware type converters', () { + const typeConverter = NullAwareSyncTypeConverter(); + expect(typeConverter.mapToDart(typeConverter.mapToSql(null)), null); + expect(typeConverter.mapToSql(typeConverter.mapToDart(null)), null); + }); + + test('test value in null aware type converters', () { + const typeConverter = NullAwareSyncTypeConverter(); + const value = SyncType.synchronized; + expect(typeConverter.mapToDart(typeConverter.mapToSql(value)), value); + expect(typeConverter.mapToSql(typeConverter.mapToDart(value.index)), + value.index); + }); + + test('test invalid value in null aware type converters', () { + const typeConverter = NullAwareSyncTypeConverter(); + const defaultValue = SyncType.locallyCreated; + expect(typeConverter.mapToDart(-1), defaultValue); + }); +}