From ca96855c8c6afa6ff4bad8ae637292cf608d588c Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Wed, 8 May 2024 16:15:29 +0200 Subject: [PATCH 01/20] Made the embed annotations --- floor_annotation/lib/floor_annotation.dart | 1 + floor_annotation/lib/src/database.dart | 4 ++++ floor_annotation/lib/src/embed.dart | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 floor_annotation/lib/src/embed.dart diff --git a/floor_annotation/lib/floor_annotation.dart b/floor_annotation/lib/floor_annotation.dart index c97fef74..90e23134 100644 --- a/floor_annotation/lib/floor_annotation.dart +++ b/floor_annotation/lib/floor_annotation.dart @@ -5,6 +5,7 @@ export 'src/dao.dart'; export 'src/database.dart'; export 'src/database_view.dart'; export 'src/delete.dart'; +export 'src/embed.dart'; export 'src/entity.dart'; export 'src/foreign_key.dart'; export 'src/fts.dart'; diff --git a/floor_annotation/lib/src/database.dart b/floor_annotation/lib/src/database.dart index d3bf873e..ec07a22d 100644 --- a/floor_annotation/lib/src/database.dart +++ b/floor_annotation/lib/src/database.dart @@ -6,6 +6,9 @@ class Database { /// The entities the database manages. final List entities; + /// The embeds the database manages. + final List embeds; + /// The views the database manages. final List views; @@ -13,6 +16,7 @@ class Database { const Database({ required this.version, required this.entities, + this.embeds = const [], this.views = const [], }); } diff --git a/floor_annotation/lib/src/embed.dart b/floor_annotation/lib/src/embed.dart new file mode 100644 index 00000000..4f7268bf --- /dev/null +++ b/floor_annotation/lib/src/embed.dart @@ -0,0 +1,5 @@ +class Embed { + const Embed(); +} + +const embed = Embed(); \ No newline at end of file From 248b7a6cec8142586bd1da2e714a381ed7ba508f Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Wed, 8 May 2024 16:25:31 +0200 Subject: [PATCH 02/20] Add new embed class to generator --- .../lib/misc/extension/embeds_extension.dart | 35 +++++++++++++++++++ .../extension/field_element_extension.dart | 14 ++++++++ .../lib/processor/embed_processor.dart | 32 +++++++++++++++++ floor_generator/lib/value_object/embed.dart | 25 +++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 floor_generator/lib/misc/extension/embeds_extension.dart create mode 100644 floor_generator/lib/misc/extension/field_element_extension.dart create mode 100644 floor_generator/lib/processor/embed_processor.dart create mode 100644 floor_generator/lib/value_object/embed.dart diff --git a/floor_generator/lib/misc/extension/embeds_extension.dart b/floor_generator/lib/misc/extension/embeds_extension.dart new file mode 100644 index 00000000..0734f47a --- /dev/null +++ b/floor_generator/lib/misc/extension/embeds_extension.dart @@ -0,0 +1,35 @@ +import 'package:analyzer/dart/element/type.dart'; +import 'package:collection/collection.dart'; +import 'package:floor_generator/misc/extension/iterable_extension.dart'; +import 'package:floor_generator/value_object/embed.dart'; +import 'package:floor_generator/value_object/type_converter.dart'; +import 'package:source_gen/source_gen.dart'; + +extension EmbedsExtension on Iterable { + // /// Returns the [Embed] in the closest [TypeConverterScope] or null + // Embed? get closestOrNull { + // return sortedByDescending((embed) => embed.scope.index) + // .firstOrNull; + // } + + /// Returns the [Embed] in the closest [TypeConverterScope] for + /// [dartType] or null + Embed? getClosestOrNull(DartType dartType) { + return toList() + .firstWhereOrNull( + (embed) => embed.classElement.name == dartType.toString()); + } + +// /// Returns the [Embed] in the closest [TypeConverterScope] for +// /// [dartType] +// Embed getClosest(DartType dartType) { +// final closest = getClosestOrNull(dartType); +// if (closest == null) { +// throw InvalidGenerationSourceError( +// 'Column type is not supported for $dartType', +// todo: 'Either use a supported type or supply a type converter.', +// ); +// } +// return closest; +// } +} \ No newline at end of file diff --git a/floor_generator/lib/misc/extension/field_element_extension.dart b/floor_generator/lib/misc/extension/field_element_extension.dart new file mode 100644 index 00000000..88b5bdeb --- /dev/null +++ b/floor_generator/lib/misc/extension/field_element_extension.dart @@ -0,0 +1,14 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:floor_annotation/floor_annotation.dart' as annotations; +import 'package:floor_generator/misc/type_utils.dart'; + +extension FieldElementExtension on FieldElement { + bool shouldBeIncluded() { + final isIgnored = hasAnnotation(annotations.ignore.runtimeType); + return !(isStatic || isSynthetic || isIgnored); + } + + bool get isEmbedded { + return (type.element?.hasAnnotation(annotations.Embed) ?? false) && type.element is ClassElement; + } +} \ No newline at end of file diff --git a/floor_generator/lib/processor/embed_processor.dart b/floor_generator/lib/processor/embed_processor.dart new file mode 100644 index 00000000..4ae8bf1f --- /dev/null +++ b/floor_generator/lib/processor/embed_processor.dart @@ -0,0 +1,32 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:floor_generator/misc/extension/field_element_extension.dart'; +import 'package:floor_generator/misc/extension/type_converters_extension.dart'; +import 'package:floor_generator/processor/field_processor.dart'; +import 'package:floor_generator/processor/processor.dart'; +import 'package:floor_generator/value_object/embed.dart'; +import 'package:floor_generator/value_object/field.dart'; +import 'package:floor_generator/value_object/type_converter.dart'; + +class EmbedProcessor extends Processor { + final ClassElement _classElement; + Set typeConverters; + + EmbedProcessor(this._classElement, this.typeConverters); + + @override + Embed process() { + return Embed( + _classElement, + _getFields(), + ); + } + + List _getFields() { + final fields = _classElement.fields + .where((fieldElement) => fieldElement.shouldBeIncluded()) + .map((field) => FieldProcessor(field, typeConverters.getClosestOrNull(field.type)).process()) + .toList(); + + return fields; + } +} \ No newline at end of file diff --git a/floor_generator/lib/value_object/embed.dart b/floor_generator/lib/value_object/embed.dart new file mode 100644 index 00000000..6db3bf51 --- /dev/null +++ b/floor_generator/lib/value_object/embed.dart @@ -0,0 +1,25 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:collection/collection.dart'; +import 'package:floor_generator/value_object/field.dart'; + +class Embed { + final ClassElement classElement; + final List fields; + + Embed(this.classElement, this.fields); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Embed && + runtimeType == other.runtimeType && + const ListEquality().equals(fields, other.fields); + + @override + int get hashCode => classElement.hashCode ^ fields.hashCode; + + @override + String toString() { + return 'Embed{classElement: $classElement, fields: $fields}'; + } +} \ No newline at end of file From 804bae1f45b3526a91cfa9442244316c5ae88602 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Wed, 15 May 2024 11:41:45 +0200 Subject: [PATCH 03/20] Embedded supports works, only taskstatus gives error when generating .g file --- example/lib/database.dart | 3 +- example/lib/main.dart | 11 ++- example/lib/task.dart | 9 +- example/lib/timestamp.dart | 28 ++++++ example/pubspec.lock | 2 +- floor_generator/lib/misc/constants.dart | 1 + .../lib/misc/extension/embeds_extension.dart | 19 ---- .../lib/processor/database_processor.dart | 41 ++++++++- .../lib/processor/embed_processor.dart | 2 +- .../lib/processor/entity_processor.dart | 44 ++++++++-- .../lib/processor/field_processor.dart | 20 +++-- .../lib/processor/queryable_processor.dart | 88 +++++++++---------- .../lib/processor/view_processor.dart | 5 +- floor_generator/lib/value_object/entity.dart | 14 ++- floor_generator/lib/value_object/field.dart | 22 +++++ .../test/processor/dao_processor_test.dart | 4 +- .../test/processor/entity_processor_test.dart | 58 ++++++------ .../test/processor/field_processor_test.dart | 20 +++-- .../query_method_processor_test.dart | 4 +- .../processor/queryable_processor_test.dart | 16 ++-- .../test/processor/view_processor_test.dart | 22 ++--- floor_generator/test/test_utils.dart | 6 +- .../test/value_object/entity_test.dart | 2 + .../test/value_object/field_test.dart | 2 + .../test/value_object/view_test.dart | 2 + .../test/writer/dao_writer_test.dart | 4 +- .../test/writer/query_method_writer_test.dart | 6 +- 27 files changed, 293 insertions(+), 162 deletions(-) create mode 100644 example/lib/timestamp.dart diff --git a/example/lib/database.dart b/example/lib/database.dart index 037973f8..5c7d6aa5 100644 --- a/example/lib/database.dart +++ b/example/lib/database.dart @@ -2,14 +2,15 @@ import 'dart:async'; import 'package:example/task.dart'; import 'package:example/task_dao.dart'; +import 'package:example/timestamp.dart'; import 'package:example/type_converter.dart'; import 'package:floor/floor.dart'; import 'package:sqflite/sqflite.dart' as sqflite; part 'database.g.dart'; -@Database(version: 1, entities: [Task]) @TypeConverters([DateTimeConverter, TaskTypeConverter]) +@Database(version: 1, entities: [Task], embeds: [Timestamp]) abstract class FlutterDatabase extends FloorDatabase { TaskDao get taskDao; } diff --git a/example/lib/main.dart b/example/lib/main.dart index b0994452..e00c695d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,7 @@ import 'package:example/database.dart'; import 'package:example/task.dart'; import 'package:example/task_dao.dart'; +import 'package:example/timestamp.dart'; import 'package:flutter/material.dart'; Future main() async { @@ -179,7 +180,7 @@ class TaskListCell extends StatelessWidget { child: ListTile( title: Text(task.message), subtitle: Text('Status: ${task.statusTitle}'), - trailing: Text(task.timestamp.toIso8601String()), + trailing: Text("task.timestamp.toIso8601String()"), ), confirmDismiss: (direction) async { String? statusMessage; @@ -263,7 +264,13 @@ class TasksTextField extends StatelessWidget { if (message.trim().isEmpty) { _textEditingController.clear(); } else { - final task = Task.optional(message: message, type: TaskType.task); + final task = Task.optional( + message: message, + type: TaskType.task, + timestamp: Timestamp( + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + )); await dao.insertTask(task); _textEditingController.clear(); } diff --git a/example/lib/task.dart b/example/lib/task.dart index 3a7240e9..c66741a2 100644 --- a/example/lib/task.dart +++ b/example/lib/task.dart @@ -1,3 +1,4 @@ +import 'package:example/timestamp.dart'; import 'package:floor/floor.dart'; enum TaskStatus { @@ -29,7 +30,7 @@ class Task { final bool? isRead; - final DateTime timestamp; + final Timestamp timestamp; final TaskStatus? status; @@ -46,7 +47,7 @@ class Task { factory Task.optional({ int? id, - DateTime? timestamp, + Timestamp? timestamp, String? message, bool? isRead, TaskStatus? status, @@ -56,7 +57,7 @@ class Task { id, isRead ?? false, message ?? 'empty', - timestamp ?? DateTime.now(), + timestamp!, status, type, ); @@ -70,7 +71,7 @@ class Task { int? id, String? message, bool? isRead, - DateTime? timestamp, + Timestamp? timestamp, TaskStatus? status, TaskType? type, }) { diff --git a/example/lib/timestamp.dart b/example/lib/timestamp.dart new file mode 100644 index 00000000..a60b81b7 --- /dev/null +++ b/example/lib/timestamp.dart @@ -0,0 +1,28 @@ +import 'package:floor/floor.dart'; + +@Embed() +class Timestamp { + @ColumnInfo(name: 'created_at') + final DateTime createdAt; + + @ColumnInfo(name: 'updated_at') + final DateTime updatedAt; + + Timestamp({required this.createdAt, required this.updatedAt}); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Timestamp && + runtimeType == other.runtimeType && + createdAt == other.createdAt && + updatedAt == other.updatedAt; + + @override + int get hashCode => createdAt.hashCode ^ updatedAt.hashCode; + + @override + String toString() { + return 'Timestamp{createdAt: $createdAt, updatedAt: $updatedAt}'; + } +} \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 841a53a7..d6d6de9f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -223,7 +223,7 @@ packages: path: "../floor" relative: true source: path - version: "1.4.2" + version: "1.5.0" floor_annotation: dependency: transitive description: diff --git a/floor_generator/lib/misc/constants.dart b/floor_generator/lib/misc/constants.dart index 689c1f7e..71c50a7e 100644 --- a/floor_generator/lib/misc/constants.dart +++ b/floor_generator/lib/misc/constants.dart @@ -4,6 +4,7 @@ abstract class AnnotationField { static const onConflict = 'onConflict'; static const databaseVersion = 'version'; + static const databaseEmbeds = 'embeds'; static const databaseEntities = 'entities'; static const databaseViews = 'views'; diff --git a/floor_generator/lib/misc/extension/embeds_extension.dart b/floor_generator/lib/misc/extension/embeds_extension.dart index 0734f47a..b19f64ef 100644 --- a/floor_generator/lib/misc/extension/embeds_extension.dart +++ b/floor_generator/lib/misc/extension/embeds_extension.dart @@ -6,12 +6,6 @@ import 'package:floor_generator/value_object/type_converter.dart'; import 'package:source_gen/source_gen.dart'; extension EmbedsExtension on Iterable { - // /// Returns the [Embed] in the closest [TypeConverterScope] or null - // Embed? get closestOrNull { - // return sortedByDescending((embed) => embed.scope.index) - // .firstOrNull; - // } - /// Returns the [Embed] in the closest [TypeConverterScope] for /// [dartType] or null Embed? getClosestOrNull(DartType dartType) { @@ -19,17 +13,4 @@ extension EmbedsExtension on Iterable { .firstWhereOrNull( (embed) => embed.classElement.name == dartType.toString()); } - -// /// Returns the [Embed] in the closest [TypeConverterScope] for -// /// [dartType] -// Embed getClosest(DartType dartType) { -// final closest = getClosestOrNull(dartType); -// if (closest == null) { -// throw InvalidGenerationSourceError( -// 'Column type is not supported for $dartType', -// todo: 'Either use a supported type or supply a type converter.', -// ); -// } -// return closest; -// } } \ No newline at end of file diff --git a/floor_generator/lib/processor/database_processor.dart b/floor_generator/lib/processor/database_processor.dart index f088d351..2caed01b 100644 --- a/floor_generator/lib/processor/database_processor.dart +++ b/floor_generator/lib/processor/database_processor.dart @@ -6,6 +6,7 @@ import 'package:floor_generator/misc/extension/set_extension.dart'; import 'package:floor_generator/misc/extension/type_converter_element_extension.dart'; import 'package:floor_generator/misc/type_utils.dart'; import 'package:floor_generator/processor/dao_processor.dart'; +import 'package:floor_generator/processor/embed_processor.dart'; import 'package:floor_generator/processor/entity_processor.dart'; import 'package:floor_generator/processor/error/database_processor_error.dart'; import 'package:floor_generator/processor/processor.dart'; @@ -13,6 +14,7 @@ import 'package:floor_generator/processor/view_processor.dart'; import 'package:floor_generator/value_object/dao_getter.dart'; import 'package:floor_generator/value_object/database.dart'; import 'package:floor_generator/value_object/entity.dart'; +import 'package:floor_generator/value_object/embed.dart'; import 'package:floor_generator/value_object/queryable.dart'; import 'package:floor_generator/value_object/type_converter.dart'; import 'package:floor_generator/value_object/view.dart'; @@ -31,8 +33,10 @@ class DatabaseProcessor extends Processor { final databaseName = _classElement.displayName; final databaseTypeConverters = _classElement.getTypeConverters(TypeConverterScope.database); - final entities = _getEntities(_classElement, databaseTypeConverters); - final views = _getViews(_classElement, databaseTypeConverters); + final embeds = _getEmbeds(_classElement, databaseTypeConverters); + final entities = + _getEntities(_classElement, databaseTypeConverters, embeds); + final views = _getViews(_classElement, databaseTypeConverters, embeds); final daoGetters = _getDaoGetters( databaseName, entities, @@ -102,9 +106,34 @@ class DatabaseProcessor extends Processor { classElement.isAbstract; } + Set _getEmbeds( + final ClassElement databaseClassElement, + final Set typeConverters, + ) { + final entities = _classElement + .getAnnotation(annotations.Database) + ?.getField(AnnotationField.databaseEmbeds) + ?.toListValue() + ?.mapNotNull((object) => object.toTypeValue()?.element) + .whereType() + .where(_isEmbed) + .map((classElement) => EmbedProcessor( + classElement, + typeConverters, + ).process()) + .toSet(); + + if (entities == null || entities.isEmpty) { + throw _processorError.noEntitiesDefined; + } + + return entities; + } + List _getEntities( final ClassElement databaseClassElement, final Set typeConverters, + final Set embedConverters, ) { final entities = _classElement .getAnnotation(annotations.Database) @@ -116,6 +145,7 @@ class DatabaseProcessor extends Processor { .map((classElement) => EntityProcessor( classElement, typeConverters, + embedConverters, ).process()) .toList(); @@ -129,6 +159,7 @@ class DatabaseProcessor extends Processor { List _getViews( final ClassElement databaseClassElement, final Set typeConverters, + final Set embedConverters, ) { return _classElement .getAnnotation(annotations.Database) @@ -140,6 +171,7 @@ class DatabaseProcessor extends Processor { .map((classElement) => ViewProcessor( classElement, typeConverters, + embedConverters, ).process()) .toList() ?? []; @@ -169,6 +201,11 @@ class DatabaseProcessor extends Processor { fieldTypeConverters; } + bool _isEmbed(final ClassElement classElement) { + return classElement.hasAnnotation(annotations.Embed) && + !classElement.isAbstract; + } + bool _isEntity(final ClassElement classElement) { return classElement.hasAnnotation(annotations.Entity) && !classElement.isAbstract; diff --git a/floor_generator/lib/processor/embed_processor.dart b/floor_generator/lib/processor/embed_processor.dart index 4ae8bf1f..cb4f5d7f 100644 --- a/floor_generator/lib/processor/embed_processor.dart +++ b/floor_generator/lib/processor/embed_processor.dart @@ -24,7 +24,7 @@ class EmbedProcessor extends Processor { List _getFields() { final fields = _classElement.fields .where((fieldElement) => fieldElement.shouldBeIncluded()) - .map((field) => FieldProcessor(field, typeConverters.getClosestOrNull(field.type)).process()) + .map((field) => FieldProcessor(field, typeConverters.getClosestOrNull(field.type), null).process()) .toList(); return fields; diff --git a/floor_generator/lib/processor/entity_processor.dart b/floor_generator/lib/processor/entity_processor.dart index 12e96142..059ab742 100644 --- a/floor_generator/lib/processor/entity_processor.dart +++ b/floor_generator/lib/processor/entity_processor.dart @@ -18,14 +18,17 @@ import 'package:floor_generator/value_object/index.dart'; import 'package:floor_generator/value_object/primary_key.dart'; import 'package:floor_generator/value_object/type_converter.dart'; +import '../value_object/embed.dart'; + class EntityProcessor extends QueryableProcessor { final EntityProcessorError _processorError; EntityProcessor( final ClassElement classElement, final Set typeConverters, + final Set embedConverters, ) : _processorError = EntityProcessorError(classElement), - super(classElement, typeConverters); + super(classElement, typeConverters, embedConverters); @override Entity process() { @@ -262,28 +265,51 @@ class EntityProcessor extends QueryableProcessor { false; } + void _processFields( + final Map map, + final List fields, { + String columnNamePrefix = '', + String parameterPrefix = '', + }) { + for (final field in fields) { + if (field.embedConverter != null) { + final embedVar = field.columnName.isEmpty ? '' : '${field.columnName}_'; + _processFields( + map, + field.embedConverter?.fields ?? [], + columnNamePrefix: '$columnNamePrefix$embedVar', + parameterPrefix: '$parameterPrefix${field.fieldElement.name}.', + ); + } else { + map['$columnNamePrefix${field.columnName}'] = + _getAttributeValue(field, parameterPrefix); + } + } + } + String _getValueMapping(final List fields) { - final keyValueList = fields.map((field) { - final columnName = field.columnName; - final attributeValue = _getAttributeValue(field); - return "'$columnName': $attributeValue"; - }).toList(); + final Map map = {}; + _processFields(map, fields); + + final keyValueList = map.entries + .map((entry) => "'${entry.key}': ${entry.value}") + .toList(); return '{${keyValueList.join(', ')}}'; } - String _getAttributeValue(final Field field) { + String _getAttributeValue(final Field field, String parameterPrefix) { final fieldElement = field.fieldElement; final parameterName = fieldElement.displayName; final fieldType = fieldElement.type; final typeConverter = [...queryableTypeConverters, field.typeConverter] .whereNotNull() .getClosestOrNull(fieldType); - String attributeValue = 'item.$parameterName'; + String attributeValue = 'item.$parameterPrefix$parameterName'; if (typeConverter != null) { attributeValue = - '_${typeConverter.name.decapitalize()}.encode(item.$parameterName)'; + '_${typeConverter.name.decapitalize()}.encode(item.$parameterPrefix$parameterName)'; } else if (fieldType.isDartCoreBool) { attributeValue = _serializeBoolean(field, attributeValue); } else if (fieldType.isEnumType) { diff --git a/floor_generator/lib/processor/field_processor.dart b/floor_generator/lib/processor/field_processor.dart index 26015be5..caa9e8af 100644 --- a/floor_generator/lib/processor/field_processor.dart +++ b/floor_generator/lib/processor/field_processor.dart @@ -12,15 +12,18 @@ import 'package:floor_generator/value_object/field.dart'; import 'package:floor_generator/value_object/type_converter.dart'; import 'package:source_gen/source_gen.dart'; +import '../value_object/embed.dart'; + class FieldProcessor extends Processor { final FieldElement _fieldElement; final TypeConverter? _typeConverter; + final Embed? _embedConverter; FieldProcessor( - final FieldElement fieldElement, - final TypeConverter? typeConverter, - ) : _fieldElement = fieldElement, - _typeConverter = typeConverter; + this._fieldElement, + this._typeConverter, + this._embedConverter, + ); @override Field process() { @@ -37,8 +40,9 @@ class FieldProcessor extends Processor { name, columnName, isNullable, - _getSqlType(typeConverter), + _getSqlType(typeConverter, _embedConverter), typeConverter, + _embedConverter ); } @@ -52,13 +56,15 @@ class FieldProcessor extends Processor { : name; } - String _getSqlType(final TypeConverter? typeConverter) { + String _getSqlType(final TypeConverter? typeConverter, final Embed? embedConverter) { final type = _fieldElement.type; if (typeConverter != null) { return typeConverter.databaseType.asSqlType(); } else if (type.isDefaultSqlType || type.isEnumType) { return type.asSqlType(); - } else { + } else if (embedConverter != null) { + return ''; + }else { throw InvalidGenerationSourceError( 'Column type is not supported for $type.', todo: 'Either make to use a supported type or supply a type converter.', diff --git a/floor_generator/lib/processor/queryable_processor.dart b/floor_generator/lib/processor/queryable_processor.dart index f5291c62..75519bdd 100644 --- a/floor_generator/lib/processor/queryable_processor.dart +++ b/floor_generator/lib/processor/queryable_processor.dart @@ -1,6 +1,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:collection/collection.dart'; -import 'package:floor_annotation/floor_annotation.dart' as annotations; +import 'package:floor_generator/misc/extension/embeds_extension.dart'; +import 'package:floor_generator/misc/extension/field_element_extension.dart'; import 'package:floor_generator/misc/extension/set_extension.dart'; import 'package:floor_generator/misc/extension/string_extension.dart'; import 'package:floor_generator/misc/extension/type_converter_element_extension.dart'; @@ -13,7 +14,8 @@ import 'package:floor_generator/value_object/field.dart'; import 'package:floor_generator/value_object/queryable.dart'; import 'package:floor_generator/value_object/type_converter.dart'; import 'package:meta/meta.dart'; -import 'package:source_gen/source_gen.dart'; + +import '../value_object/embed.dart'; abstract class QueryableProcessor extends Processor { final QueryableProcessorError _queryableProcessorError; @@ -23,11 +25,14 @@ abstract class QueryableProcessor extends Processor { final Set queryableTypeConverters; + final Set embedConverters; + @protected QueryableProcessor( this.classElement, final Set typeConverters, - ) : _queryableProcessorError = QueryableProcessorError(classElement), + this.embedConverters, + ) : _queryableProcessorError = QueryableProcessorError(classElement), queryableTypeConverters = typeConverters + classElement.getTypeConverters(TypeConverterScope.queryable); @@ -39,19 +44,23 @@ abstract class QueryableProcessor extends Processor { final fields = [ ...classElement.fields, ...classElement.allSupertypes.expand((type) => type.element.fields), - ]; - - return fields - .where((fieldElement) => fieldElement.shouldBeIncluded()) - .map((field) { - final typeConverter = - queryableTypeConverters.getClosestOrNull(field.type); - return FieldProcessor(field, typeConverter).process(); + ].where((fieldElement) => fieldElement.shouldBeIncluded()); + + return fields.map((field) { + if (field.isEmbedded) { + return FieldProcessor(field, null, embedConverters.getClosestOrNull(field.type)).process(); + } else { + return FieldProcessor(field, queryableTypeConverters.getClosestOrNull(field.type), null).process(); + } }).toList(); } @protected String getConstructor(final List fields) { + return _getConstructor(classElement, fields); + } + + String _getConstructor(ClassElement classElement, final List fields, {String prefix = ''}) { final constructorParameters = classElement.constructors .firstWhereOrNull((element) => element.isPublic && !element.isFactory) ?.parameters; @@ -61,7 +70,7 @@ abstract class QueryableProcessor extends Processor { } else { final parameterValues = constructorParameters .map((parameterElement) => - _getParameterValue(parameterElement, fields)) + _getParameterValue(parameterElement, fields, prefix: prefix)) .where((parameterValue) => parameterValue != null) .join(', '); @@ -72,56 +81,43 @@ abstract class QueryableProcessor extends Processor { /// Returns `null` whenever field is @ignored String? _getParameterValue( final ParameterElement parameterElement, - final List fields, + final List fields, { + final String prefix = '', + } ) { final parameterName = parameterElement.displayName; - final field = - // null whenever field is @ignored - fields.firstWhereOrNull((field) => field.name == parameterName); + final field = fields.firstWhereOrNull((field) => field.fieldElement.displayName == parameterName); if (field != null) { - final databaseValue = "row['${field.columnName}']"; + final databaseValue = "row['$prefix${field.columnName}']"; String parameterValue; - - final typeConverter = [...queryableTypeConverters, field.typeConverter] - .whereNotNull() - .getClosestOrNull(parameterElement.type); - - if (typeConverter != null) { - final castedDatabaseValue = databaseValue.cast( - typeConverter.databaseType, - parameterElement, - ); - - parameterValue = - '_${typeConverter.name.decapitalize()}.decode($castedDatabaseValue)'; - } else if (parameterElement.type.isDefaultSqlType || - parameterElement.type.isEnumType) { + if (parameterElement.type.isDefaultSqlType) { parameterValue = databaseValue.cast( parameterElement.type, parameterElement, ); + } else if (field.embedConverter != null) { + final embedVar = field.columnName.isEmpty ? '' : '${field.columnName}_'; + parameterValue = _getConstructor(field.embedConverter!.classElement, field.embedConverter!.fields, prefix: '$prefix$embedVar'); } else { - throw InvalidGenerationSourceError( - 'Column type is not supported for ${parameterElement.type}', - todo: - 'Either use a supported type https://pinchbv.github.io/floor/entities/#supported-types or supply a type converter.', + final typeConverter = [ + ...queryableTypeConverters, + field.typeConverter, + ].whereNotNull().getClosest(parameterElement.type); + + final castedDatabaseValue = databaseValue.cast( + typeConverter.databaseType, + parameterElement, ); + + parameterValue = '_${typeConverter.name.decapitalize()}.decode($castedDatabaseValue)'; } if (parameterElement.isNamed) { return '$parameterName: $parameterValue'; } return parameterValue; // also covers positional parameter - } else { - return null; - } - } -} - -extension on FieldElement { - bool shouldBeIncluded() { - final isIgnored = hasAnnotation(annotations.ignore.runtimeType); - return !(isStatic || isSynthetic || isIgnored); + } + return null; } } diff --git a/floor_generator/lib/processor/view_processor.dart b/floor_generator/lib/processor/view_processor.dart index b30d9bd5..3b1995df 100644 --- a/floor_generator/lib/processor/view_processor.dart +++ b/floor_generator/lib/processor/view_processor.dart @@ -7,14 +7,17 @@ import 'package:floor_generator/processor/queryable_processor.dart'; import 'package:floor_generator/value_object/type_converter.dart'; import 'package:floor_generator/value_object/view.dart'; +import '../value_object/embed.dart'; + class ViewProcessor extends QueryableProcessor { final ViewProcessorError _processorError; ViewProcessor( final ClassElement classElement, final Set typeConverters, + final Set embedConverters, ) : _processorError = ViewProcessorError(classElement), - super(classElement, typeConverters); + super(classElement, typeConverters, embedConverters); @override View process() { diff --git a/floor_generator/lib/value_object/entity.dart b/floor_generator/lib/value_object/entity.dart index fda23624..58bc9d21 100644 --- a/floor_generator/lib/value_object/entity.dart +++ b/floor_generator/lib/value_object/entity.dart @@ -30,7 +30,7 @@ class Entity extends Queryable { ) : super(classElement, name, fields, constructor); String getCreateTableStatement() { - final databaseDefinition = fields.map((field) { + final databaseDefinition = _getFields(fields).map((field) { final autoIncrement = primaryKey.fields.contains(field) && primaryKey.autoGenerateId; return field.getDatabaseDefinition(autoIncrement); @@ -57,6 +57,18 @@ class Entity extends Queryable { } } + List _getFields(List fields, {String columnNamePrefix = ''}) { + return fields.map((field) { + final embedConverter = field.embedConverter; + if (embedConverter != null) { + final embedVar = field.columnName.isEmpty ? '' : '${field.columnName}_'; + return _getFields(embedConverter.fields, columnNamePrefix: embedVar); + } else { + return [field.copyWith(columnNamePrefix: columnNamePrefix)]; + } + }).reduce((value, element) => value + element); + } + String? _createPrimaryKeyDefinition() { if (primaryKey.autoGenerateId) { return null; diff --git a/floor_generator/lib/value_object/field.dart b/floor_generator/lib/value_object/field.dart index 0f265ed4..b743543b 100644 --- a/floor_generator/lib/value_object/field.dart +++ b/floor_generator/lib/value_object/field.dart @@ -1,5 +1,7 @@ import 'package:analyzer/dart/element/element.dart'; +import 'package:floor_generator/value_object/embed.dart'; import 'package:floor_generator/value_object/type_converter.dart'; +import 'package:source_gen/source_gen.dart'; /// Represents an Entity field and thus a table column. class Field { @@ -9,6 +11,7 @@ class Field { final bool isNullable; final String sqlType; final TypeConverter? typeConverter; + final Embed? embedConverter; Field( this.fieldElement, @@ -17,10 +20,19 @@ class Field { this.isNullable, this.sqlType, this.typeConverter, + this.embedConverter, ); /// The database column definition. String getDatabaseDefinition(final bool autoGenerate) { + if (embedConverter != null) { + throw InvalidGenerationSourceError( + 'You ', + todo: 'Either make to use a supported type or supply a type converter.', + element: fieldElement, + ); + } + final columnSpecification = StringBuffer(); if (autoGenerate) { @@ -33,6 +45,16 @@ class Field { return '`$columnName` $sqlType$columnSpecification'; } + Field copyWith({ + String columnNamePrefix = '', + }) => Field(fieldElement, + name, + '$columnNamePrefix$columnName', + isNullable, + sqlType, + typeConverter, embedConverter, + ); + @override bool operator ==(Object other) => identical(this, other) || diff --git a/floor_generator/test/processor/dao_processor_test.dart b/floor_generator/test/processor/dao_processor_test.dart index a718498f..995e9a63 100644 --- a/floor_generator/test/processor/dao_processor_test.dart +++ b/floor_generator/test/processor/dao_processor_test.dart @@ -232,7 +232,7 @@ Future> _getEntities() async { return library.classes .where((classElement) => classElement.hasAnnotation(annotations.Entity)) - .map((classElement) => EntityProcessor(classElement, {}).process()) + .map((classElement) => EntityProcessor(classElement, {}, {}).process()) .toList(); } @@ -258,6 +258,6 @@ Future> _getViews() async { return library.classes .where((classElement) => classElement.hasAnnotation(annotations.DatabaseView)) - .map((classElement) => ViewProcessor(classElement, {}).process()) + .map((classElement) => ViewProcessor(classElement, {}, {}).process()) .toList(); } diff --git a/floor_generator/test/processor/entity_processor_test.dart b/floor_generator/test/processor/entity_processor_test.dart index 02332bbb..9f8aa003 100644 --- a/floor_generator/test/processor/entity_processor_test.dart +++ b/floor_generator/test/processor/entity_processor_test.dart @@ -29,11 +29,11 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process(); + final actual = EntityProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -77,11 +77,11 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process(); + final actual = EntityProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -115,11 +115,11 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process(); + final actual = EntityProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey(fields, false); const foreignKeys = []; @@ -154,11 +154,11 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process(); + final actual = EntityProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey(fields.sublist(0, 1), false); const foreignKeys = []; @@ -202,11 +202,11 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process(); + final actual = EntityProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -266,7 +266,7 @@ void main() { '''); final actual = - EntityProcessor(classElements[1], {}).process().foreignKeys[0]; + EntityProcessor(classElements[1], {}, {}).process().foreignKeys[0]; final expected = ForeignKey( 'Person', @@ -296,7 +296,7 @@ void main() { } '''); - final actual = EntityProcessor(classElements[0], {}).process().fts; + final actual = EntityProcessor(classElements[0], {}, {}).process().fts; final Fts expected = Fts3('simple', []); @@ -321,7 +321,7 @@ void main() { } '''); - final actual = EntityProcessor(classElements[0], {}).process().fts; + final actual = EntityProcessor(classElements[0], {}, {}).process().fts; final Fts expected = Fts4('simple', []); @@ -342,11 +342,11 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process(); + final actual = EntityProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -381,7 +381,7 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process().valueMapping; + final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " @@ -403,7 +403,7 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process().valueMapping; + final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " @@ -428,7 +428,7 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process().valueMapping; + final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " @@ -453,7 +453,7 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}).process().valueMapping; + final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " @@ -476,7 +476,7 @@ void main() { } '''); - final processor = EntityProcessor(classElements, {}); + final processor = EntityProcessor(classElements, {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -496,7 +496,7 @@ void main() { } '''); - final processor = EntityProcessor(classElements, {}); + final processor = EntityProcessor(classElements, {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -537,7 +537,7 @@ void main() { } '''); - final processor = EntityProcessor(classElements[1], {}); + final processor = EntityProcessor(classElements[1], {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -578,7 +578,7 @@ void main() { } '''); - final processor = EntityProcessor(classElements[1], {}); + final processor = EntityProcessor(classElements[1], {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -612,7 +612,7 @@ void main() { } '''); - final processor = EntityProcessor(classElements[0], {}); + final processor = EntityProcessor(classElements[0], {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -644,7 +644,7 @@ void main() { } '''); - final processor = EntityProcessor(classElements, {}); + final processor = EntityProcessor(classElements, {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -668,7 +668,7 @@ void main() { } '''); - final processor = EntityProcessor(classElement, {}); + final processor = EntityProcessor(classElement, {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -692,7 +692,7 @@ void main() { } '''); - final processor = EntityProcessor(classElement, {}); + final processor = EntityProcessor(classElement, {}, {}); expect( processor.process, throwsInvalidGenerationSourceError(EntityProcessorError(classElement) @@ -716,7 +716,7 @@ void main() { } '''); - final processor = EntityProcessor(classElement, {}); + final processor = EntityProcessor(classElement, {}, {}); expect( processor.process, throwsInvalidGenerationSourceError( @@ -738,7 +738,7 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}); + final actual = EntityProcessor(classElement, {}, {}); expect( actual.process, diff --git a/floor_generator/test/processor/field_processor_test.dart b/floor_generator/test/processor/field_processor_test.dart index 7334bec0..83df54ce 100644 --- a/floor_generator/test/processor/field_processor_test.dart +++ b/floor_generator/test/processor/field_processor_test.dart @@ -16,7 +16,7 @@ void main() { final int id; '''); - final actual = FieldProcessor(fieldElement, null).process(); + final actual = FieldProcessor(fieldElement, null, null).process(); const name = 'id'; const columnName = 'id'; @@ -29,6 +29,7 @@ void main() { isNullable, sqlType, null, + null, ); expect(actual, equals(expected)); }); @@ -38,7 +39,7 @@ void main() { final int? id; '''); - final actual = FieldProcessor(fieldElement, null).process(); + final actual = FieldProcessor(fieldElement, null, null).process(); const name = 'id'; const columnName = 'id'; @@ -51,6 +52,7 @@ void main() { isNullable, sqlType, null, + null, ); expect(actual, equals(expected)); }); @@ -61,7 +63,7 @@ void main() { final Uint8List bytes; '''); - final actual = FieldProcessor(fieldElement, null).process(); + final actual = FieldProcessor(fieldElement, null, null).process(); const name = 'bytes'; const columnName = 'data'; @@ -74,6 +76,7 @@ void main() { isNullable, sqlType, null, + null, ); expect(actual, equals(expected)); }); @@ -89,7 +92,7 @@ void main() { final DateTime dateTime; '''); - final actual = FieldProcessor(fieldElement, typeConverter).process(); + final actual = FieldProcessor(fieldElement, typeConverter, null).process(); const name = 'dateTime'; const columnName = 'dateTime'; @@ -102,6 +105,7 @@ void main() { isNullable, sqlType, typeConverter, + null, ); expect(actual, equals(expected)); }); @@ -112,7 +116,7 @@ void main() { final DateTime dateTime; '''); - final actual = FieldProcessor(fieldElement, null).process(); + final actual = FieldProcessor(fieldElement, null, null).process(); const name = 'dateTime'; const columnName = 'dateTime'; @@ -131,6 +135,7 @@ void main() { isNullable, sqlType, typeConverter, + null, ); expect(actual, equals(expected)); }); @@ -148,7 +153,7 @@ void main() { '''); final actual = - FieldProcessor(fieldElement, externalTypeConverter).process(); + FieldProcessor(fieldElement, externalTypeConverter, null).process(); const name = 'dateTime'; const columnName = 'dateTime'; @@ -167,6 +172,7 @@ void main() { isNullable, sqlType, typeConverter, + null, ); expect(actual, equals(expected)); }); @@ -176,7 +182,7 @@ void main() { '''); expect( - FieldProcessor(fieldElement, null).process, + FieldProcessor(fieldElement, null, null).process, throwsInvalidGenerationSourceError(InvalidGenerationSourceError( 'Column type is not supported for List.', todo: diff --git a/floor_generator/test/processor/query_method_processor_test.dart b/floor_generator/test/processor/query_method_processor_test.dart index 5d1470b5..0ce6bce8 100644 --- a/floor_generator/test/processor/query_method_processor_test.dart +++ b/floor_generator/test/processor/query_method_processor_test.dart @@ -511,7 +511,7 @@ Future> _getEntities() async { return library.classes .where((classElement) => classElement.hasAnnotation(annotations.Entity)) - .map((classElement) => EntityProcessor(classElement, {}).process()) + .map((classElement) => EntityProcessor(classElement, {}, {}).process()) .toList(); } @@ -537,6 +537,6 @@ Future> _getViews() async { return library.classes .where((classElement) => classElement.hasAnnotation(annotations.DatabaseView)) - .map((classElement) => ViewProcessor(classElement, {}).process()) + .map((classElement) => ViewProcessor(classElement, {}, {}).process()) .toList(); } diff --git a/floor_generator/test/processor/queryable_processor_test.dart b/floor_generator/test/processor/queryable_processor_test.dart index 3c5176ed..6273fe8f 100644 --- a/floor_generator/test/processor/queryable_processor_test.dart +++ b/floor_generator/test/processor/queryable_processor_test.dart @@ -26,7 +26,7 @@ void main() { final actual = TestProcessor(classElement).process(); final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); const constructor = "Person(row['id'] as int, row['name'] as String)"; final expected = TestQueryable( @@ -57,9 +57,9 @@ void main() { final actual = TestProcessor(classElement, {typeConverter}).process(); - final idField = FieldProcessor(classElement.fields[0], null).process(); + final idField = FieldProcessor(classElement.fields[0], null, null).process(); final dateTimeField = - FieldProcessor(classElement.fields[1], typeConverter).process(); + FieldProcessor(classElement.fields[1], typeConverter, null).process(); final fields = [idField, dateTimeField]; const constructor = "Order(row['id'] as int, _typeConverter.decode(row['dateTime'] as int))"; @@ -103,9 +103,9 @@ void main() { await intDartType, TypeConverterScope.queryable, ); - final idField = FieldProcessor(classElement.fields[0], null).process(); + final idField = FieldProcessor(classElement.fields[0], null, null).process(); final dateTimeField = - FieldProcessor(classElement.fields[1], typeConverter).process(); + FieldProcessor(classElement.fields[1], typeConverter, null).process(); final fields = [idField, dateTimeField]; const constructor = "Order(row['id'] as int, _dateTimeConverter.decode(row['dateTime'] as int))"; @@ -157,9 +157,9 @@ void main() { await intDartType, TypeConverterScope.queryable, ); - final idField = FieldProcessor(classElement.fields[0], null).process(); + final idField = FieldProcessor(classElement.fields[0], null, null).process(); final dateTimeField = - FieldProcessor(classElement.fields[1], typeConverter).process(); + FieldProcessor(classElement.fields[1], typeConverter, null).process(); final fields = [idField, dateTimeField]; const constructor = "Order(row['id'] as int, _dateTimeConverter.decode(row['dateTime'] as int))"; @@ -689,7 +689,7 @@ class TestProcessor extends QueryableProcessor { TestProcessor( ClassElement classElement, [ Set? typeConverters, - ]) : super(classElement, typeConverters ?? {}); + ]) : super(classElement, typeConverters ?? {}, {}); @override TestQueryable process() { diff --git a/floor_generator/test/processor/view_processor_test.dart b/floor_generator/test/processor/view_processor_test.dart index 44aa6de9..21b4405a 100644 --- a/floor_generator/test/processor/view_processor_test.dart +++ b/floor_generator/test/processor/view_processor_test.dart @@ -18,11 +18,11 @@ void main() { } '''); - final actual = ViewProcessor(classElement, {}).process(); + final actual = ViewProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); const query = 'SELECT * from otherentity'; const constructor = "Person(row['id'] as int, row['name'] as String)"; @@ -48,11 +48,11 @@ void main() { } '''); - final actual = ViewProcessor(classElement, {}).process(); + final actual = ViewProcessor(classElement, {}, {}).process(); const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); const query = 'WITH subquery as (SELECT * from otherentity) SELECT subquery.*'; @@ -78,7 +78,7 @@ void main() { } '''); - final actual = () => ViewProcessor(classElement, {}).process(); + final actual = () => ViewProcessor(classElement, {}, {}).process(); expect(actual, throwsInvalidGenerationSourceError()); }); @@ -97,7 +97,7 @@ void main() { } '''); - final actual = () => ViewProcessor(classElement, {}).process(); + final actual = () => ViewProcessor(classElement, {}, {}).process(); expect(actual, throwsInvalidGenerationSourceError()); }); @@ -116,7 +116,7 @@ void main() { } '''); - final actual = () => ViewProcessor(classElement, {}).process(); + final actual = () => ViewProcessor(classElement, {}, {}).process(); expect(actual, throwsInvalidGenerationSourceError()); }); @@ -135,7 +135,7 @@ void main() { } """); - final actual = ViewProcessor(classElement, {}).process().query; + final actual = ViewProcessor(classElement, {}, {}).process().query; const expected = ''' SELECT * @@ -157,7 +157,7 @@ void main() { } '''); - final actual = ViewProcessor(classElement, {}).process().query; + final actual = ViewProcessor(classElement, {}, {}).process().query; const expected = 'SELECT * from otherentity'; expect(actual, equals(expected)); @@ -175,11 +175,11 @@ void main() { } '''); - final actual = ViewProcessor(classElement, {}).process(); + final actual = ViewProcessor(classElement, {}, {}).process(); const name = 'personview'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null).process()) + .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) .toList(); const query = 'SELECT * from otherentity'; const constructor = "Person(row['id'] as int, row['name'] as String)"; diff --git a/floor_generator/test/test_utils.dart b/floor_generator/test/test_utils.dart index 0a5960ae..2363bf3a 100644 --- a/floor_generator/test/test_utils.dart +++ b/floor_generator/test/test_utils.dart @@ -190,12 +190,12 @@ Future createDao(final String methodSignature) async { final entities = library.classes .where((classElement) => classElement.hasAnnotation(annotations.Entity)) - .map((classElement) => EntityProcessor(classElement, {}).process()) + .map((classElement) => EntityProcessor(classElement, {}, {}).process()) .toList(); final views = library.classes .where((classElement) => classElement.hasAnnotation(annotations.DatabaseView)) - .map((classElement) => ViewProcessor(classElement, {}).process()) + .map((classElement) => ViewProcessor(classElement, {}, {}).process()) .toList(); return DaoProcessor( @@ -260,7 +260,7 @@ Future getPersonEntity() async { return library.classes .where((classElement) => classElement.hasAnnotation(annotations.Entity)) - .map((classElement) => EntityProcessor(classElement, {}).process()) + .map((classElement) => EntityProcessor(classElement, {}, {}).process()) .first; } diff --git a/floor_generator/test/value_object/entity_test.dart b/floor_generator/test/value_object/entity_test.dart index 013fa912..a91c2ef4 100644 --- a/floor_generator/test/value_object/entity_test.dart +++ b/floor_generator/test/value_object/entity_test.dart @@ -21,6 +21,7 @@ void main() { false, SqlType.integer, null, + null ); final nullableField = Field( fakeFieldElement, @@ -29,6 +30,7 @@ void main() { true, SqlType.text, null, + null, ); final allFields = [field, nullableField]; diff --git a/floor_generator/test/value_object/field_test.dart b/floor_generator/test/value_object/field_test.dart index e01f8b26..20610013 100644 --- a/floor_generator/test/value_object/field_test.dart +++ b/floor_generator/test/value_object/field_test.dart @@ -16,6 +16,7 @@ void main() { false, SqlType.integer, null, + null, ); final actual = field.getDatabaseDefinition(autoGenerate); @@ -34,6 +35,7 @@ void main() { true, SqlType.text, null, + null, ); final actual = field.getDatabaseDefinition(autoGenerate); diff --git a/floor_generator/test/value_object/view_test.dart b/floor_generator/test/value_object/view_test.dart index 78a494b3..032d3c43 100644 --- a/floor_generator/test/value_object/view_test.dart +++ b/floor_generator/test/value_object/view_test.dart @@ -16,6 +16,7 @@ void main() { false, SqlType.integer, null, + null, ); final nullableField = Field( fakeFieldElement, @@ -24,6 +25,7 @@ void main() { true, SqlType.text, null, + null, ); final allFields = [field, nullableField]; diff --git a/floor_generator/test/writer/dao_writer_test.dart b/floor_generator/test/writer/dao_writer_test.dart index cbf940ce..2f2c4972 100644 --- a/floor_generator/test/writer/dao_writer_test.dart +++ b/floor_generator/test/writer/dao_writer_test.dart @@ -445,13 +445,13 @@ Future _createDao(final String dao) async { final entities = library.classes .where((classElement) => classElement.hasAnnotation(annotations.Entity)) - .map((classElement) => EntityProcessor(classElement, {}).process()) + .map((classElement) => EntityProcessor(classElement, {}, {}).process()) .toList(); final views = library.classes .where((classElement) => classElement.hasAnnotation(annotations.DatabaseView)) - .map((classElement) => ViewProcessor(classElement, {}).process()) + .map((classElement) => ViewProcessor(classElement, {}, {}).process()) .toList(); return DaoProcessor( diff --git a/floor_generator/test/writer/query_method_writer_test.dart b/floor_generator/test/writer/query_method_writer_test.dart index caac2306..bc285602 100644 --- a/floor_generator/test/writer/query_method_writer_test.dart +++ b/floor_generator/test/writer/query_method_writer_test.dart @@ -897,10 +897,8 @@ Future createOrderDao( final entities = library.classes .where((classElement) => classElement.hasAnnotation(annotations.Entity)) - .map((classElement) => EntityProcessor( - classElement, - typeConverters, - ).process()) + .map((classElement) => + EntityProcessor(classElement, typeConverters, {}).process()) .toList(); return DaoProcessor( From 6e1c640ae3ba7fb9f1cce6fbf766bc97cac7fbde Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 21 May 2024 15:34:00 +0200 Subject: [PATCH 04/20] Add converter for taskstatus --- example/lib/database.dart | 2 +- example/lib/database.g.dart | 64 +++++++++++++++++++++------------ example/lib/type_converter.dart | 12 +++++++ 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/example/lib/database.dart b/example/lib/database.dart index 5c7d6aa5..c6be150b 100644 --- a/example/lib/database.dart +++ b/example/lib/database.dart @@ -9,7 +9,7 @@ import 'package:sqflite/sqflite.dart' as sqflite; part 'database.g.dart'; -@TypeConverters([DateTimeConverter, TaskTypeConverter]) +@TypeConverters([DateTimeConverter, TaskTypeConverter, TaskStatusConverter]) @Database(version: 1, entities: [Task], embeds: [Timestamp]) abstract class FlutterDatabase extends FloorDatabase { TaskDao get taskDao; diff --git a/example/lib/database.g.dart b/example/lib/database.g.dart index 39dcf3a9..d7ea5351 100644 --- a/example/lib/database.g.dart +++ b/example/lib/database.g.dart @@ -96,7 +96,7 @@ class _$FlutterDatabase extends FlutterDatabase { }, onCreate: (database, version) async { await database.execute( - 'CREATE TABLE IF NOT EXISTS `Task` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` TEXT NOT NULL, `isRead` INTEGER, `timestamp` INTEGER NOT NULL, `status` INTEGER, `type` TEXT)'); + 'CREATE TABLE IF NOT EXISTS `Task` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` TEXT NOT NULL, `isRead` INTEGER, `timestamp_created_at` INTEGER NOT NULL, `timestamp_updated_at` INTEGER NOT NULL, `status` TEXT, `type` TEXT)'); await callback?.onCreate?.call(database, version); }, @@ -122,8 +122,11 @@ class _$TaskDao extends TaskDao { 'id': item.id, 'message': item.message, 'isRead': item.isRead == null ? null : (item.isRead! ? 1 : 0), - 'timestamp': _dateTimeConverter.encode(item.timestamp), - 'status': item.status?.index, + 'timestamp_created_at': + _dateTimeConverter.encode(item.timestamp.createdAt), + 'timestamp_updated_at': + _dateTimeConverter.encode(item.timestamp.updatedAt), + 'status': _taskStatusConverter.encode(item.status), 'type': _taskTypeConverter.encode(item.type) }, changeListener), @@ -135,8 +138,11 @@ class _$TaskDao extends TaskDao { 'id': item.id, 'message': item.message, 'isRead': item.isRead == null ? null : (item.isRead! ? 1 : 0), - 'timestamp': _dateTimeConverter.encode(item.timestamp), - 'status': item.status?.index, + 'timestamp_created_at': + _dateTimeConverter.encode(item.timestamp.createdAt), + 'timestamp_updated_at': + _dateTimeConverter.encode(item.timestamp.updatedAt), + 'status': _taskStatusConverter.encode(item.status), 'type': _taskTypeConverter.encode(item.type) }, changeListener), @@ -148,8 +154,11 @@ class _$TaskDao extends TaskDao { 'id': item.id, 'message': item.message, 'isRead': item.isRead == null ? null : (item.isRead! ? 1 : 0), - 'timestamp': _dateTimeConverter.encode(item.timestamp), - 'status': item.status?.index, + 'timestamp_created_at': + _dateTimeConverter.encode(item.timestamp.createdAt), + 'timestamp_updated_at': + _dateTimeConverter.encode(item.timestamp.updatedAt), + 'status': _taskStatusConverter.encode(item.status), 'type': _taskTypeConverter.encode(item.type) }, changeListener); @@ -173,10 +182,12 @@ class _$TaskDao extends TaskDao { row['id'] as int?, row['isRead'] == null ? null : (row['isRead'] as int) != 0, row['message'] as String, - _dateTimeConverter.decode(row['timestamp'] as int), - row['status'] == null - ? null - : TaskStatus.values[row['status'] as int], + Timestamp( + createdAt: _dateTimeConverter + .decode(row['timestamp_created_at'] as int), + updatedAt: _dateTimeConverter + .decode(row['timestamp_updated_at'] as int)), + _taskStatusConverter.decode(row['status'] as String?), _taskTypeConverter.decode(row['type'] as String?)), arguments: [id]); } @@ -188,10 +199,12 @@ class _$TaskDao extends TaskDao { row['id'] as int?, row['isRead'] == null ? null : (row['isRead'] as int) != 0, row['message'] as String, - _dateTimeConverter.decode(row['timestamp'] as int), - row['status'] == null - ? null - : TaskStatus.values[row['status'] as int], + Timestamp( + createdAt: _dateTimeConverter + .decode(row['timestamp_created_at'] as int), + updatedAt: _dateTimeConverter + .decode(row['timestamp_updated_at'] as int)), + _taskStatusConverter.decode(row['status'] as String?), _taskTypeConverter.decode(row['type'] as String?))); } @@ -202,10 +215,12 @@ class _$TaskDao extends TaskDao { row['id'] as int?, row['isRead'] == null ? null : (row['isRead'] as int) != 0, row['message'] as String, - _dateTimeConverter.decode(row['timestamp'] as int), - row['status'] == null - ? null - : TaskStatus.values[row['status'] as int], + Timestamp( + createdAt: _dateTimeConverter + .decode(row['timestamp_created_at'] as int), + updatedAt: _dateTimeConverter + .decode(row['timestamp_updated_at'] as int)), + _taskStatusConverter.decode(row['status'] as String?), _taskTypeConverter.decode(row['type'] as String?)), queryableName: 'task', isView: false); @@ -226,10 +241,12 @@ class _$TaskDao extends TaskDao { row['id'] as int?, row['isRead'] == null ? null : (row['isRead'] as int) != 0, row['message'] as String, - _dateTimeConverter.decode(row['timestamp'] as int), - row['status'] == null - ? null - : TaskStatus.values[row['status'] as int], + Timestamp( + createdAt: _dateTimeConverter + .decode(row['timestamp_created_at'] as int), + updatedAt: _dateTimeConverter + .decode(row['timestamp_updated_at'] as int)), + _taskStatusConverter.decode(row['status'] as String?), _taskTypeConverter.decode(row['type'] as String?)), arguments: [status.index], queryableName: 'task', @@ -281,3 +298,4 @@ class _$TaskDao extends TaskDao { // ignore_for_file: unused_element final _dateTimeConverter = DateTimeConverter(); final _taskTypeConverter = TaskTypeConverter(); +final _taskStatusConverter = TaskStatusConverter(); diff --git a/example/lib/type_converter.dart b/example/lib/type_converter.dart index fc2a65aa..29e8e4dc 100644 --- a/example/lib/type_converter.dart +++ b/example/lib/type_converter.dart @@ -24,3 +24,15 @@ class TaskTypeConverter extends TypeConverter { return value?.name; } } + +class TaskStatusConverter extends TypeConverter { + @override + TaskStatus? decode(String? databaseValue) { + return databaseValue == null ? null : TaskStatus.values.byName(databaseValue); + } + + @override + String? encode(TaskStatus? value) { + return value?.name; + } +} From 99cd140b95b30d832ce425ea1daff63ad2f794fb Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 21 May 2024 15:37:54 +0200 Subject: [PATCH 05/20] Update main --- example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index e00c695d..c6e80df3 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -180,7 +180,7 @@ class TaskListCell extends StatelessWidget { child: ListTile( title: Text(task.message), subtitle: Text('Status: ${task.statusTitle}'), - trailing: Text("task.timestamp.toIso8601String()"), + trailing: Text(task.timestamp.createdAt.toIso8601String()), ), confirmDismiss: (direction) async { String? statusMessage; From a3e15148cb5f042d105f65742c7dbe4150aa85ee Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 21 May 2024 15:42:28 +0200 Subject: [PATCH 06/20] Update main --- example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index c6e80df3..5c4a408f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -180,7 +180,7 @@ class TaskListCell extends StatelessWidget { child: ListTile( title: Text(task.message), subtitle: Text('Status: ${task.statusTitle}'), - trailing: Text(task.timestamp.createdAt.toIso8601String()), + trailing: Text(task.timestamp.toString()), ), confirmDismiss: (direction) async { String? statusMessage; From d25e530daf918ea5c0cbc634a1dc9614239bdc14 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 21 May 2024 16:32:40 +0200 Subject: [PATCH 07/20] fix issues --- floor_annotation/lib/src/embed.dart | 2 +- floor_generator/lib/misc/extension/embeds_extension.dart | 2 -- floor_generator/lib/processor/database_processor.dart | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/floor_annotation/lib/src/embed.dart b/floor_annotation/lib/src/embed.dart index 4f7268bf..67258c69 100644 --- a/floor_annotation/lib/src/embed.dart +++ b/floor_annotation/lib/src/embed.dart @@ -2,4 +2,4 @@ class Embed { const Embed(); } -const embed = Embed(); \ No newline at end of file +const embed = Embed(); diff --git a/floor_generator/lib/misc/extension/embeds_extension.dart b/floor_generator/lib/misc/extension/embeds_extension.dart index b19f64ef..0e788da7 100644 --- a/floor_generator/lib/misc/extension/embeds_extension.dart +++ b/floor_generator/lib/misc/extension/embeds_extension.dart @@ -1,9 +1,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:collection/collection.dart'; -import 'package:floor_generator/misc/extension/iterable_extension.dart'; import 'package:floor_generator/value_object/embed.dart'; import 'package:floor_generator/value_object/type_converter.dart'; -import 'package:source_gen/source_gen.dart'; extension EmbedsExtension on Iterable { /// Returns the [Embed] in the closest [TypeConverterScope] for diff --git a/floor_generator/lib/processor/database_processor.dart b/floor_generator/lib/processor/database_processor.dart index 2caed01b..0a143a66 100644 --- a/floor_generator/lib/processor/database_processor.dart +++ b/floor_generator/lib/processor/database_processor.dart @@ -10,11 +10,11 @@ import 'package:floor_generator/processor/embed_processor.dart'; import 'package:floor_generator/processor/entity_processor.dart'; import 'package:floor_generator/processor/error/database_processor_error.dart'; import 'package:floor_generator/processor/processor.dart'; +import 'package:floor_generator/value_object/embed.dart'; import 'package:floor_generator/processor/view_processor.dart'; import 'package:floor_generator/value_object/dao_getter.dart'; import 'package:floor_generator/value_object/database.dart'; import 'package:floor_generator/value_object/entity.dart'; -import 'package:floor_generator/value_object/embed.dart'; import 'package:floor_generator/value_object/queryable.dart'; import 'package:floor_generator/value_object/type_converter.dart'; import 'package:floor_generator/value_object/view.dart'; From 0a3c42c4f9544bbe7e5556056f5a564bc63064ee Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Fri, 24 May 2024 11:38:12 +0200 Subject: [PATCH 08/20] Fix getEmbeds() --- .../lib/processor/database_processor.dart | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/floor_generator/lib/processor/database_processor.dart b/floor_generator/lib/processor/database_processor.dart index 0a143a66..369647cb 100644 --- a/floor_generator/lib/processor/database_processor.dart +++ b/floor_generator/lib/processor/database_processor.dart @@ -110,24 +110,19 @@ class DatabaseProcessor extends Processor { final ClassElement databaseClassElement, final Set typeConverters, ) { - final entities = _classElement - .getAnnotation(annotations.Database) - ?.getField(AnnotationField.databaseEmbeds) - ?.toListValue() - ?.mapNotNull((object) => object.toTypeValue()?.element) - .whereType() - .where(_isEmbed) - .map((classElement) => EmbedProcessor( - classElement, - typeConverters, - ).process()) - .toSet(); - - if (entities == null || entities.isEmpty) { - throw _processorError.noEntitiesDefined; - } - - return entities; + return _classElement + .getAnnotation(annotations.Database) + ?.getField(AnnotationField.databaseEmbeds) + ?.toListValue() + ?.mapNotNull((object) => object.toTypeValue()?.element) + .whereType() + .where(_isEmbed) + .map((classElement) => EmbedProcessor( + classElement, + typeConverters, + ).process()) + .toSet() ?? + {}; } List _getEntities( From 389f2815795f65decbe8d4ab166cdf68a93d5da8 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Fri, 24 May 2024 12:40:27 +0200 Subject: [PATCH 09/20] Fix date format in example --- example/lib/timestamp.dart | 8 ++++++-- example/pubspec.lock | 8 ++++++++ example/pubspec.yaml | 1 + floor_generator/lib/processor/database_processor.dart | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/example/lib/timestamp.dart b/example/lib/timestamp.dart index a60b81b7..8428e419 100644 --- a/example/lib/timestamp.dart +++ b/example/lib/timestamp.dart @@ -1,4 +1,5 @@ import 'package:floor/floor.dart'; +import 'package:intl/intl.dart'; @Embed() class Timestamp { @@ -23,6 +24,9 @@ class Timestamp { @override String toString() { - return 'Timestamp{createdAt: $createdAt, updatedAt: $updatedAt}'; + final DateFormat formatter = DateFormat('yyyy-MM-dd'); + final String formattedCreatedAt = formatter.format(createdAt); + final String formattedUpdatedAt = formatter.format(updatedAt); + return 'Timestamp{createdAt: $formattedCreatedAt, updatedAt: $formattedUpdatedAt}'; } -} \ No newline at end of file +} diff --git a/example/pubspec.lock b/example/pubspec.lock index d6d6de9f..c780dd34 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -311,6 +311,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" io: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 0b074ef2..b25a1d64 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: flutter: sdk: flutter sqflite: any # This dependency is needed to solve the problem with warning about transitive dependencies + intl: ^0.17.0 dev_dependencies: analyzer: ^6.4.1 diff --git a/floor_generator/lib/processor/database_processor.dart b/floor_generator/lib/processor/database_processor.dart index 369647cb..4d1f77ba 100644 --- a/floor_generator/lib/processor/database_processor.dart +++ b/floor_generator/lib/processor/database_processor.dart @@ -10,10 +10,10 @@ import 'package:floor_generator/processor/embed_processor.dart'; import 'package:floor_generator/processor/entity_processor.dart'; import 'package:floor_generator/processor/error/database_processor_error.dart'; import 'package:floor_generator/processor/processor.dart'; -import 'package:floor_generator/value_object/embed.dart'; import 'package:floor_generator/processor/view_processor.dart'; import 'package:floor_generator/value_object/dao_getter.dart'; import 'package:floor_generator/value_object/database.dart'; +import 'package:floor_generator/value_object/embed.dart'; import 'package:floor_generator/value_object/entity.dart'; import 'package:floor_generator/value_object/queryable.dart'; import 'package:floor_generator/value_object/type_converter.dart'; From 989ca78f4655752d335434b74409e59f494959e4 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Fri, 24 May 2024 12:46:45 +0200 Subject: [PATCH 10/20] Fix format issues --- .../lib/misc/extension/embeds_extension.dart | 7 ++-- .../extension/field_element_extension.dart | 5 +-- .../lib/processor/embed_processor.dart | 6 ++-- .../lib/processor/entity_processor.dart | 5 ++- .../lib/processor/field_processor.dart | 22 ++++++------ .../lib/processor/queryable_processor.dart | 34 ++++++++++++------- floor_generator/lib/value_object/embed.dart | 8 ++--- floor_generator/lib/value_object/field.dart | 19 ++++++----- .../test/processor/entity_processor_test.dart | 30 ++++++++++------ .../processor/queryable_processor_test.dart | 12 ++++--- .../test/processor/view_processor_test.dart | 9 +++-- .../test/value_object/entity_test.dart | 11 ++---- 12 files changed, 95 insertions(+), 73 deletions(-) diff --git a/floor_generator/lib/misc/extension/embeds_extension.dart b/floor_generator/lib/misc/extension/embeds_extension.dart index 0e788da7..a6a01f1d 100644 --- a/floor_generator/lib/misc/extension/embeds_extension.dart +++ b/floor_generator/lib/misc/extension/embeds_extension.dart @@ -7,8 +7,7 @@ extension EmbedsExtension on Iterable { /// Returns the [Embed] in the closest [TypeConverterScope] for /// [dartType] or null Embed? getClosestOrNull(DartType dartType) { - return toList() - .firstWhereOrNull( - (embed) => embed.classElement.name == dartType.toString()); + return toList().firstWhereOrNull( + (embed) => embed.classElement.name == dartType.toString()); } -} \ No newline at end of file +} diff --git a/floor_generator/lib/misc/extension/field_element_extension.dart b/floor_generator/lib/misc/extension/field_element_extension.dart index 88b5bdeb..037dc06e 100644 --- a/floor_generator/lib/misc/extension/field_element_extension.dart +++ b/floor_generator/lib/misc/extension/field_element_extension.dart @@ -9,6 +9,7 @@ extension FieldElementExtension on FieldElement { } bool get isEmbedded { - return (type.element?.hasAnnotation(annotations.Embed) ?? false) && type.element is ClassElement; + return (type.element?.hasAnnotation(annotations.Embed) ?? false) && + type.element is ClassElement; } -} \ No newline at end of file +} diff --git a/floor_generator/lib/processor/embed_processor.dart b/floor_generator/lib/processor/embed_processor.dart index cb4f5d7f..5ab5a4f7 100644 --- a/floor_generator/lib/processor/embed_processor.dart +++ b/floor_generator/lib/processor/embed_processor.dart @@ -24,9 +24,11 @@ class EmbedProcessor extends Processor { List _getFields() { final fields = _classElement.fields .where((fieldElement) => fieldElement.shouldBeIncluded()) - .map((field) => FieldProcessor(field, typeConverters.getClosestOrNull(field.type), null).process()) + .map((field) => FieldProcessor( + field, typeConverters.getClosestOrNull(field.type), null) + .process()) .toList(); return fields; } -} \ No newline at end of file +} diff --git a/floor_generator/lib/processor/entity_processor.dart b/floor_generator/lib/processor/entity_processor.dart index 059ab742..23514ee4 100644 --- a/floor_generator/lib/processor/entity_processor.dart +++ b/floor_generator/lib/processor/entity_processor.dart @@ -291,9 +291,8 @@ class EntityProcessor extends QueryableProcessor { final Map map = {}; _processFields(map, fields); - final keyValueList = map.entries - .map((entry) => "'${entry.key}': ${entry.value}") - .toList(); + final keyValueList = + map.entries.map((entry) => "'${entry.key}': ${entry.value}").toList(); return '{${keyValueList.join(', ')}}'; } diff --git a/floor_generator/lib/processor/field_processor.dart b/floor_generator/lib/processor/field_processor.dart index caa9e8af..db2db04c 100644 --- a/floor_generator/lib/processor/field_processor.dart +++ b/floor_generator/lib/processor/field_processor.dart @@ -36,14 +36,13 @@ class FieldProcessor extends Processor { }.whereNotNull().closestOrNull; return Field( - _fieldElement, - name, - columnName, - isNullable, - _getSqlType(typeConverter, _embedConverter), - typeConverter, - _embedConverter - ); + _fieldElement, + name, + columnName, + isNullable, + _getSqlType(typeConverter, _embedConverter), + typeConverter, + _embedConverter); } String _getColumnName(final String name) { @@ -56,15 +55,16 @@ class FieldProcessor extends Processor { : name; } - String _getSqlType(final TypeConverter? typeConverter, final Embed? embedConverter) { + String _getSqlType( + final TypeConverter? typeConverter, final Embed? embedConverter) { final type = _fieldElement.type; if (typeConverter != null) { return typeConverter.databaseType.asSqlType(); } else if (type.isDefaultSqlType || type.isEnumType) { return type.asSqlType(); - } else if (embedConverter != null) { + } else if (embedConverter != null) { return ''; - }else { + } else { throw InvalidGenerationSourceError( 'Column type is not supported for $type.', todo: 'Either make to use a supported type or supply a type converter.', diff --git a/floor_generator/lib/processor/queryable_processor.dart b/floor_generator/lib/processor/queryable_processor.dart index 75519bdd..f48193a8 100644 --- a/floor_generator/lib/processor/queryable_processor.dart +++ b/floor_generator/lib/processor/queryable_processor.dart @@ -31,8 +31,8 @@ abstract class QueryableProcessor extends Processor { QueryableProcessor( this.classElement, final Set typeConverters, - this.embedConverters, - ) : _queryableProcessorError = QueryableProcessorError(classElement), + this.embedConverters, + ) : _queryableProcessorError = QueryableProcessorError(classElement), queryableTypeConverters = typeConverters + classElement.getTypeConverters(TypeConverterScope.queryable); @@ -48,9 +48,13 @@ abstract class QueryableProcessor extends Processor { return fields.map((field) { if (field.isEmbedded) { - return FieldProcessor(field, null, embedConverters.getClosestOrNull(field.type)).process(); + return FieldProcessor( + field, null, embedConverters.getClosestOrNull(field.type)) + .process(); } else { - return FieldProcessor(field, queryableTypeConverters.getClosestOrNull(field.type), null).process(); + return FieldProcessor(field, + queryableTypeConverters.getClosestOrNull(field.type), null) + .process(); } }).toList(); } @@ -60,7 +64,8 @@ abstract class QueryableProcessor extends Processor { return _getConstructor(classElement, fields); } - String _getConstructor(ClassElement classElement, final List fields, {String prefix = ''}) { + String _getConstructor(ClassElement classElement, final List fields, + {String prefix = ''}) { final constructorParameters = classElement.constructors .firstWhereOrNull((element) => element.isPublic && !element.isFactory) ?.parameters; @@ -81,12 +86,12 @@ abstract class QueryableProcessor extends Processor { /// Returns `null` whenever field is @ignored String? _getParameterValue( final ParameterElement parameterElement, - final List fields, { - final String prefix = '', - } - ) { + final List fields, { + final String prefix = '', + }) { final parameterName = parameterElement.displayName; - final field = fields.firstWhereOrNull((field) => field.fieldElement.displayName == parameterName); + final field = fields.firstWhereOrNull( + (field) => field.fieldElement.displayName == parameterName); if (field != null) { final databaseValue = "row['$prefix${field.columnName}']"; @@ -98,7 +103,9 @@ abstract class QueryableProcessor extends Processor { ); } else if (field.embedConverter != null) { final embedVar = field.columnName.isEmpty ? '' : '${field.columnName}_'; - parameterValue = _getConstructor(field.embedConverter!.classElement, field.embedConverter!.fields, prefix: '$prefix$embedVar'); + parameterValue = _getConstructor( + field.embedConverter!.classElement, field.embedConverter!.fields, + prefix: '$prefix$embedVar'); } else { final typeConverter = [ ...queryableTypeConverters, @@ -110,14 +117,15 @@ abstract class QueryableProcessor extends Processor { parameterElement, ); - parameterValue = '_${typeConverter.name.decapitalize()}.decode($castedDatabaseValue)'; + parameterValue = + '_${typeConverter.name.decapitalize()}.decode($castedDatabaseValue)'; } if (parameterElement.isNamed) { return '$parameterName: $parameterValue'; } return parameterValue; // also covers positional parameter - } + } return null; } } diff --git a/floor_generator/lib/value_object/embed.dart b/floor_generator/lib/value_object/embed.dart index 6db3bf51..1cda9b56 100644 --- a/floor_generator/lib/value_object/embed.dart +++ b/floor_generator/lib/value_object/embed.dart @@ -11,9 +11,9 @@ class Embed { @override bool operator ==(Object other) => identical(this, other) || - other is Embed && - runtimeType == other.runtimeType && - const ListEquality().equals(fields, other.fields); + other is Embed && + runtimeType == other.runtimeType && + const ListEquality().equals(fields, other.fields); @override int get hashCode => classElement.hashCode ^ fields.hashCode; @@ -22,4 +22,4 @@ class Embed { String toString() { return 'Embed{classElement: $classElement, fields: $fields}'; } -} \ No newline at end of file +} diff --git a/floor_generator/lib/value_object/field.dart b/floor_generator/lib/value_object/field.dart index b743543b..8fe3d564 100644 --- a/floor_generator/lib/value_object/field.dart +++ b/floor_generator/lib/value_object/field.dart @@ -27,7 +27,7 @@ class Field { String getDatabaseDefinition(final bool autoGenerate) { if (embedConverter != null) { throw InvalidGenerationSourceError( - 'You ', + 'You ', todo: 'Either make to use a supported type or supply a type converter.', element: fieldElement, ); @@ -47,13 +47,16 @@ class Field { Field copyWith({ String columnNamePrefix = '', - }) => Field(fieldElement, - name, - '$columnNamePrefix$columnName', - isNullable, - sqlType, - typeConverter, embedConverter, - ); + }) => + Field( + fieldElement, + name, + '$columnNamePrefix$columnName', + isNullable, + sqlType, + typeConverter, + embedConverter, + ); @override bool operator ==(Object other) => diff --git a/floor_generator/test/processor/entity_processor_test.dart b/floor_generator/test/processor/entity_processor_test.dart index 9f8aa003..3ed3735c 100644 --- a/floor_generator/test/processor/entity_processor_test.dart +++ b/floor_generator/test/processor/entity_processor_test.dart @@ -33,7 +33,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -81,7 +82,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -119,7 +121,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey(fields, false); const foreignKeys = []; @@ -158,7 +161,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey(fields.sublist(0, 1), false); const foreignKeys = []; @@ -206,7 +210,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -346,7 +351,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); final primaryKey = PrimaryKey([fields[0]], false); const foreignKeys = []; @@ -381,7 +387,8 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; + final actual = + EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " @@ -403,7 +410,8 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; + final actual = + EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " @@ -428,7 +436,8 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; + final actual = + EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " @@ -453,7 +462,8 @@ void main() { } '''); - final actual = EntityProcessor(classElement, {}, {}).process().valueMapping; + final actual = + EntityProcessor(classElement, {}, {}).process().valueMapping; const expected = '{' "'id': item.id, " diff --git a/floor_generator/test/processor/queryable_processor_test.dart b/floor_generator/test/processor/queryable_processor_test.dart index 6273fe8f..5b67f2f0 100644 --- a/floor_generator/test/processor/queryable_processor_test.dart +++ b/floor_generator/test/processor/queryable_processor_test.dart @@ -26,7 +26,8 @@ void main() { final actual = TestProcessor(classElement).process(); final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); const constructor = "Person(row['id'] as int, row['name'] as String)"; final expected = TestQueryable( @@ -57,7 +58,8 @@ void main() { final actual = TestProcessor(classElement, {typeConverter}).process(); - final idField = FieldProcessor(classElement.fields[0], null, null).process(); + final idField = + FieldProcessor(classElement.fields[0], null, null).process(); final dateTimeField = FieldProcessor(classElement.fields[1], typeConverter, null).process(); final fields = [idField, dateTimeField]; @@ -103,7 +105,8 @@ void main() { await intDartType, TypeConverterScope.queryable, ); - final idField = FieldProcessor(classElement.fields[0], null, null).process(); + final idField = + FieldProcessor(classElement.fields[0], null, null).process(); final dateTimeField = FieldProcessor(classElement.fields[1], typeConverter, null).process(); final fields = [idField, dateTimeField]; @@ -157,7 +160,8 @@ void main() { await intDartType, TypeConverterScope.queryable, ); - final idField = FieldProcessor(classElement.fields[0], null, null).process(); + final idField = + FieldProcessor(classElement.fields[0], null, null).process(); final dateTimeField = FieldProcessor(classElement.fields[1], typeConverter, null).process(); final fields = [idField, dateTimeField]; diff --git a/floor_generator/test/processor/view_processor_test.dart b/floor_generator/test/processor/view_processor_test.dart index 21b4405a..55fc776d 100644 --- a/floor_generator/test/processor/view_processor_test.dart +++ b/floor_generator/test/processor/view_processor_test.dart @@ -22,7 +22,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); const query = 'SELECT * from otherentity'; const constructor = "Person(row['id'] as int, row['name'] as String)"; @@ -52,7 +53,8 @@ void main() { const name = 'Person'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); const query = 'WITH subquery as (SELECT * from otherentity) SELECT subquery.*'; @@ -179,7 +181,8 @@ void main() { const name = 'personview'; final fields = classElement.fields - .map((fieldElement) => FieldProcessor(fieldElement, null, null).process()) + .map((fieldElement) => + FieldProcessor(fieldElement, null, null).process()) .toList(); const query = 'SELECT * from otherentity'; const constructor = "Person(row['id'] as int, row['name'] as String)"; diff --git a/floor_generator/test/value_object/entity_test.dart b/floor_generator/test/value_object/entity_test.dart index a91c2ef4..ce0952da 100644 --- a/floor_generator/test/value_object/entity_test.dart +++ b/floor_generator/test/value_object/entity_test.dart @@ -14,15 +14,8 @@ void main() { final fakeClassElement = FakeClassElement(); final fakeFieldElement = FakeFieldElement(); - final field = Field( - fakeFieldElement, - 'field1Name', - 'field1ColumnName', - false, - SqlType.integer, - null, - null - ); + final field = Field(fakeFieldElement, 'field1Name', 'field1ColumnName', false, + SqlType.integer, null, null); final nullableField = Field( fakeFieldElement, 'field2Name', From d79daadf14949119c87ded714118d353d6d79a4f Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Mon, 27 May 2024 16:38:47 +0200 Subject: [PATCH 11/20] Fix test in floor_generator --- floor_generator/lib/processor/queryable_processor.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/floor_generator/lib/processor/queryable_processor.dart b/floor_generator/lib/processor/queryable_processor.dart index f48193a8..6597bfe2 100644 --- a/floor_generator/lib/processor/queryable_processor.dart +++ b/floor_generator/lib/processor/queryable_processor.dart @@ -96,7 +96,8 @@ abstract class QueryableProcessor extends Processor { final databaseValue = "row['$prefix${field.columnName}']"; String parameterValue; - if (parameterElement.type.isDefaultSqlType) { + if (parameterElement.type.isDefaultSqlType || + parameterElement.type.isEnumType) { parameterValue = databaseValue.cast( parameterElement.type, parameterElement, From c950102826428edd8778534b62f9a48331bec30c Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Mon, 27 May 2024 16:40:06 +0200 Subject: [PATCH 12/20] format --- floor_generator/lib/processor/queryable_processor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/floor_generator/lib/processor/queryable_processor.dart b/floor_generator/lib/processor/queryable_processor.dart index 6597bfe2..0faf23a7 100644 --- a/floor_generator/lib/processor/queryable_processor.dart +++ b/floor_generator/lib/processor/queryable_processor.dart @@ -97,7 +97,7 @@ abstract class QueryableProcessor extends Processor { String parameterValue; if (parameterElement.type.isDefaultSqlType || - parameterElement.type.isEnumType) { + parameterElement.type.isEnumType) { parameterValue = databaseValue.cast( parameterElement.type, parameterElement, From 92a8c137dcef32970a0ec189e5d2a989e02e5ea7 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 28 May 2024 10:20:52 +0200 Subject: [PATCH 13/20] Fix type converter in the queryable_processor.dart --- example/lib/task.dart | 6 +++- example/lib/timestamp.dart | 10 +++--- .../lib/processor/queryable_processor.dart | 35 +++++++++++-------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/example/lib/task.dart b/example/lib/task.dart index c66741a2..b6042a1c 100644 --- a/example/lib/task.dart +++ b/example/lib/task.dart @@ -57,7 +57,11 @@ class Task { id, isRead ?? false, message ?? 'empty', - timestamp!, + timestamp ?? + Timestamp( + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + ), status, type, ); diff --git a/example/lib/timestamp.dart b/example/lib/timestamp.dart index 8428e419..5b568c6f 100644 --- a/example/lib/timestamp.dart +++ b/example/lib/timestamp.dart @@ -14,10 +14,10 @@ class Timestamp { @override bool operator ==(Object other) => identical(this, other) || - other is Timestamp && - runtimeType == other.runtimeType && - createdAt == other.createdAt && - updatedAt == other.updatedAt; + other is Timestamp && + runtimeType == other.runtimeType && + createdAt == other.createdAt && + updatedAt == other.updatedAt; @override int get hashCode => createdAt.hashCode ^ updatedAt.hashCode; @@ -27,6 +27,6 @@ class Timestamp { final DateFormat formatter = DateFormat('yyyy-MM-dd'); final String formattedCreatedAt = formatter.format(createdAt); final String formattedUpdatedAt = formatter.format(updatedAt); - return 'Timestamp{createdAt: $formattedCreatedAt, updatedAt: $formattedUpdatedAt}'; + return 'createdAt: $formattedCreatedAt \n updatedAt: $formattedUpdatedAt'; } } diff --git a/floor_generator/lib/processor/queryable_processor.dart b/floor_generator/lib/processor/queryable_processor.dart index 0faf23a7..b498bb30 100644 --- a/floor_generator/lib/processor/queryable_processor.dart +++ b/floor_generator/lib/processor/queryable_processor.dart @@ -14,6 +14,7 @@ import 'package:floor_generator/value_object/field.dart'; import 'package:floor_generator/value_object/queryable.dart'; import 'package:floor_generator/value_object/type_converter.dart'; import 'package:meta/meta.dart'; +import 'package:source_gen/source_gen.dart'; import '../value_object/embed.dart'; @@ -90,13 +91,26 @@ abstract class QueryableProcessor extends Processor { final String prefix = '', }) { final parameterName = parameterElement.displayName; - final field = fields.firstWhereOrNull( - (field) => field.fieldElement.displayName == parameterName); + final field = + fields.firstWhereOrNull((field) => field.name == parameterName); if (field != null) { final databaseValue = "row['$prefix${field.columnName}']"; String parameterValue; - if (parameterElement.type.isDefaultSqlType || + + final typeConverter = [...queryableTypeConverters, field.typeConverter] + .whereNotNull() + .getClosestOrNull(parameterElement.type); + + if (typeConverter != null) { + final castedDatabaseValue = databaseValue.cast( + typeConverter.databaseType, + parameterElement, + ); + + parameterValue = + '_${typeConverter.name.decapitalize()}.decode($castedDatabaseValue)'; + } else if (parameterElement.type.isDefaultSqlType || parameterElement.type.isEnumType) { parameterValue = databaseValue.cast( parameterElement.type, @@ -108,18 +122,11 @@ abstract class QueryableProcessor extends Processor { field.embedConverter!.classElement, field.embedConverter!.fields, prefix: '$prefix$embedVar'); } else { - final typeConverter = [ - ...queryableTypeConverters, - field.typeConverter, - ].whereNotNull().getClosest(parameterElement.type); - - final castedDatabaseValue = databaseValue.cast( - typeConverter.databaseType, - parameterElement, + throw InvalidGenerationSourceError( + 'Column type is not supported for ${parameterElement.type}', + todo: + 'Either use a supported type https://pinchbv.github.io/floor/entities/#supported-types or supply a type converter.', ); - - parameterValue = - '_${typeConverter.name.decapitalize()}.decode($castedDatabaseValue)'; } if (parameterElement.isNamed) { From 56b57d69f9ef2f44c90b336d49c849d0622c290c Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 28 May 2024 16:08:39 +0200 Subject: [PATCH 14/20] change dart version --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7fa4ff9..25d1bfc3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 10 container: - image: dart:3.3.0 + image: dart:3.4.1 steps: - name: Check out code @@ -35,7 +35,7 @@ jobs: timeout-minutes: 10 container: - image: dart:3.3.0 + image: dart:3.4.1 steps: - name: Check out code @@ -83,7 +83,7 @@ jobs: - name: Install dependencies run: flutter packages get - working-directory: floor + working-directory: floor_common - name: Run generator run: flutter packages pub run build_runner build --delete-conflicting-outputs From 9661cd14e706bbe773c609d2fbfddb0770990158 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 28 May 2024 16:15:01 +0200 Subject: [PATCH 15/20] change dart version --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25d1bfc3..fa88a238 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 10 container: - image: dart:3.4.1 + image: dart:3.3.0 steps: - name: Check out code @@ -35,7 +35,7 @@ jobs: timeout-minutes: 10 container: - image: dart:3.4.1 + image: dart:3.3.0 steps: - name: Check out code From 470f494a722de73409c05c2c1869028ad7cdc6c2 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Tue, 28 May 2024 16:35:42 +0200 Subject: [PATCH 16/20] Add time before closing database --- example/test/main_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/example/test/main_test.dart b/example/test/main_test.dart index 836ea537..871e1aa5 100644 --- a/example/test/main_test.dart +++ b/example/test/main_test.dart @@ -14,6 +14,7 @@ void main() { }); tearDown(() async { + await Future.delayed(const Duration(milliseconds: 100)); await database.close(); }); From 8c4105cef21efd2ab9b499b25760ec1ab1a75c08 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Wed, 29 May 2024 10:00:25 +0200 Subject: [PATCH 17/20] Remove TaskStatus converter --- example/lib/database.dart | 2 +- example/lib/database.g.dart | 25 ++++++++++++++++--------- example/lib/type_converter.dart | 12 ------------ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/example/lib/database.dart b/example/lib/database.dart index c6be150b..5c7d6aa5 100644 --- a/example/lib/database.dart +++ b/example/lib/database.dart @@ -9,7 +9,7 @@ import 'package:sqflite/sqflite.dart' as sqflite; part 'database.g.dart'; -@TypeConverters([DateTimeConverter, TaskTypeConverter, TaskStatusConverter]) +@TypeConverters([DateTimeConverter, TaskTypeConverter]) @Database(version: 1, entities: [Task], embeds: [Timestamp]) abstract class FlutterDatabase extends FloorDatabase { TaskDao get taskDao; diff --git a/example/lib/database.g.dart b/example/lib/database.g.dart index d7ea5351..496effff 100644 --- a/example/lib/database.g.dart +++ b/example/lib/database.g.dart @@ -96,7 +96,7 @@ class _$FlutterDatabase extends FlutterDatabase { }, onCreate: (database, version) async { await database.execute( - 'CREATE TABLE IF NOT EXISTS `Task` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` TEXT NOT NULL, `isRead` INTEGER, `timestamp_created_at` INTEGER NOT NULL, `timestamp_updated_at` INTEGER NOT NULL, `status` TEXT, `type` TEXT)'); + 'CREATE TABLE IF NOT EXISTS `Task` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` TEXT NOT NULL, `isRead` INTEGER, `timestamp_created_at` INTEGER NOT NULL, `timestamp_updated_at` INTEGER NOT NULL, `status` INTEGER, `type` TEXT)'); await callback?.onCreate?.call(database, version); }, @@ -126,7 +126,7 @@ class _$TaskDao extends TaskDao { _dateTimeConverter.encode(item.timestamp.createdAt), 'timestamp_updated_at': _dateTimeConverter.encode(item.timestamp.updatedAt), - 'status': _taskStatusConverter.encode(item.status), + 'status': item.status?.index, 'type': _taskTypeConverter.encode(item.type) }, changeListener), @@ -142,7 +142,7 @@ class _$TaskDao extends TaskDao { _dateTimeConverter.encode(item.timestamp.createdAt), 'timestamp_updated_at': _dateTimeConverter.encode(item.timestamp.updatedAt), - 'status': _taskStatusConverter.encode(item.status), + 'status': item.status?.index, 'type': _taskTypeConverter.encode(item.type) }, changeListener), @@ -158,7 +158,7 @@ class _$TaskDao extends TaskDao { _dateTimeConverter.encode(item.timestamp.createdAt), 'timestamp_updated_at': _dateTimeConverter.encode(item.timestamp.updatedAt), - 'status': _taskStatusConverter.encode(item.status), + 'status': item.status?.index, 'type': _taskTypeConverter.encode(item.type) }, changeListener); @@ -187,7 +187,9 @@ class _$TaskDao extends TaskDao { .decode(row['timestamp_created_at'] as int), updatedAt: _dateTimeConverter .decode(row['timestamp_updated_at'] as int)), - _taskStatusConverter.decode(row['status'] as String?), + row['status'] == null + ? null + : TaskStatus.values[row['status'] as int], _taskTypeConverter.decode(row['type'] as String?)), arguments: [id]); } @@ -204,7 +206,9 @@ class _$TaskDao extends TaskDao { .decode(row['timestamp_created_at'] as int), updatedAt: _dateTimeConverter .decode(row['timestamp_updated_at'] as int)), - _taskStatusConverter.decode(row['status'] as String?), + row['status'] == null + ? null + : TaskStatus.values[row['status'] as int], _taskTypeConverter.decode(row['type'] as String?))); } @@ -220,7 +224,9 @@ class _$TaskDao extends TaskDao { .decode(row['timestamp_created_at'] as int), updatedAt: _dateTimeConverter .decode(row['timestamp_updated_at'] as int)), - _taskStatusConverter.decode(row['status'] as String?), + row['status'] == null + ? null + : TaskStatus.values[row['status'] as int], _taskTypeConverter.decode(row['type'] as String?)), queryableName: 'task', isView: false); @@ -246,7 +252,9 @@ class _$TaskDao extends TaskDao { .decode(row['timestamp_created_at'] as int), updatedAt: _dateTimeConverter .decode(row['timestamp_updated_at'] as int)), - _taskStatusConverter.decode(row['status'] as String?), + row['status'] == null + ? null + : TaskStatus.values[row['status'] as int], _taskTypeConverter.decode(row['type'] as String?)), arguments: [status.index], queryableName: 'task', @@ -298,4 +306,3 @@ class _$TaskDao extends TaskDao { // ignore_for_file: unused_element final _dateTimeConverter = DateTimeConverter(); final _taskTypeConverter = TaskTypeConverter(); -final _taskStatusConverter = TaskStatusConverter(); diff --git a/example/lib/type_converter.dart b/example/lib/type_converter.dart index 29e8e4dc..fc2a65aa 100644 --- a/example/lib/type_converter.dart +++ b/example/lib/type_converter.dart @@ -24,15 +24,3 @@ class TaskTypeConverter extends TypeConverter { return value?.name; } } - -class TaskStatusConverter extends TypeConverter { - @override - TaskStatus? decode(String? databaseValue) { - return databaseValue == null ? null : TaskStatus.values.byName(databaseValue); - } - - @override - String? encode(TaskStatus? value) { - return value?.name; - } -} From ffcd2202de7777515097a8476f220ee778406e76 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Wed, 29 May 2024 10:12:43 +0200 Subject: [PATCH 18/20] Change ToString() --- example/lib/main.dart | 2 +- example/lib/timestamp.dart | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 5c4a408f..43fa85c0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -180,7 +180,7 @@ class TaskListCell extends StatelessWidget { child: ListTile( title: Text(task.message), subtitle: Text('Status: ${task.statusTitle}'), - trailing: Text(task.timestamp.toString()), + trailing: Text(task.timestamp.format()), ), confirmDismiss: (direction) async { String? statusMessage; diff --git a/example/lib/timestamp.dart b/example/lib/timestamp.dart index 5b568c6f..76aadfa0 100644 --- a/example/lib/timestamp.dart +++ b/example/lib/timestamp.dart @@ -22,11 +22,15 @@ class Timestamp { @override int get hashCode => createdAt.hashCode ^ updatedAt.hashCode; - @override - String toString() { + String format() { final DateFormat formatter = DateFormat('yyyy-MM-dd'); final String formattedCreatedAt = formatter.format(createdAt); final String formattedUpdatedAt = formatter.format(updatedAt); - return 'createdAt: $formattedCreatedAt \n updatedAt: $formattedUpdatedAt'; + return 'Created at: $formattedCreatedAt \nUpdated at: $formattedUpdatedAt'; + } + + @override + String toString() { + return 'Timestamp(createdAt: $createdAt, updatedAt: $updatedAt)'; } } From e842d553fd6b2b57a9f0b5dd9353e40c08f96061 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Wed, 29 May 2024 10:19:17 +0200 Subject: [PATCH 19/20] Add comment --- example/test/main_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/example/test/main_test.dart b/example/test/main_test.dart index 871e1aa5..2236d773 100644 --- a/example/test/main_test.dart +++ b/example/test/main_test.dart @@ -14,6 +14,7 @@ void main() { }); tearDown(() async { + //Delay is necessary, otherwise test wil fail after completion await Future.delayed(const Duration(milliseconds: 100)); await database.close(); }); From d3d00794a26253d27ef91f22641b29e8ae780648 Mon Sep 17 00:00:00 2001 From: hendrikvanderkaaden Date: Wed, 29 May 2024 10:23:03 +0200 Subject: [PATCH 20/20] Use entity for embedded --- example/lib/timestamp.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/example/lib/timestamp.dart b/example/lib/timestamp.dart index 76aadfa0..2714383f 100644 --- a/example/lib/timestamp.dart +++ b/example/lib/timestamp.dart @@ -2,6 +2,7 @@ import 'package:floor/floor.dart'; import 'package:intl/intl.dart'; @Embed() +@Entity() class Timestamp { @ColumnInfo(name: 'created_at') final DateTime createdAt;