feat: Take @SerializableField properties into account when generating Query.parseRow
(#98)
This commit is contained in:
parent
fd5f726e2f
commit
95f425021a
6 changed files with 110 additions and 13 deletions
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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')
|
||||
|
@ -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]);
|
||||
|
@ -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,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
|
||||
);
|
|
@ -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
|
||||
);
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ class HasCarMigration extends Migration {
|
|||
table.serial('id').primaryKey();
|
||||
table.timeStamp('created_at');
|
||||
table.timeStamp('updated_at');
|
||||
table.varChar('color');
|
||||
table.integer('type').defaultsTo(0);
|
||||
},
|
||||
);
|
||||
|
@ -63,6 +64,7 @@ class HasCarQuery extends Query<HasCar, HasCarQueryWhere> {
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'color',
|
||||
'type',
|
||||
];
|
||||
return _selectedFields.isEmpty
|
||||
|
@ -95,10 +97,15 @@ class HasCarQuery extends Query<HasCar, HasCarQueryWhere> {
|
|||
fields.contains('created_at') ? mapToNullableDateTime(row[1]) : null,
|
||||
updatedAt:
|
||||
fields.contains('updated_at') ? mapToNullableDateTime(row[2]) : null,
|
||||
type: fields.contains('type')
|
||||
color: fields.contains('color')
|
||||
? row[3] == null
|
||||
? null
|
||||
: CarType.values[(row[3] as int)]
|
||||
: codeToColor((row[3] as String))
|
||||
: null,
|
||||
type: fields.contains('type')
|
||||
? row[4] == null
|
||||
? null
|
||||
: CarType.values[(row[4] as int)]
|
||||
: null,
|
||||
);
|
||||
return Optional.of(model);
|
||||
|
@ -124,6 +131,10 @@ class HasCarQueryWhere extends QueryWhere {
|
|||
query,
|
||||
'updated_at',
|
||||
),
|
||||
color = StringSqlExpressionBuilder(
|
||||
query,
|
||||
'color',
|
||||
),
|
||||
type = EnumSqlExpressionBuilder<CarType?>(
|
||||
query,
|
||||
'type',
|
||||
|
@ -136,6 +147,8 @@ class HasCarQueryWhere extends QueryWhere {
|
|||
|
||||
final DateTimeSqlExpressionBuilder updatedAt;
|
||||
|
||||
final StringSqlExpressionBuilder color;
|
||||
|
||||
final EnumSqlExpressionBuilder<CarType?> type;
|
||||
|
||||
@override
|
||||
|
@ -144,6 +157,7 @@ class HasCarQueryWhere extends QueryWhere {
|
|||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
color,
|
||||
type,
|
||||
];
|
||||
}
|
||||
|
@ -170,6 +184,11 @@ class HasCarQueryValues extends MapQueryValues {
|
|||
}
|
||||
|
||||
set updatedAt(DateTime? value) => values['updated_at'] = value;
|
||||
Color? get color {
|
||||
return codeToColor((values['color'] as String));
|
||||
}
|
||||
|
||||
set color(Color? value) => values['color'] = colorToCode(value);
|
||||
CarType? get type {
|
||||
return CarType.values[(values['type'] as int)];
|
||||
}
|
||||
|
@ -178,6 +197,7 @@ class HasCarQueryValues extends MapQueryValues {
|
|||
void copyFrom(HasCar model) {
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
color = model.color;
|
||||
type = model.type;
|
||||
}
|
||||
}
|
||||
|
@ -192,6 +212,7 @@ class HasCar extends _HasCar {
|
|||
this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.color,
|
||||
this.type = CarType.sedan,
|
||||
});
|
||||
|
||||
|
@ -207,6 +228,9 @@ class HasCar extends _HasCar {
|
|||
@override
|
||||
DateTime? updatedAt;
|
||||
|
||||
@override
|
||||
Color? color;
|
||||
|
||||
@override
|
||||
CarType? type;
|
||||
|
||||
|
@ -214,12 +238,14 @@ class HasCar extends _HasCar {
|
|||
String? id,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
Color? color,
|
||||
CarType? type,
|
||||
}) {
|
||||
return HasCar(
|
||||
id: id ?? this.id,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
color: color ?? this.color,
|
||||
type: type ?? this.type);
|
||||
}
|
||||
|
||||
|
@ -229,6 +255,7 @@ class HasCar extends _HasCar {
|
|||
other.id == id &&
|
||||
other.createdAt == createdAt &&
|
||||
other.updatedAt == updatedAt &&
|
||||
other.color == color &&
|
||||
other.type == type;
|
||||
}
|
||||
|
||||
|
@ -238,13 +265,14 @@ class HasCar extends _HasCar {
|
|||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
color,
|
||||
type,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HasCar(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, type=$type)';
|
||||
return 'HasCar(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, color=$color, type=$type)';
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
@ -296,6 +324,7 @@ class HasCarSerializer extends Codec<HasCar, Map> {
|
|||
? (map['updated_at'] as DateTime)
|
||||
: DateTime.parse(map['updated_at'].toString()))
|
||||
: null,
|
||||
color: codeToColor(map['color']),
|
||||
type: map['type'] as CarType? ?? CarType.sedan);
|
||||
}
|
||||
|
||||
|
@ -307,6 +336,7 @@ class HasCarSerializer extends Codec<HasCar, Map> {
|
|||
'id': model.id,
|
||||
'created_at': model.createdAt?.toIso8601String(),
|
||||
'updated_at': model.updatedAt?.toIso8601String(),
|
||||
'color': colorToCode(model.color),
|
||||
'type': model.type
|
||||
};
|
||||
}
|
||||
|
@ -317,6 +347,7 @@ abstract class HasCarFields {
|
|||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
color,
|
||||
type,
|
||||
];
|
||||
|
||||
|
@ -326,5 +357,7 @@ abstract class HasCarFields {
|
|||
|
||||
static const String updatedAt = 'updated_at';
|
||||
|
||||
static const String color = 'color';
|
||||
|
||||
static const String type = 'type';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue