diff --git a/moor/test/data/tables/custom_tables.g.dart b/moor/test/data/tables/custom_tables.g.dart index a1538148..2bddbf68 100644 --- a/moor/test/data/tables/custom_tables.g.dart +++ b/moor/test/data/tables/custom_tables.g.dart @@ -289,8 +289,8 @@ class ConfigTable extends Table with TableInfo { return ConfigTable(_db, alias); } - static TypeConverter $converter0 = const SyncTypeConverter(); - static TypeConverter $converter1 = + static TypeConverter $converter0 = const SyncTypeConverter(); + static TypeConverter $converter1 = const EnumIndexConverter(SyncType.values); @override bool get dontWriteConstraints => true; @@ -1614,7 +1614,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { readsFrom: {config}).map(config.mapFromRow); } - Selectable typeConverterVar(SyncType? var1, List var2) { + Selectable typeConverterVar(SyncType var1, List var2) { var $arrayStartIndex = 2; final expandedvar2 = $expandVar($arrayStartIndex, var2.length); $arrayStartIndex += var2.length; @@ -1623,7 +1623,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { variables: [ Variable(ConfigTable.$converter0.mapToSql(var1)!), for (var $ in var2) - Variable(ConfigTable.$converter1.mapToSql($)!) + Variable(ConfigTable.$converter1.mapToSql($)!) ], readsFrom: { config diff --git a/moor/test/data/tables/todos.g.dart b/moor/test/data/tables/todos.g.dart index c0a8d705..73bfb972 100644 --- a/moor/test/data/tables/todos.g.dart +++ b/moor/test/data/tables/todos.g.dart @@ -558,7 +558,7 @@ class $CategoriesTable extends Categories return $CategoriesTable(_db, alias); } - static TypeConverter $converter0 = + static TypeConverter $converter0 = const EnumIndexConverter(CategoryPriority.values); } @@ -1316,7 +1316,7 @@ class $TableWithoutPKTable extends TableWithoutPK return $TableWithoutPKTable(_db, alias); } - static TypeConverter $converter0 = + static TypeConverter $converter0 = const CustomConverter(); } diff --git a/moor_generator/lib/src/analyzer/moor/create_table_reader.dart b/moor_generator/lib/src/analyzer/moor/create_table_reader.dart index 1368679a..ee5f1a98 100644 --- a/moor_generator/lib/src/analyzer/moor/create_table_reader.dart +++ b/moor_generator/lib/src/analyzer/moor/create_table_reader.dart @@ -11,6 +11,7 @@ import 'package:moor_generator/src/model/used_type_converter.dart'; import 'package:moor_generator/src/utils/names.dart'; import 'package:moor_generator/src/utils/string_escaper.dart'; import 'package:moor_generator/src/utils/type_converter_hint.dart'; +import 'package:moor_generator/src/utils/type_utils.dart'; import 'package:recase/recase.dart'; import 'package:sqlparser/sqlparser.dart'; @@ -217,8 +218,11 @@ class CreateTableReader { } final interfaceType = type as InterfaceType; - // TypeConverter declares a "D mapToDart(S fromDb);". We need to know D - final typeInDart = interfaceType.getMethod('mapToDart').returnType; + final asTypeConverter = interfaceType.allSupertypes.firstWhere( + (type) => isFromMoor(type) && type.element.name == 'TypeConverter'); + + // TypeConverter, where D is the type in Dart + final typeInDart = asTypeConverter.typeArguments.first; return UsedTypeConverter( expression: code, mappedType: typeInDart, sqlType: sqlType); diff --git a/moor_generator/lib/src/model/types.dart b/moor_generator/lib/src/model/types.dart index f4624a87..302c30f7 100644 --- a/moor_generator/lib/src/model/types.dart +++ b/moor_generator/lib/src/model/types.dart @@ -17,6 +17,11 @@ abstract class HasType { } extension OperationOnTypes on HasType { + /// Whether this type is nullable in Dart + bool get nullableInDart { + return nullable || typeConverter?.hasNullableDartType == true; + } + /// the Dart type of this column that can be handled by moors type mapping. /// Basically the same as [dartTypeCode], minus custom types and nullability. String get variableTypeName => dartTypeNames[type]; @@ -30,7 +35,7 @@ extension OperationOnTypes on HasType { /// This is the same as [dartTypeCode] but without custom types. String variableTypeCode( [GenerationOptions options = const GenerationOptions()]) { - final hasSuffix = nullable && options.nnbd; + final hasSuffix = nullableInDart && options.nnbd; return hasSuffix ? '$variableTypeName?' : variableTypeName; } @@ -39,7 +44,10 @@ extension OperationOnTypes on HasType { /// [int]. String dartTypeCode([GenerationOptions options = const GenerationOptions()]) { if (typeConverter != null) { - return typeConverter.mappedType?.codeString(options); + final needsSuffix = nullable && !typeConverter.hasNullableDartType; + final baseType = typeConverter.mappedType.codeString(options); + + return needsSuffix ? '$baseType?' : baseType; } return variableTypeCode(options); diff --git a/moor_generator/lib/src/model/used_type_converter.dart b/moor_generator/lib/src/model/used_type_converter.dart index ec81e277..72707cef 100644 --- a/moor_generator/lib/src/model/used_type_converter.dart +++ b/moor_generator/lib/src/model/used_type_converter.dart @@ -37,6 +37,9 @@ class UsedTypeConverter { @required this.sqlType, }); + bool get hasNullableDartType => + mappedType.nullabilitySuffix == NullabilitySuffix.question; + factory UsedTypeConverter.forEnumColumn(DartType enumType, bool nullable) { if (enumType.element is! ClassElement) { throw InvalidTypeForEnumConverterException('Not a class', enumType); @@ -61,7 +64,7 @@ class UsedTypeConverter { /// A suitable typename to store an instance of the type converter used here. String converterNameInCode(GenerationOptions options) { - final sqlDartType = options.nullableType(dartTypeNames[sqlType]); + final sqlDartType = dartTypeNames[sqlType]; return 'TypeConverter<${mappedType.codeString(options)}, $sqlDartType>'; } } diff --git a/moor_generator/lib/src/writer/tables/data_class_writer.dart b/moor_generator/lib/src/writer/tables/data_class_writer.dart index 10286e41..b4108284 100644 --- a/moor_generator/lib/src/writer/tables/data_class_writer.dart +++ b/moor_generator/lib/src/writer/tables/data_class_writer.dart @@ -176,15 +176,17 @@ class DataClassWriter { for (var i = 0; i < table.columns.length; i++) { final column = table.columns[i]; final last = i == table.columns.length - 1; + final isNullable = column.nullableInDart; final typeName = column.dartTypeCode(scope.generationOptions); - if (wrapNullableInValue && column.nullable) { + if (wrapNullableInValue && isNullable) { _buffer ..write('Value<$typeName> ${column.dartGetterName} ') ..write('= const Value.absent()'); - } else if (!column.nullable && scope.generationOptions.nnbd) { - // We always write nullable types in the copyWith constructor, since all - // parameters are optional. + } else if (!isNullable && scope.generationOptions.nnbd) { + // We always use nullable parameters in copyWith, since all parameters + // are optional. The !isNullable check is there to avoid a duplicate + // question mark in the type name. _buffer.write('$typeName? ${column.dartGetterName}'); } else { _buffer.write('$typeName ${column.dartGetterName}');