Default values
This commit is contained in:
parent
7db3ec1a36
commit
133b6a3c41
11 changed files with 173 additions and 10 deletions
|
@ -67,6 +67,7 @@ part 'book.g.dart';
|
|||
abstract class _Book extends Model {
|
||||
String get author;
|
||||
|
||||
@DefaultValue('[Untitled]')
|
||||
String get title;
|
||||
|
||||
String get description;
|
||||
|
|
|
@ -27,6 +27,7 @@ targets:
|
|||
_book:
|
||||
sources:
|
||||
- "test/models/book.dart"
|
||||
- "test/models/goat.dart"
|
||||
- "test/models/game_pad_button.dart"
|
||||
- "test/models/with_enum.dart"
|
||||
_typescript_definition:
|
||||
|
|
|
@ -2,7 +2,7 @@ library angel_serialize_generator;
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:analyzer/dart/constant/value.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:angel_model/angel_model.dart';
|
||||
|
@ -48,6 +48,32 @@ TypeReference convertTypeReference(DartType t) {
|
|||
});
|
||||
}
|
||||
|
||||
String dartObjectToString(DartObject v) {
|
||||
if (v.isNull) return 'null';
|
||||
if (v.toBoolValue() != null) return v.toBoolValue().toString();
|
||||
if (v.toIntValue() != null) return v.toIntValue().toString();
|
||||
if (v.toDoubleValue() != null) return v.toDoubleValue().toString();
|
||||
if (v.toSymbolValue() != null) return '#' + v.toSymbolValue();
|
||||
if (v.toTypeValue() != null) return v.toTypeValue().name;
|
||||
if (v.toListValue() != null)
|
||||
return 'const [' + v.toListValue().map(dartObjectToString).join(', ') + ']';
|
||||
if (v.toMapValue() != null) {
|
||||
return 'const {' +
|
||||
v.toMapValue().entries.map((entry) {
|
||||
var k = dartObjectToString(entry.key);
|
||||
var v = dartObjectToString(entry.value);
|
||||
return '$k: $v';
|
||||
}).join(', ') +
|
||||
'}';
|
||||
}
|
||||
if (v.toStringValue() != null) {
|
||||
return literalString(v.toStringValue())
|
||||
.accept(new DartEmitter())
|
||||
.toString();
|
||||
}
|
||||
throw new ArgumentError(v.toString());
|
||||
}
|
||||
|
||||
/// Determines if a type supports `package:angel_serialize`.
|
||||
bool isModelClass(DartType t) {
|
||||
if (t == null) return false;
|
||||
|
|
|
@ -67,6 +67,14 @@ Future<BuildContext> buildContext(
|
|||
);
|
||||
}
|
||||
|
||||
// Check for @DefaultValue()
|
||||
var defAnn =
|
||||
const TypeChecker.fromRuntime(DefaultValue).firstAnnotationOf(el);
|
||||
if (defAnn != null) {
|
||||
var rev = new ConstantReader(defAnn).revive().positionalArguments[0];
|
||||
ctx.defaults[field.name] = rev;
|
||||
}
|
||||
|
||||
// Check for alias
|
||||
Alias alias;
|
||||
var aliasAnn = aliasTypeChecker.firstAnnotationOf(el);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:analyzer/dart/constant/value.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
|
@ -15,6 +16,9 @@ class BuildContext {
|
|||
/// A map of field names to resolved names from `@Alias()` declarations.
|
||||
final Map<String, String> aliases = {};
|
||||
|
||||
/// A map of field names to their default values.
|
||||
final Map<String, DartObject> defaults = {};
|
||||
|
||||
/// A map of fields that have been marked as to be excluded from serialization.
|
||||
final Map<String, Exclude> excluded = {};
|
||||
|
||||
|
|
|
@ -96,6 +96,12 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
? 'List'
|
||||
: 'Map';
|
||||
var defaultValue = typeName == 'List' ? '[]' : '{}';
|
||||
var existingDefault = ctx.defaults[field.name];
|
||||
|
||||
if (existingDefault != null) {
|
||||
defaultValue = dartObjectToString(existingDefault);
|
||||
}
|
||||
|
||||
constructor.initializers.add(new Code('''
|
||||
this.${field.name} =
|
||||
new $typeName.unmodifiable(${field.name} ?? $defaultValue)'''));
|
||||
|
@ -109,6 +115,12 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
..name = field.name
|
||||
..named = true;
|
||||
|
||||
var existingDefault = ctx.defaults[field.name];
|
||||
|
||||
if (existingDefault != null) {
|
||||
b.defaultTo = new Code(dartObjectToString(existingDefault));
|
||||
}
|
||||
|
||||
if (!isListOrMapType(field.type))
|
||||
b.toThis = true;
|
||||
else {
|
||||
|
|
|
@ -203,18 +203,27 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
String deserializedRepresentation =
|
||||
"map['$alias'] as ${typeToString(field.type)}";
|
||||
|
||||
var defaultValue = 'null';
|
||||
var existingDefault = ctx.defaults[field.name];
|
||||
|
||||
if (existingDefault != null) {
|
||||
defaultValue = dartObjectToString(existingDefault);
|
||||
deserializedRepresentation =
|
||||
'$deserializedRepresentation ?? $defaultValue';
|
||||
}
|
||||
|
||||
// Deserialize dates
|
||||
if (dateTimeTypeChecker.isAssignableFromType(field.type))
|
||||
deserializedRepresentation = "map['$alias'] != null ? "
|
||||
"(map['$alias'] is DateTime ? (map['$alias'] as DateTime) : DateTime.parse(map['$alias'].toString()))"
|
||||
" : null";
|
||||
" : $defaultValue";
|
||||
|
||||
// Serialize model classes via `XSerializer.toMap`
|
||||
else if (isModelClass(field.type)) {
|
||||
var rc = new ReCase(field.type.name);
|
||||
deserializedRepresentation = "map['$alias'] != null"
|
||||
" ? ${rc.pascalCase}Serializer.fromMap(map['$alias'] as Map)"
|
||||
" : null";
|
||||
" : $defaultValue";
|
||||
} else if (field.type is InterfaceType) {
|
||||
var t = field.type as InterfaceType;
|
||||
|
||||
|
@ -224,7 +233,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
" ? new List.unmodifiable(((map['$alias'] as Iterable)"
|
||||
".where((x) => x is Map) as Iterable<Map>)"
|
||||
".map(${rc.pascalCase}Serializer.fromMap))"
|
||||
" : null";
|
||||
" : $defaultValue";
|
||||
} else if (isMapToModelType(t)) {
|
||||
var rc = new ReCase(t.typeArguments[1].name);
|
||||
deserializedRepresentation = '''
|
||||
|
@ -233,7 +242,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
return out..[key] = ${rc.pascalCase}Serializer
|
||||
.fromMap(((map['$alias'] as Map)[key]) as Map);
|
||||
}))
|
||||
: null
|
||||
: $defaultValue
|
||||
''';
|
||||
} else if (t.element.isEnum) {
|
||||
deserializedRepresentation = '''
|
||||
|
@ -243,7 +252,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
(
|
||||
map['$alias'] is int
|
||||
? ${t.name}.values[map['$alias'] as int]
|
||||
: null
|
||||
: $defaultValue
|
||||
)
|
||||
''';
|
||||
} else if (const TypeChecker.fromRuntime(List)
|
||||
|
@ -254,7 +263,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
deserializedRepresentation = '''
|
||||
map['$alias'] is Iterable
|
||||
? (map['$alias'] as Iterable).cast<$arg>().toList()
|
||||
: null
|
||||
: $defaultValue
|
||||
''';
|
||||
} else if (const TypeChecker.fromRuntime(Map)
|
||||
.isAssignableFromType(t) &&
|
||||
|
@ -266,7 +275,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
deserializedRepresentation = '''
|
||||
map['$alias'] is Map
|
||||
? (map['$alias'] as Map).cast<$key, $value>()
|
||||
: null
|
||||
: $defaultValue
|
||||
''';
|
||||
} else if (const TypeChecker.fromRuntime(Uint8List)
|
||||
.isAssignableFromType(t)) {
|
||||
|
@ -281,7 +290,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
(
|
||||
map['$alias'] is String
|
||||
? new Uint8List.fromList(base64.decode(map['$alias'] as String))
|
||||
: null
|
||||
: $defaultValue
|
||||
)
|
||||
)
|
||||
''';
|
||||
|
|
24
angel_serialize_generator/test/default_value_test.dart
Normal file
24
angel_serialize_generator/test/default_value_test.dart
Normal file
|
@ -0,0 +1,24 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'models/goat.dart';
|
||||
|
||||
void main() {
|
||||
group('constructor', () {
|
||||
test('int default', () {
|
||||
expect(Goat().integer, 34);
|
||||
});
|
||||
|
||||
test('list default', () {
|
||||
expect(Goat().list, [34, 35]);
|
||||
});
|
||||
});
|
||||
|
||||
group('from map', () {
|
||||
test('int default', () {
|
||||
expect(GoatSerializer.fromMap({}).integer, 34);
|
||||
});
|
||||
|
||||
test('list default', () {
|
||||
expect(GoatSerializer.fromMap({}).list, [34, 35]);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -3,7 +3,6 @@ import 'package:collection/collection.dart';
|
|||
import 'game_pad_button.dart';
|
||||
part 'game_pad.g.dart';
|
||||
|
||||
|
||||
@Serializable(autoIdAndDateFields: false)
|
||||
class _Gamepad {
|
||||
List<GamepadButton> buttons;
|
||||
|
|
12
angel_serialize_generator/test/models/goat.dart
Normal file
12
angel_serialize_generator/test/models/goat.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
part 'goat.g.dart';
|
||||
|
||||
@Serializable(autoIdAndDateFields: false)
|
||||
abstract class _Goat {
|
||||
@DefaultValue(34)
|
||||
int get integer;
|
||||
|
||||
@DefaultValue([34, 35])
|
||||
List<int> get list;
|
||||
}
|
67
angel_serialize_generator/test/models/goat.g.dart
Normal file
67
angel_serialize_generator/test/models/goat.g.dart
Normal file
|
@ -0,0 +1,67 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'goat.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonModelGenerator
|
||||
// **************************************************************************
|
||||
|
||||
@generatedSerializable
|
||||
class Goat implements _Goat {
|
||||
const Goat({this.integer: 34, List<int> this.list: const [34, 35]});
|
||||
|
||||
@override
|
||||
final int integer;
|
||||
|
||||
@override
|
||||
final List<int> list;
|
||||
|
||||
Goat copyWith({int integer, List<int> list}) {
|
||||
return new Goat(integer: integer ?? this.integer, list: list ?? this.list);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _Goat &&
|
||||
other.integer == integer &&
|
||||
const ListEquality<int>(const DefaultEquality<int>())
|
||||
.equals(other.list, list);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([integer, list]);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return GoatSerializer.toMap(this);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// SerializerGenerator
|
||||
// **************************************************************************
|
||||
|
||||
abstract class GoatSerializer {
|
||||
static Goat fromMap(Map map) {
|
||||
return new Goat(
|
||||
integer: map['integer'] as int ?? 34,
|
||||
list: map['list'] is Iterable
|
||||
? (map['list'] as Iterable).cast<int>().toList()
|
||||
: const [34, 35]);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> toMap(_Goat model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {'integer': model.integer, 'list': model.list};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GoatFields {
|
||||
static const List<String> allFields = const <String>[integer, list];
|
||||
|
||||
static const String integer = 'integer';
|
||||
|
||||
static const String list = 'list';
|
||||
}
|
Loading…
Reference in a new issue