From 459ec0cf4bbb379672fdfc8854566dbd0318c600 Mon Sep 17 00:00:00 2001 From: Alexander Wilde Date: Thu, 29 Dec 2022 13:48:40 +0000 Subject: [PATCH] Add writeToColumnsMixin --- drift_dev/lib/src/analysis/options.dart | 5 ++ .../lib/src/generated/analysis/options.g.dart | 7 +- .../tables/update_companion_writer.dart | 81 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/drift_dev/lib/src/analysis/options.dart b/drift_dev/lib/src/analysis/options.dart index 876781b7..e85c9c38 100644 --- a/drift_dev/lib/src/analysis/options.dart +++ b/drift_dev/lib/src/analysis/options.dart @@ -94,6 +94,9 @@ class DriftOptions { @JsonKey(name: 'case_from_dart_to_sql', defaultValue: CaseFromDartToSql.snake) final CaseFromDartToSql caseFromDartToSql; + @JsonKey(name: 'write_to_columns_mixins', defaultValue: false) + final bool writeToColumnsMixins; + @internal const DriftOptions.defaults({ this.generateFromJsonStringConstructor = false, @@ -115,6 +118,7 @@ class DriftOptions { this.storeDateTimeValuesAsText = false, this.dialect = const DialectOptions(SqlDialect.sqlite, null), this.caseFromDartToSql = CaseFromDartToSql.snake, + this.writeToColumnsMixins = false, }); DriftOptions({ @@ -136,6 +140,7 @@ class DriftOptions { required this.sqliteAnalysisOptions, required this.storeDateTimeValuesAsText, required this.caseFromDartToSql, + required this.writeToColumnsMixins, this.dialect, }) { // ignore: deprecated_member_use_from_same_package diff --git a/drift_dev/lib/src/generated/analysis/options.g.dart b/drift_dev/lib/src/generated/analysis/options.g.dart index f90cf24c..c815e41a 100644 --- a/drift_dev/lib/src/generated/analysis/options.g.dart +++ b/drift_dev/lib/src/generated/analysis/options.g.dart @@ -31,7 +31,8 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate( 'named_parameters_always_required', 'scoped_dart_components', 'store_date_time_values_as_text', - 'case_from_dart_to_sql' + 'case_from_dart_to_sql', + 'write_to_columns_mixins' ], ); final val = DriftOptions( @@ -83,6 +84,8 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate( (v) => $enumDecodeNullable(_$CaseFromDartToSqlEnumMap, v) ?? CaseFromDartToSql.snake), + writeToColumnsMixins: $checkedConvert( + 'write_to_columns_mixins', (v) => v as bool? ?? false), dialect: $checkedConvert('sql', (v) => v == null ? null : DialectOptions.fromJson(v as Map)), ); @@ -110,6 +113,7 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate( 'sqliteAnalysisOptions': 'sqlite', 'storeDateTimeValuesAsText': 'store_date_time_values_as_text', 'caseFromDartToSql': 'case_from_dart_to_sql', + 'writeToColumnsMixins': 'write_to_columns_mixins', 'dialect': 'sql' }, ); @@ -142,6 +146,7 @@ Map _$DriftOptionsToJson(DriftOptions instance) => 'store_date_time_values_as_text': instance.storeDateTimeValuesAsText, 'case_from_dart_to_sql': _$CaseFromDartToSqlEnumMap[instance.caseFromDartToSql]!, + 'write_to_columns_mixins': instance.writeToColumnsMixins, }; const _$SqlModuleEnumMap = { diff --git a/drift_dev/lib/src/writer/tables/update_companion_writer.dart b/drift_dev/lib/src/writer/tables/update_companion_writer.dart index 4461d707..c34f3cab 100644 --- a/drift_dev/lib/src/writer/tables/update_companion_writer.dart +++ b/drift_dev/lib/src/writer/tables/update_companion_writer.dart @@ -46,6 +46,9 @@ class UpdateCompanionWriter { if (table.existingRowClass?.generateInsertable ?? false) { _writeToCompanionExtension(); } + if (scope.options.writeToColumnsMixins) { + _writeToColumnsMixin(); + } } void _writeFields() { @@ -263,4 +266,82 @@ class UpdateCompanionWriter { ..write('return $insertableClass(this);\n') ..write('}\n}\n'); } + + void _writeToColumnsMixin() { + final info = table.existingRowClass; + if (info == null) return; + + _buffer.write('mixin ${table.nameOfRowClass}Columns '); + + final type = _emitter.dartCode(_emitter.writer.rowType(table)); + _buffer.writeln('implements ${_emitter.drift('Insertable')}<$type> {'); + + for (final column in columns) { + if (column.documentationComment != null) { + _buffer.write('${column.documentationComment}\n'); + } + final typeName = _emitter.dartCode(_emitter.dartType(column)); + _buffer.writeln('$typeName get ${column.nameInDart};'); + } + + _writeTableToColumnsOverride(); + _buffer.write('}'); + } + + void _writeTableToColumnsOverride() { + final expression = _emitter.drift('Expression'); + final variable = _emitter.drift('Variable'); + + _buffer + ..write('@override\nMap toColumns' + '(bool nullToAbsent) {\n') + ..write('final map = {};'); + + for (final column in columns) { + // Generated column - cannot be used for inserts or updates + if (column.isGenerated) continue; + + // We include all columns that are not null. If nullToAbsent is false, we + // also include null columns. When generating NNBD code, we can include + // non-nullable columns without an additional null check since we know + // the values aren't going to be null. + final needsNullCheck = column.nullableInDart; + final needsScope = needsNullCheck || column.typeConverter != null; + if (needsNullCheck) { + _buffer.write('if (!nullToAbsent || ${column.nameInDart} != null)'); + } + if (needsScope) _buffer.write('{'); + + final typeName = + _emitter.dartCode(_emitter.variableTypeCode(column, nullable: false)); + final mapSetter = 'map[${asDartLiteral(column.nameInSql)}] = ' + '$variable<$typeName>'; + + if (column.typeConverter != null) { + // apply type converter before writing the variable + final converter = column.typeConverter!; + + _emitter + ..write('final converter = ') + ..writeDart(_emitter.writer + .readConverter(converter, forNullable: column.nullable)) + ..writeln(';') + ..write(mapSetter) + ..write('(converter.toSql(${column.nameInDart})'); + _buffer.write(');'); + } else { + // no type converter. Write variable directly + _buffer + ..write(mapSetter) + ..write('(') + ..write(column.nameInDart) + ..write(');'); + } + + // This one closes the optional if from before. + if (needsScope) _buffer.write('}'); + } + + _buffer.write('return map; \n}\n'); + } }