Skip to content

Commit

Permalink
feat: Take @SerializableField properties into account when generating…
Browse files Browse the repository at this point in the history
… `Query.parseRow` (dart-backend#98)
  • Loading branch information
BenVercammen committed Mar 22, 2023
1 parent fd5f726 commit 95f4250
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 13 deletions.
55 changes: 46 additions & 9 deletions packages/orm/angel_orm_generator/lib/src/orm_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:angel3_serialize/angel3_serialize.dart';
import 'package:angel3_serialize_generator/angel3_serialize_generator.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart' hide LibraryBuilder;
Expand Down Expand Up @@ -277,11 +278,8 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
* EnumType.values[(row[3] as int)] : null,
*/
var isNull = expr.equalTo(literalNull);

Reference enumType =
convertTypeReference(fType, ignoreNullabilityCheck: true);
expr = isNull.conditional(literalNull,
enumType.property('values').index(expr.asA(refer('int'))));
final parseExpression = _deserializeEnumExpression(field, expr);
expr = isNull.conditional(literalNull, parseExpression);
} else if (fType.isDartCoreBool) {
// Generated Code: mapToBool(row[i])
expr = refer('mapToBool').call([expr]);
Expand Down Expand Up @@ -892,9 +890,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var value = refer('values').index(literalString(name!));

if (fType is InterfaceType && fType.element is EnumElement) {
var asInt = value.asA(refer('int'));
var t = convertTypeReference(fType, ignoreNullabilityCheck: true);
value = t.property('values').index(asInt);
value = _deserializeEnumExpression(field, value);
} else if (const TypeChecker.fromRuntime(List)
.isAssignableFromType(fType)) {
value = refer('json')
Expand Down Expand Up @@ -926,7 +922,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
Expression value = refer('value');

if (fType is InterfaceType && fType.element is EnumElement) {
value = CodeExpression(Code('value?.index'));
value = _serializeEnumExpression(field, value);
} else if (const TypeChecker.fromRuntime(List)
.isAssignableFromType(fType)) {
value = refer('json').property('encode').call([value]);
Expand Down Expand Up @@ -1005,4 +1001,45 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}));
});
}

/// Retrieve the [Expression] to parse a serialized enumeration field.
/// Takes into account the [SerializableField] properties.
/// Defaults to `enum.values[index as int]`
Expression _deserializeEnumExpression(FieldElement field, Expression expr) {
Reference enumType =
convertTypeReference(field.type, ignoreNullabilityCheck: true);
const TypeChecker serializableFieldTypeChecker =
TypeChecker.fromRuntime(SerializableField);
final annotation = serializableFieldTypeChecker.firstAnnotationOf(field);
Expression? parseExpr;
if (null != annotation) {
final deserializer = annotation.getField('deserializer')?.toSymbolValue();
if (null != deserializer) {
var type = 'int';
final serializesTo = annotation.getField('serializesTo')?.toTypeValue();
if (null != serializesTo) {
type = serializesTo.element!.displayName;
}
parseExpr = Reference(deserializer).expression([expr.asA(refer(type))]);
}
}
return parseExpr ??
enumType.property('values').index(expr.asA(refer('int')));
}

/// Retrieve the [Expression] to serialize the enumeration field.
/// Takes into account the [SerializableField] properties.
Expression _serializeEnumExpression(FieldElement field, Expression expr) {
const TypeChecker serializableFieldTypeChecker =
TypeChecker.fromRuntime(SerializableField);
final annotation = serializableFieldTypeChecker.firstAnnotationOf(field);
Expression? parseExpr;
if (null != annotation) {
final serializer = annotation.getField('serializer')?.toSymbolValue();
if (null != serializer) {
parseExpr = Reference(serializer).expression([expr]);
}
}
return parseExpr ?? CodeExpression(Code('value?.index'));
}
}
1 change: 1 addition & 0 deletions packages/orm/angel_orm_mysql/test/migrations/has_car.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
CREATE TABLE IF NOT EXISTS has_cars (
id serial PRIMARY KEY,
type int not null,
color varchar(1),
created_at datetime,
updated_at datetime
);
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
CREATE TEMPORARY TABLE "has_cars" (
id serial PRIMARY KEY,
type int not null,
color varchar(1),
created_at timestamp,
updated_at timestamp
);
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ void enumAndNestedTests(FutureOr<QueryExecutor> Function() createExecutor,
});

test('insert', () async {
var query = HasCarQuery()..values.type = CarType.sedan;
var query = HasCarQuery()
..values.type = CarType.sedan
..values.color = Color.red;
var resultOpt = await (query.insert(executor));
expect(resultOpt.isPresent, true);
resultOpt.ifPresent((result) {
expect(result.type, CarType.sedan);
expect(result.color, Color.red);
});
});

Expand Down
22 changes: 22 additions & 0 deletions packages/orm/angel_orm_test/lib/src/models/has_car.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ part 'has_car.g.dart';

enum CarType { sedan, suv, atv }

Color? codeToColor(String? code) => code == null
? null
: Color.values.firstWhere((color) => color.code == code);

String? colorToCode(Color? color) => color?.code;

enum Color {
red('R'), green('G'), blue('B');

const Color(this.code);

final String code;
}

@orm
@serializable
abstract class _HasCar extends Model {
Expand All @@ -21,4 +35,12 @@ abstract class _HasCar extends Model {

@SerializableField(isNullable: false, defaultValue: CarType.sedan)
CarType? get type;

@SerializableField(
serializesTo: String,
serializer: #colorToCode,
deserializer: #codeToColor,
)
@Column(type: ColumnType.varChar, length: 1)
Color? color;
}
39 changes: 36 additions & 3 deletions packages/orm/angel_orm_test/lib/src/models/has_car.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 95f4250

Please sign in to comment.