2.0.9 - add enums
This commit is contained in:
parent
403e2e600e
commit
bdb7b30bc4
14 changed files with 217 additions and 36 deletions
7
.idea/runConfigurations/tests_in_enum_test_dart.xml
Normal file
7
.idea/runConfigurations/tests_in_enum_test_dart.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="tests in enum_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/angel_serialize_generator/test/enum_test.dart" />
|
||||||
|
<option name="testRunnerOptions" value="-j4" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
11
README.md
11
README.md
|
@ -61,9 +61,20 @@ part 'book.g.dart';
|
||||||
@serializable
|
@serializable
|
||||||
abstract class _Book extends Model {
|
abstract class _Book extends Model {
|
||||||
String get author;
|
String get author;
|
||||||
|
|
||||||
String get title;
|
String get title;
|
||||||
|
|
||||||
String get description;
|
String get description;
|
||||||
|
|
||||||
int get pageCount;
|
int get pageCount;
|
||||||
|
|
||||||
|
BookType get type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// It even supports enums!
|
||||||
|
enum BookType {
|
||||||
|
fiction,
|
||||||
|
nonFiction
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
# 2.0.9
|
||||||
|
* Now supports de/serialization of `enum` types.
|
||||||
|
* Generate `const` constructors when possible.
|
||||||
|
* Remove `whereType`, perform manual coercion.
|
||||||
|
|
||||||
# 2.0.8
|
# 2.0.8
|
||||||
* Generate a `fromMap` with typecasting, for Dart 2's sake.
|
* Generate a `fromMap` with typecasting, for Dart 2's sake.
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ targets:
|
||||||
_book:
|
_book:
|
||||||
sources:
|
sources:
|
||||||
- "test/models/book.dart"
|
- "test/models/book.dart"
|
||||||
|
- "test/models/with_enum.dart"
|
||||||
_typescript_definition:
|
_typescript_definition:
|
||||||
sources:
|
sources:
|
||||||
- "lib/*.dart"
|
- "lib/*.dart"
|
||||||
|
|
|
@ -3,6 +3,7 @@ library angel_serialize_generator;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
import 'package:analyzer/dart/element/type.dart';
|
import 'package:analyzer/dart/element/type.dart';
|
||||||
|
import 'package:angel_model/angel_model.dart';
|
||||||
import 'package:angel_serialize/angel_serialize.dart';
|
import 'package:angel_serialize/angel_serialize.dart';
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
import 'package:code_buffer/code_buffer.dart';
|
import 'package:code_buffer/code_buffer.dart';
|
||||||
|
@ -66,6 +67,14 @@ bool isListOrMapType(DartType t) {
|
||||||
const TypeChecker.fromRuntime(Map).isAssignableFromType(t);
|
const TypeChecker.fromRuntime(Map).isAssignableFromType(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isEnumType(DartType t) {
|
||||||
|
if (t is InterfaceType) {
|
||||||
|
return t.element.isEnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines if a [DartType] is a `List` with the first type argument being a `Model`.
|
/// Determines if a [DartType] is a `List` with the first type argument being a `Model`.
|
||||||
bool isListModelType(InterfaceType t) {
|
bool isListModelType(InterfaceType t) {
|
||||||
return const TypeChecker.fromRuntime(List).isAssignableFromType(t) &&
|
return const TypeChecker.fromRuntime(List).isAssignableFromType(t) &&
|
||||||
|
@ -80,6 +89,8 @@ bool isMapToModelType(InterfaceType t) {
|
||||||
isModelClass(t.typeArguments[1]);
|
isModelClass(t.typeArguments[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAssignableToModel(DartType type) => const TypeChecker.fromRuntime(Model).isAssignableFromType(type);
|
||||||
|
|
||||||
/// Compute a [String] representation of a [type].
|
/// Compute a [String] representation of a [type].
|
||||||
String typeToString(DartType type) {
|
String typeToString(DartType type) {
|
||||||
if (type is InterfaceType) {
|
if (type is InterfaceType) {
|
||||||
|
|
|
@ -26,9 +26,13 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
void generateClass(
|
void generateClass(
|
||||||
BuildContext ctx, LibraryBuilder file, ConstantReader annotation) {
|
BuildContext ctx, LibraryBuilder file, ConstantReader annotation) {
|
||||||
file.body.add(new Class((clazz) {
|
file.body.add(new Class((clazz) {
|
||||||
clazz
|
clazz..name = ctx.modelClassNameRecase.pascalCase;
|
||||||
..name = ctx.modelClassNameRecase.pascalCase
|
|
||||||
..extend = new Reference(ctx.originalClassName);
|
if (shouldBeConstant(ctx)) {
|
||||||
|
clazz.implements.add(new Reference(ctx.originalClassName));
|
||||||
|
} else {
|
||||||
|
clazz.extend = new Reference(ctx.originalClassName);
|
||||||
|
}
|
||||||
|
|
||||||
//if (ctx.importsPackageMeta)
|
//if (ctx.importsPackageMeta)
|
||||||
// clazz.annotations.add(new CodeExpression(new Code('immutable')));
|
// clazz.annotations.add(new CodeExpression(new Code('immutable')));
|
||||||
|
@ -61,11 +65,19 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool shouldBeConstant(BuildContext ctx) {
|
||||||
|
// Check if all fields are without a getter
|
||||||
|
return !isAssignableToModel(ctx.clazz.type) &&
|
||||||
|
ctx.clazz.fields.every((f) => f.getter == null || f.setter == null);
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a constructor with named parameters.
|
/// Generate a constructor with named parameters.
|
||||||
void generateConstructor(
|
void generateConstructor(
|
||||||
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
|
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
|
||||||
clazz.constructors.add(new Constructor((constructor) {
|
clazz.constructors.add(new Constructor((constructor) {
|
||||||
// Add all `super` params
|
// Add all `super` params
|
||||||
|
constructor.constant = ctx.clazz.unnamedConstructor?.isConst == true ||
|
||||||
|
shouldBeConstant(ctx);
|
||||||
|
|
||||||
for (var param in ctx.constructorParameters) {
|
for (var param in ctx.constructorParameters) {
|
||||||
constructor.requiredParameters.add(new Parameter((b) => b
|
constructor.requiredParameters.add(new Parameter((b) => b
|
||||||
|
@ -156,7 +168,7 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
}
|
}
|
||||||
|
|
||||||
static String generateEquality(DartType type, [bool nullable = false]) {
|
static String generateEquality(DartType type, [bool nullable = false]) {
|
||||||
//if (type is! InterfaceType) return 'const DefaultEquality()';
|
//if (type is! InterfaceType) return 'const DefaultEquality()';
|
||||||
var it = type as InterfaceType;
|
var it = type as InterfaceType;
|
||||||
if (const TypeChecker.fromRuntime(List).isAssignableFromType(type)) {
|
if (const TypeChecker.fromRuntime(List).isAssignableFromType(type)) {
|
||||||
if (it.typeParameters.length == 1) {
|
if (it.typeParameters.length == 1) {
|
||||||
|
|
|
@ -112,6 +112,12 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
return map..[key] = ${rc.pascalCase}Serializer.toMap(model.${field
|
return map..[key] = ${rc.pascalCase}Serializer.toMap(model.${field
|
||||||
.name}[key]);
|
.name}[key]);
|
||||||
})''';
|
})''';
|
||||||
|
} else if (t.element.isEnum) {
|
||||||
|
serializedRepresentation = '''
|
||||||
|
model.${field.name} == null ?
|
||||||
|
null
|
||||||
|
: ${t.name}.values.indexOf(model.${field.name})
|
||||||
|
''';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,19 +199,31 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
if (isListModelType(t)) {
|
if (isListModelType(t)) {
|
||||||
var rc = new ReCase(t.typeArguments[0].name);
|
var rc = new ReCase(t.typeArguments[0].name);
|
||||||
deserializedRepresentation = "map['$alias'] is Iterable"
|
deserializedRepresentation = "map['$alias'] is Iterable"
|
||||||
" ? new List.unmodifiable((map['$alias'] as Iterable).whereType<Map>().map(${rc
|
" ? new List.unmodifiable(((map['$alias'] as Iterable)"
|
||||||
.pascalCase}Serializer.fromMap))"
|
".where((x) => x is Map) as Iterable<Map>)"
|
||||||
|
".map(${rc.pascalCase}Serializer.fromMap))"
|
||||||
" : null";
|
" : null";
|
||||||
} else if (isMapToModelType(t)) {
|
} else if (isMapToModelType(t)) {
|
||||||
var rc = new ReCase(t.typeArguments[1].name);
|
var rc = new ReCase(t.typeArguments[1].name);
|
||||||
deserializedRepresentation = '''
|
deserializedRepresentation = '''
|
||||||
map['$alias'] is Map
|
map['$alias'] is Map
|
||||||
? new Map.unmodifiable((map['$alias'] as Map).keys.fold({}, (out, key) {
|
? new Map.unmodifiable((map['$alias'] as Map).keys.fold({}, (out, key) {
|
||||||
return out..[key] = ${rc
|
return out..[key] = ${rc.pascalCase}Serializer
|
||||||
.pascalCase}Serializer.fromMap((map['$alias'] as Map)[key]);
|
.fromMap((map['$alias'] as Map)[key]);
|
||||||
}))
|
}))
|
||||||
: null
|
: null
|
||||||
''';
|
''';
|
||||||
|
} else if (t.element.isEnum) {
|
||||||
|
deserializedRepresentation = '''
|
||||||
|
map['$alias'] is ${t.name}
|
||||||
|
? map['$alias']
|
||||||
|
:
|
||||||
|
(
|
||||||
|
map['$alias'] is int
|
||||||
|
? ${t.name}.values[map['$alias']]
|
||||||
|
: null
|
||||||
|
)
|
||||||
|
''';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,5 +16,5 @@ dependencies:
|
||||||
recase: ^1.0.0
|
recase: ^1.0.0
|
||||||
source_gen: ^0.7.0
|
source_gen: ^0.7.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^0.9.0
|
build_runner: ^0.8.0
|
||||||
test: ^1.0.0
|
test: ^1.0.0
|
48
angel_serialize_generator/test/enum_test.dart
Normal file
48
angel_serialize_generator/test/enum_test.dart
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'models/with_enum.dart';
|
||||||
|
|
||||||
|
const WithEnum aWithEnum = const WithEnum(type: WithEnumType.a);
|
||||||
|
const WithEnum aWithEnum2 = const WithEnum(type: WithEnumType.a);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('enum serializes to int', () {
|
||||||
|
var w = new WithEnum(type: WithEnumType.b).toJson();
|
||||||
|
expect(w[WithEnumFields.type], WithEnumType.values.indexOf(WithEnumType.b));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('enum serializes null if null', () {
|
||||||
|
var w = new WithEnum(type: null).toJson();
|
||||||
|
expect(w[WithEnumFields.type], null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('enum deserializes to null from null', () {
|
||||||
|
var map = {WithEnumFields.type: null};
|
||||||
|
var w = WithEnumSerializer.fromMap(map);
|
||||||
|
expect(w.type, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('enum deserializes from int', () {
|
||||||
|
var map = {
|
||||||
|
WithEnumFields.type: WithEnumType.values.indexOf(WithEnumType.b)
|
||||||
|
};
|
||||||
|
var w = WithEnumSerializer.fromMap(map);
|
||||||
|
expect(w.type, WithEnumType.b);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('enum deserializes from value', () {
|
||||||
|
var map = {WithEnumFields.type: WithEnumType.c};
|
||||||
|
var w = WithEnumSerializer.fromMap(map);
|
||||||
|
expect(w.type, WithEnumType.c);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('equality', () {
|
||||||
|
expect(
|
||||||
|
new WithEnum(type: WithEnumType.a), new WithEnum(type: WithEnumType.a));
|
||||||
|
expect(new WithEnum(type: WithEnumType.a),
|
||||||
|
isNot(new WithEnum(type: WithEnumType.b)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('const', () {
|
||||||
|
expect(identical(aWithEnum, aWithEnum2), true);
|
||||||
|
});
|
||||||
|
}
|
|
@ -17,24 +17,26 @@ abstract class AuthorSerializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Author(
|
return new Author(
|
||||||
id: map['id'],
|
id: map['id'] as String,
|
||||||
name: map['name'],
|
name: map['name'] as String,
|
||||||
age: map['age'],
|
age: map['age'] as int,
|
||||||
books: map['books'] is Iterable
|
books: map['books'] is Iterable
|
||||||
? new List.unmodifiable(map['books'].map(BookSerializer.fromMap))
|
? new List.unmodifiable(((map['books'] as Iterable)
|
||||||
|
.where((x) => x is Map) as Iterable<Map>)
|
||||||
|
.map(BookSerializer.fromMap))
|
||||||
: null,
|
: null,
|
||||||
newestBook: map['newest_book'] != null
|
newestBook: map['newest_book'] != null
|
||||||
? BookSerializer.fromMap(map['newest_book'])
|
? BookSerializer.fromMap(map['newest_book'])
|
||||||
: null,
|
: null,
|
||||||
obscured: map['obscured'],
|
obscured: map['obscured'] as String,
|
||||||
createdAt: map['created_at'] != null
|
createdAt: map['created_at'] != null
|
||||||
? (map['created_at'] is DateTime
|
? (map['created_at'] is DateTime
|
||||||
? map['created_at']
|
? (map['created_at'] as DateTime)
|
||||||
: DateTime.parse(map['created_at']))
|
: DateTime.parse(map['created_at']))
|
||||||
: null,
|
: null,
|
||||||
updatedAt: map['updated_at'] != null
|
updatedAt: map['updated_at'] != null
|
||||||
? (map['updated_at'] is DateTime
|
? (map['updated_at'] is DateTime
|
||||||
? map['updated_at']
|
? (map['updated_at'] as DateTime)
|
||||||
: DateTime.parse(map['updated_at']))
|
: DateTime.parse(map['updated_at']))
|
||||||
: null);
|
: null);
|
||||||
}
|
}
|
||||||
|
@ -83,21 +85,23 @@ abstract class AuthorFields {
|
||||||
abstract class LibrarySerializer {
|
abstract class LibrarySerializer {
|
||||||
static Library fromMap(Map map) {
|
static Library fromMap(Map map) {
|
||||||
return new Library(
|
return new Library(
|
||||||
id: map['id'],
|
id: map['id'] as String,
|
||||||
collection: map['collection'] is Map
|
collection: map['collection'] is Map
|
||||||
? new Map.unmodifiable(map['collection'].keys.fold({}, (out, key) {
|
? new Map.unmodifiable(
|
||||||
|
(map['collection'] as Map).keys.fold({}, (out, key) {
|
||||||
return out
|
return out
|
||||||
..[key] = BookSerializer.fromMap(map['collection'][key]);
|
..[key] =
|
||||||
|
BookSerializer.fromMap((map['collection'] as Map)[key]);
|
||||||
}))
|
}))
|
||||||
: null,
|
: null,
|
||||||
createdAt: map['created_at'] != null
|
createdAt: map['created_at'] != null
|
||||||
? (map['created_at'] is DateTime
|
? (map['created_at'] is DateTime
|
||||||
? map['created_at']
|
? (map['created_at'] as DateTime)
|
||||||
: DateTime.parse(map['created_at']))
|
: DateTime.parse(map['created_at']))
|
||||||
: null,
|
: null,
|
||||||
updatedAt: map['updated_at'] != null
|
updatedAt: map['updated_at'] != null
|
||||||
? (map['updated_at'] is DateTime
|
? (map['updated_at'] is DateTime
|
||||||
? map['updated_at']
|
? (map['updated_at'] as DateTime)
|
||||||
: DateTime.parse(map['updated_at']))
|
: DateTime.parse(map['updated_at']))
|
||||||
: null);
|
: null);
|
||||||
}
|
}
|
||||||
|
@ -131,18 +135,18 @@ abstract class BookmarkSerializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Bookmark(book,
|
return new Bookmark(book,
|
||||||
id: map['id'],
|
id: map['id'] as String,
|
||||||
history: map['history'],
|
history: map['history'] as List<int>,
|
||||||
page: map['page'],
|
page: map['page'] as int,
|
||||||
comment: map['comment'],
|
comment: map['comment'] as String,
|
||||||
createdAt: map['created_at'] != null
|
createdAt: map['created_at'] != null
|
||||||
? (map['created_at'] is DateTime
|
? (map['created_at'] is DateTime
|
||||||
? map['created_at']
|
? (map['created_at'] as DateTime)
|
||||||
: DateTime.parse(map['created_at']))
|
: DateTime.parse(map['created_at']))
|
||||||
: null,
|
: null,
|
||||||
updatedAt: map['updated_at'] != null
|
updatedAt: map['updated_at'] != null
|
||||||
? (map['updated_at'] is DateTime
|
? (map['updated_at'] is DateTime
|
||||||
? map['updated_at']
|
? (map['updated_at'] as DateTime)
|
||||||
: DateTime.parse(map['updated_at']))
|
: DateTime.parse(map['updated_at']))
|
||||||
: null);
|
: null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,21 @@ part of angel_serialize.test.models.book;
|
||||||
abstract class BookSerializer {
|
abstract class BookSerializer {
|
||||||
static Book fromMap(Map map) {
|
static Book fromMap(Map map) {
|
||||||
return new Book(
|
return new Book(
|
||||||
id: map['id'],
|
id: map['id'] as String,
|
||||||
author: map['author'],
|
author: map['author'] as String,
|
||||||
title: map['title'],
|
title: map['title'] as String,
|
||||||
description: map['description'],
|
description: map['description'] as String,
|
||||||
pageCount: map['page_count'],
|
pageCount: map['page_count'] as int,
|
||||||
notModels: map['not_models'],
|
notModels: map['not_models'] as List<double>,
|
||||||
camelCaseString: map['camelCase'],
|
camelCaseString: map['camelCase'] as String,
|
||||||
createdAt: map['created_at'] != null
|
createdAt: map['created_at'] != null
|
||||||
? (map['created_at'] is DateTime
|
? (map['created_at'] is DateTime
|
||||||
? map['created_at']
|
? (map['created_at'] as DateTime)
|
||||||
: DateTime.parse(map['created_at']))
|
: DateTime.parse(map['created_at']))
|
||||||
: null,
|
: null,
|
||||||
updatedAt: map['updated_at'] != null
|
updatedAt: map['updated_at'] != null
|
||||||
? (map['updated_at'] is DateTime
|
? (map['updated_at'] is DateTime
|
||||||
? map['updated_at']
|
? (map['updated_at'] as DateTime)
|
||||||
: DateTime.parse(map['updated_at']))
|
: DateTime.parse(map['updated_at']))
|
||||||
: null);
|
: null);
|
||||||
}
|
}
|
||||||
|
|
11
angel_serialize_generator/test/models/with_enum.dart
Normal file
11
angel_serialize_generator/test/models/with_enum.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:angel_model/angel_model.dart';
|
||||||
|
import 'package:angel_serialize/angel_serialize.dart';
|
||||||
|
part 'with_enum.g.dart';
|
||||||
|
part 'with_enum.serializer.g.dart';
|
||||||
|
|
||||||
|
@Serializable(autoIdAndDateFields: false)
|
||||||
|
abstract class _WithEnum {
|
||||||
|
WithEnumType get type;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum WithEnumType { a, b, c }
|
26
angel_serialize_generator/test/models/with_enum.g.dart
Normal file
26
angel_serialize_generator/test/models/with_enum.g.dart
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'with_enum.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: JsonModelGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class WithEnum implements _WithEnum {
|
||||||
|
const WithEnum({this.type});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final WithEnumType type;
|
||||||
|
|
||||||
|
WithEnum copyWith({WithEnumType type}) {
|
||||||
|
return new WithEnum(type: type ?? this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator ==(other) {
|
||||||
|
return other is _WithEnum && other.type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return WithEnumSerializer.toMap(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'with_enum.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: SerializerGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
abstract class WithEnumSerializer {
|
||||||
|
static WithEnum fromMap(Map map) {
|
||||||
|
return new WithEnum(
|
||||||
|
type: map['type'] is WithEnumType
|
||||||
|
? map['type']
|
||||||
|
: (map['type'] is int ? WithEnumType.values[map['type']] : null));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, dynamic> toMap(WithEnum model) {
|
||||||
|
return {
|
||||||
|
'type':
|
||||||
|
model.type == null ? null : WithEnumType.values.indexOf(model.type)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class WithEnumFields {
|
||||||
|
static const String type = 'type';
|
||||||
|
}
|
Loading…
Reference in a new issue