mirror of https://github.com/AMT-Cheif/drift.git
Implicitly create type converters for enums, Dart api (#478)
This commit is contained in:
parent
4611ecc3c8
commit
0f2ff8c97a
|
@ -3,6 +3,7 @@
|
|||
- Update companions now implement `==` and `hashCode`
|
||||
- New `containsCase` method for text in `package:moor/extensions/moor_ffi.dart`
|
||||
- The `toCompanion` method is back for data classes, but its generation can be disabled with a build option
|
||||
- New `intEnum` column method to automatically map between an enum and an int
|
||||
|
||||
## 3.0.2
|
||||
|
||||
|
|
|
@ -62,6 +62,14 @@ abstract class Table {
|
|||
@protected
|
||||
IntColumnBuilder integer() => _isGenerated();
|
||||
|
||||
/// Creates a column to store an `enum` class [T].
|
||||
///
|
||||
/// In the database, the column will be represented as an integer
|
||||
/// corresponding to the enums index. Note that this can invalidate your data
|
||||
/// if you add another value to the enum class.
|
||||
@protected
|
||||
IntColumnBuilder intEnum<T>() => _isGenerated();
|
||||
|
||||
/// Use this as the body of a getter to declare a column that holds strings.
|
||||
/// Example (inside the body of a table class):
|
||||
/// ```
|
||||
|
|
|
@ -52,7 +52,7 @@ void main() {
|
|||
'INSERT INTO todos (content) VALUES (?)',
|
||||
'UPDATE users SET name = ?;',
|
||||
'UPDATE users SET name = ? WHERE name = ?;',
|
||||
'UPDATE categories SET `desc` = ? WHERE id = ?;',
|
||||
'UPDATE categories SET `desc` = ?, priority = 0 WHERE id = ?;',
|
||||
'DELETE FROM categories WHERE 1;',
|
||||
'DELETE FROM todos WHERE id = ?;',
|
||||
],
|
||||
|
|
|
@ -33,8 +33,12 @@ class Users extends Table with AutoIncrement {
|
|||
class Categories extends Table with AutoIncrement {
|
||||
TextColumn get description =>
|
||||
text().named('desc').customConstraint('NOT NULL UNIQUE')();
|
||||
IntColumn get priority =>
|
||||
intEnum<CategoryPriority>().withDefault(const Constant(0))();
|
||||
}
|
||||
|
||||
enum CategoryPriority { low, medium, high }
|
||||
|
||||
class SharedTodos extends Table {
|
||||
IntColumn get todo => integer()();
|
||||
IntColumn get user => integer()();
|
||||
|
|
|
@ -7,6 +7,19 @@ part of 'todos.dart';
|
|||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this
|
||||
class _$GeneratedConverter$0 extends TypeConverter<CategoryPriority, int> {
|
||||
const _$GeneratedConverter$0();
|
||||
@override
|
||||
CategoryPriority mapToDart(int fromDb) {
|
||||
return fromDb == null ? null : CategoryPriority.values[fromDb];
|
||||
}
|
||||
|
||||
@override
|
||||
int mapToSql(CategoryPriority value) {
|
||||
return value?.index;
|
||||
}
|
||||
}
|
||||
|
||||
class TodoEntry extends DataClass implements Insertable<TodoEntry> {
|
||||
final int id;
|
||||
final String title;
|
||||
|
@ -336,7 +349,9 @@ class $TodosTableTable extends TodosTable
|
|||
class Category extends DataClass implements Insertable<Category> {
|
||||
final int id;
|
||||
final String description;
|
||||
Category({@required this.id, @required this.description});
|
||||
final CategoryPriority priority;
|
||||
Category(
|
||||
{@required this.id, @required this.description, @required this.priority});
|
||||
factory Category.fromData(Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
|
@ -346,6 +361,8 @@ class Category extends DataClass implements Insertable<Category> {
|
|||
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
|
||||
description:
|
||||
stringType.mapFromDatabaseResponse(data['${effectivePrefix}desc']),
|
||||
priority: $CategoriesTable.$converter0.mapToDart(
|
||||
intType.mapFromDatabaseResponse(data['${effectivePrefix}priority'])),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
@ -357,6 +374,10 @@ class Category extends DataClass implements Insertable<Category> {
|
|||
if (!nullToAbsent || description != null) {
|
||||
map['desc'] = Variable<String>(description);
|
||||
}
|
||||
if (!nullToAbsent || priority != null) {
|
||||
final converter = $CategoriesTable.$converter0;
|
||||
map['priority'] = Variable<int>(converter.mapToSql(priority));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -366,6 +387,9 @@ class Category extends DataClass implements Insertable<Category> {
|
|||
description: description == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(description),
|
||||
priority: priority == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(priority),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -375,6 +399,7 @@ class Category extends DataClass implements Insertable<Category> {
|
|||
return Category(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
description: serializer.fromJson<String>(json['description']),
|
||||
priority: serializer.fromJson<CategoryPriority>(json['priority']),
|
||||
);
|
||||
}
|
||||
factory Category.fromJsonString(String encodedJson,
|
||||
|
@ -388,57 +413,72 @@ class Category extends DataClass implements Insertable<Category> {
|
|||
return <String, dynamic>{
|
||||
'id': serializer.toJson<int>(id),
|
||||
'description': serializer.toJson<String>(description),
|
||||
'priority': serializer.toJson<CategoryPriority>(priority),
|
||||
};
|
||||
}
|
||||
|
||||
Category copyWith({int id, String description}) => Category(
|
||||
Category copyWith({int id, String description, CategoryPriority priority}) =>
|
||||
Category(
|
||||
id: id ?? this.id,
|
||||
description: description ?? this.description,
|
||||
priority: priority ?? this.priority,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('Category(')
|
||||
..write('id: $id, ')
|
||||
..write('description: $description')
|
||||
..write('description: $description, ')
|
||||
..write('priority: $priority')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(id.hashCode, description.hashCode));
|
||||
int get hashCode =>
|
||||
$mrjf($mrjc(id.hashCode, $mrjc(description.hashCode, priority.hashCode)));
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
identical(this, other) ||
|
||||
(other is Category &&
|
||||
other.id == this.id &&
|
||||
other.description == this.description);
|
||||
other.description == this.description &&
|
||||
other.priority == this.priority);
|
||||
}
|
||||
|
||||
class CategoriesCompanion extends UpdateCompanion<Category> {
|
||||
final Value<int> id;
|
||||
final Value<String> description;
|
||||
final Value<CategoryPriority> priority;
|
||||
const CategoriesCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.description = const Value.absent(),
|
||||
this.priority = const Value.absent(),
|
||||
});
|
||||
CategoriesCompanion.insert({
|
||||
this.id = const Value.absent(),
|
||||
@required String description,
|
||||
this.priority = const Value.absent(),
|
||||
}) : description = Value(description);
|
||||
static Insertable<Category> custom({
|
||||
Expression<int> id,
|
||||
Expression<String> description,
|
||||
Expression<int> priority,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (description != null) 'desc': description,
|
||||
if (priority != null) 'priority': priority,
|
||||
});
|
||||
}
|
||||
|
||||
CategoriesCompanion copyWith({Value<int> id, Value<String> description}) {
|
||||
CategoriesCompanion copyWith(
|
||||
{Value<int> id,
|
||||
Value<String> description,
|
||||
Value<CategoryPriority> priority}) {
|
||||
return CategoriesCompanion(
|
||||
id: id ?? this.id,
|
||||
description: description ?? this.description,
|
||||
priority: priority ?? this.priority,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -451,6 +491,10 @@ class CategoriesCompanion extends UpdateCompanion<Category> {
|
|||
if (description.present) {
|
||||
map['desc'] = Variable<String>(description.value);
|
||||
}
|
||||
if (priority.present) {
|
||||
final converter = $CategoriesTable.$converter0;
|
||||
map['priority'] = Variable<int>(converter.mapToSql(priority.value));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
@ -480,8 +524,17 @@ class $CategoriesTable extends Categories
|
|||
$customConstraints: 'NOT NULL UNIQUE');
|
||||
}
|
||||
|
||||
final VerificationMeta _priorityMeta = const VerificationMeta('priority');
|
||||
GeneratedIntColumn _priority;
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [id, description];
|
||||
GeneratedIntColumn get priority => _priority ??= _constructPriority();
|
||||
GeneratedIntColumn _constructPriority() {
|
||||
return GeneratedIntColumn('priority', $tableName, false,
|
||||
defaultValue: const Constant(0));
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [id, description, priority];
|
||||
@override
|
||||
$CategoriesTable get asDslTable => this;
|
||||
@override
|
||||
|
@ -502,6 +555,7 @@ class $CategoriesTable extends Categories
|
|||
} else if (isInserting) {
|
||||
context.missing(_descriptionMeta);
|
||||
}
|
||||
context.handle(_priorityMeta, const VerificationResult.success());
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -517,6 +571,9 @@ class $CategoriesTable extends Categories
|
|||
$CategoriesTable createAlias(String alias) {
|
||||
return $CategoriesTable(_db, alias);
|
||||
}
|
||||
|
||||
static TypeConverter<CategoryPriority, int> $converter0 =
|
||||
const _$GeneratedConverter$0();
|
||||
}
|
||||
|
||||
class User extends DataClass implements Insertable<User> {
|
||||
|
|
|
@ -73,7 +73,11 @@ void main() {
|
|||
});
|
||||
|
||||
test('generated data classes can be converted to companions', () {
|
||||
final entry = Category(id: 3, description: 'description');
|
||||
final entry = Category(
|
||||
id: 3,
|
||||
description: 'description',
|
||||
priority: CategoryPriority.low,
|
||||
);
|
||||
final companion = entry.toCompanion(false);
|
||||
|
||||
expect(companion.runtimeType, CategoriesCompanion);
|
||||
|
@ -82,6 +86,7 @@ void main() {
|
|||
equals(CategoriesCompanion.insert(
|
||||
description: 'description',
|
||||
id: const Value(3),
|
||||
priority: const Value(CategoryPriority.low),
|
||||
)),
|
||||
);
|
||||
});
|
||||
|
|
|
@ -218,4 +218,16 @@ void main() {
|
|||
));
|
||||
expect(id, 3);
|
||||
});
|
||||
|
||||
test('applies implicit type converter', () async {
|
||||
await db.into(db.categories).insert(CategoriesCompanion.insert(
|
||||
description: 'description',
|
||||
priority: const Value(CategoryPriority.medium),
|
||||
));
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT INTO categories (`desc`, priority) VALUES (?, ?)',
|
||||
['description', 1],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ void main() {
|
|||
verify(executor.runSelect(
|
||||
'SELECT t.id AS "t.id", t.title AS "t.title", '
|
||||
't.content AS "t.content", t.target_date AS "t.target_date", '
|
||||
't.category AS "t.category", c.id AS "c.id", c.`desc` AS "c.desc" '
|
||||
't.category AS "t.category", c.id AS "c.id", c.`desc` AS "c.desc", '
|
||||
'c.priority AS "c.priority" '
|
||||
'FROM todos t LEFT OUTER JOIN categories c ON c.id = t.category;',
|
||||
argThat(isEmpty)));
|
||||
});
|
||||
|
@ -43,6 +44,7 @@ void main() {
|
|||
't.category': 3,
|
||||
'c.id': 3,
|
||||
'c.desc': 'description',
|
||||
'c.priority': 2,
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
@ -65,7 +67,13 @@ void main() {
|
|||
));
|
||||
|
||||
expect(
|
||||
row.readTable(categories), Category(id: 3, description: 'description'));
|
||||
row.readTable(categories),
|
||||
Category(
|
||||
id: 3,
|
||||
description: 'description',
|
||||
priority: CategoryPriority.high,
|
||||
),
|
||||
);
|
||||
|
||||
verify(executor.runSelect(argThat(contains('DISTINCT')), any));
|
||||
});
|
||||
|
@ -167,20 +175,29 @@ void main() {
|
|||
|
||||
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
||||
return [
|
||||
{'c.id': 3, 'c.desc': 'Description', 'c2': 11}
|
||||
{'c.id': 3, 'c.desc': 'Description', 'c.priority': 1, 'c3': 11}
|
||||
];
|
||||
});
|
||||
|
||||
final result = await query.getSingle();
|
||||
|
||||
verify(executor.runSelect(
|
||||
'SELECT c.id AS "c.id", c.`desc` AS "c.desc", LENGTH(c.`desc`) AS "c2" '
|
||||
'SELECT c.id AS "c.id", c.`desc` AS "c.desc", c.priority AS "c.priority"'
|
||||
', LENGTH(c.`desc`) AS "c3" '
|
||||
'FROM categories c;',
|
||||
[],
|
||||
));
|
||||
|
||||
expect(result.readTable(categories),
|
||||
equals(Category(id: 3, description: 'Description')));
|
||||
expect(
|
||||
result.readTable(categories),
|
||||
equals(
|
||||
Category(
|
||||
id: 3,
|
||||
description: 'Description',
|
||||
priority: CategoryPriority.medium,
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(result.read(descriptionLength), 11);
|
||||
});
|
||||
|
||||
|
@ -205,20 +222,28 @@ void main() {
|
|||
|
||||
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
||||
return [
|
||||
{'c.id': 3, 'c.desc': 'desc', 'c2': 10}
|
||||
{'c.id': 3, 'c.desc': 'desc', 'c.priority': 0, 'c3': 10}
|
||||
];
|
||||
});
|
||||
|
||||
final result = await query.getSingle();
|
||||
|
||||
verify(executor.runSelect(
|
||||
'SELECT c.id AS "c.id", c.`desc` AS "c.desc", COUNT(t.id) AS "c2" '
|
||||
'SELECT c.id AS "c.id", c.`desc` AS "c.desc", '
|
||||
'c.priority AS "c.priority", COUNT(t.id) AS "c3" '
|
||||
'FROM categories c INNER JOIN todos t ON t.category = c.id '
|
||||
'GROUP BY c.id HAVING COUNT(t.id) >= ?;',
|
||||
[10]));
|
||||
|
||||
expect(result.readTable(todos), isNull);
|
||||
expect(result.readTable(categories), Category(id: 3, description: 'desc'));
|
||||
expect(
|
||||
result.readTable(categories),
|
||||
Category(
|
||||
id: 3,
|
||||
description: 'desc',
|
||||
priority: CategoryPriority.low,
|
||||
),
|
||||
);
|
||||
expect(result.read(amountOfTodos), 10);
|
||||
});
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ void main() {
|
|||
verify(mockExecutor.runCustom(
|
||||
'CREATE TABLE IF NOT EXISTS categories '
|
||||
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
|
||||
'`desc` VARCHAR NOT NULL UNIQUE);',
|
||||
'`desc` VARCHAR NOT NULL UNIQUE, '
|
||||
'priority INTEGER NOT NULL DEFAULT 0);',
|
||||
[]));
|
||||
|
||||
verify(mockExecutor.runCustom(
|
||||
|
|
|
@ -156,4 +156,27 @@ void main() {
|
|||
..markTablesUpdated({db.todosTable});
|
||||
});
|
||||
});
|
||||
|
||||
test('applies implicit type converter', () async {
|
||||
when(executor.runSelect(any, any)).thenAnswer((_) {
|
||||
return Future.value([
|
||||
{
|
||||
'id': 1,
|
||||
'desc': 'description',
|
||||
'priority': 2,
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
final category = await db.select(db.categories).getSingle();
|
||||
|
||||
expect(
|
||||
category,
|
||||
Category(
|
||||
id: 1,
|
||||
description: 'description',
|
||||
priority: CategoryPriority.high,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
part of 'parser.dart';
|
||||
|
||||
const String startInt = 'integer';
|
||||
const String startEnum = 'intEnum';
|
||||
const String startString = 'text';
|
||||
const String startBool = 'boolean';
|
||||
const String startDateTime = 'dateTime';
|
||||
|
@ -9,6 +10,7 @@ const String startReal = 'real';
|
|||
|
||||
const Set<String> starters = {
|
||||
startInt,
|
||||
startEnum,
|
||||
startString,
|
||||
startBool,
|
||||
startDateTime,
|
||||
|
@ -185,6 +187,29 @@ class ColumnParser {
|
|||
sqlType: columnType);
|
||||
}
|
||||
|
||||
if (foundStartMethod == startEnum) {
|
||||
if (converter != null) {
|
||||
base.step.reportError(ErrorInDartCode(
|
||||
message: 'Using $startEnum will apply a custom converter by default, '
|
||||
"so you can't add an additional converter",
|
||||
affectedElement: getter.declaredElement,
|
||||
severity: Severity.warning,
|
||||
));
|
||||
}
|
||||
|
||||
final enumType = remainingExpr.typeArgumentTypes[0];
|
||||
try {
|
||||
converter = UsedTypeConverter.forEnumColumn(enumType);
|
||||
} on InvalidTypeForEnumConverterException catch (e) {
|
||||
base.step.errors.report(ErrorInDartCode(
|
||||
message: "Can't use $startEnum with "
|
||||
'${e.invalidType.getDisplayString()}: ${e.reason}',
|
||||
affectedElement: getter.declaredElement,
|
||||
severity: Severity.error,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (foundDefaultExpression != null && clientDefaultExpression != null) {
|
||||
base.step.reportError(
|
||||
ErrorInDartCode(
|
||||
|
@ -217,6 +242,7 @@ class ColumnParser {
|
|||
startBool: ColumnType.boolean,
|
||||
startString: ColumnType.text,
|
||||
startInt: ColumnType.integer,
|
||||
startEnum: ColumnType.integer,
|
||||
startDateTime: ColumnType.datetime,
|
||||
startBlob: ColumnType.blob,
|
||||
startReal: ColumnType.real,
|
||||
|
|
|
@ -81,7 +81,7 @@ class MoorOptions {
|
|||
this.generateConnectConstructor = false,
|
||||
this.legacyTypeInference = false,
|
||||
this.eagerlyLoadDartAst = false,
|
||||
this.dataClassToCompanions,
|
||||
this.dataClassToCompanions = true,
|
||||
this.modules = const [],
|
||||
});
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moor_generator/src/model/table.dart';
|
||||
|
@ -11,10 +12,15 @@ class UsedTypeConverter {
|
|||
/// The table using this type converter.
|
||||
MoorTable table;
|
||||
|
||||
/// Whether this type converter is implicitly declared for enum mappings,
|
||||
/// which means that the implementation of the converter needs to be
|
||||
/// generated as well.
|
||||
final bool isForEnum;
|
||||
|
||||
/// The expression that will construct the type converter at runtime. The
|
||||
/// type converter constructed will map a [mappedType] to the [sqlType] and
|
||||
/// vice-versa.
|
||||
final String expression;
|
||||
String expression;
|
||||
|
||||
/// The type that will be present at runtime.
|
||||
final DartType mappedType;
|
||||
|
@ -35,5 +41,37 @@ class UsedTypeConverter {
|
|||
UsedTypeConverter(
|
||||
{@required this.expression,
|
||||
@required this.mappedType,
|
||||
@required this.sqlType});
|
||||
@required this.sqlType,
|
||||
this.isForEnum = false});
|
||||
|
||||
factory UsedTypeConverter.forEnumColumn(DartType enumType) {
|
||||
if (enumType.element is! ClassElement) {
|
||||
throw InvalidTypeForEnumConverterException('Not a class', enumType);
|
||||
}
|
||||
|
||||
final creatingClass = enumType.element as ClassElement;
|
||||
if (!creatingClass.isEnum) {
|
||||
throw InvalidTypeForEnumConverterException('Not an enum', enumType);
|
||||
}
|
||||
|
||||
return UsedTypeConverter(
|
||||
expression: 'bogus expression for enum value',
|
||||
mappedType: enumType,
|
||||
sqlType: ColumnType.integer,
|
||||
isForEnum: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidTypeForEnumConverterException implements Exception {
|
||||
final String reason;
|
||||
final DartType invalidType;
|
||||
|
||||
InvalidTypeForEnumConverterException(this.reason, this.invalidType);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Invalid type for enum converter: '
|
||||
'${invalidType.getDisplayString()}. Reason: $reason';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
||||
|
@ -16,6 +17,44 @@ class DatabaseWriter {
|
|||
DatabaseWriter(this.db, this.scope);
|
||||
|
||||
void write() {
|
||||
// Write generated convertesr
|
||||
final enumConverters =
|
||||
db.tables.expand((t) => t.converters).where((c) => c.isForEnum);
|
||||
final generatedConvertersForType = <DartType, String>{};
|
||||
var amountOfGeneratedConverters = 0;
|
||||
|
||||
for (final converter in enumConverters) {
|
||||
String classForConverter;
|
||||
|
||||
if (generatedConvertersForType.containsKey(converter.mappedType)) {
|
||||
classForConverter = generatedConvertersForType[converter.mappedType];
|
||||
} else {
|
||||
final id = amountOfGeneratedConverters++;
|
||||
classForConverter = '_\$GeneratedConverter\$$id';
|
||||
|
||||
final buffer = scope.leaf();
|
||||
final dartType = converter.mappedType.getDisplayString();
|
||||
final superClass = converter.displayNameOfConverter;
|
||||
|
||||
buffer
|
||||
..writeln('class $classForConverter extends $superClass {')
|
||||
..writeln('const $classForConverter();')
|
||||
..writeln('@override')
|
||||
..writeln('$dartType mapToDart(int fromDb) {')
|
||||
..writeln('return fromDb == null ? null : $dartType.values[fromDb];')
|
||||
..writeln('}')
|
||||
..writeln('@override')
|
||||
..writeln('int mapToSql($dartType value) {')
|
||||
..writeln('return value?.index;')
|
||||
..writeln('}')
|
||||
..writeln('}');
|
||||
|
||||
generatedConvertersForType[converter.mappedType] = classForConverter;
|
||||
}
|
||||
|
||||
converter.expression = 'const $classForConverter()';
|
||||
}
|
||||
|
||||
// Write referenced tables
|
||||
for (final table in db.tables) {
|
||||
TableWriter(table, scope.child()).writeInto();
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
TestState state;
|
||||
|
||||
setUpAll(() async {
|
||||
state = TestState.withContent({
|
||||
'foo|lib/main.dart': '''
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
enum Fruits {
|
||||
apple, orange, banana
|
||||
}
|
||||
|
||||
class NotAnEnum {}
|
||||
|
||||
class ValidUsage extends Table {
|
||||
IntColumn get fruit => intEnum<Fruits>()();
|
||||
}
|
||||
|
||||
class InvalidNoEnum extends Table {
|
||||
IntColumn get fruit => intEnum<NotAnEnum>()();
|
||||
}
|
||||
''',
|
||||
});
|
||||
|
||||
await state.analyze('package:foo/main.dart');
|
||||
});
|
||||
|
||||
test('parses enum columns', () {
|
||||
final file =
|
||||
state.file('package:foo/main.dart').currentResult as ParsedDartFile;
|
||||
final table =
|
||||
file.declaredTables.singleWhere((t) => t.sqlName == 'valid_usage');
|
||||
|
||||
expect(
|
||||
table.converters,
|
||||
contains(isA<UsedTypeConverter>()
|
||||
.having((e) => e.isForEnum, 'isForEnum', isTrue)),
|
||||
);
|
||||
});
|
||||
|
||||
test('fails when used with a non-enum class', () {
|
||||
final errors = state.file('package:foo/main.dart').errors.errors;
|
||||
|
||||
expect(
|
||||
errors,
|
||||
contains(isA<MoorError>().having((e) => e.message, 'message',
|
||||
allOf(contains('Not an enum'), contains('NotAnEnum')))),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import 'package:moor_generator/src/analyzer/options.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||
import 'package:moor_generator/src/writer/database_writer.dart';
|
||||
import 'package:moor_generator/src/writer/writer.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../analyzer/utils.dart';
|
||||
|
||||
void main() {
|
||||
test('does not generate multiple converters for the same enum', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.dart': '''
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
enum MyEnum { foo, bar, baz }
|
||||
|
||||
class TableA extends Table {
|
||||
IntColumn get col => intEnum<MyEnum>()();
|
||||
}
|
||||
|
||||
class TableB extends Table {
|
||||
IntColumn get another => intEnum<MyEnum>()();
|
||||
}
|
||||
|
||||
@UseMoor(tables: [TableA, TableB])
|
||||
class Database {
|
||||
|
||||
}
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.dart');
|
||||
final db = (file.currentResult as ParsedDartFile).declaredDatabases.single;
|
||||
|
||||
final writer = Writer(const MoorOptions());
|
||||
DatabaseWriter(db, writer.child()).write();
|
||||
|
||||
expect(
|
||||
writer.writeGenerated(),
|
||||
allOf(
|
||||
contains(r'_$GeneratedConverter$0'),
|
||||
isNot(contains(r'_$GeneratedConverter$1')),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue