drift/moor_generator/lib/src/parser/table_parser.dart

125 lines
4.4 KiB
Dart

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:moor_generator/src/errors.dart';
import 'package:moor_generator/src/model/specified_column.dart';
import 'package:moor_generator/src/model/specified_table.dart';
import 'package:moor_generator/src/parser/parser.dart';
import 'package:moor_generator/src/utils/names.dart';
import 'package:moor_generator/src/utils/type_utils.dart';
import 'package:moor_generator/src/moor_generator.dart'; // ignore: implementation_imports
import 'package:recase/recase.dart';
import 'package:moor/sqlite_keywords.dart';
class TableParser extends ParserBase {
TableParser(MoorGenerator generator) : super(generator);
SpecifiedTable parse(ClassElement element) {
final sqlName = _parseTableName(element);
if (sqlName == null) return null;
final columns = _parseColumns(element);
return SpecifiedTable(
fromClass: element,
columns: columns,
sqlName: escapeIfNeeded(sqlName),
dartTypeName: _readDartTypeName(element),
primaryKey: _readPrimaryKey(element, columns),
);
}
String _readDartTypeName(ClassElement element) {
final nameAnnotation = element.metadata.singleWhere(
(e) => e.computeConstantValue().type.name == 'DataClassName',
orElse: () => null);
if (nameAnnotation == null) {
return dataClassNameForClassName(element.name);
} else {
return nameAnnotation.constantValue.getField('name').toStringValue();
}
}
String _parseTableName(ClassElement element) {
// todo allow override via a field (final String tableName = '') as well
final tableNameGetter = element.getGetter('tableName');
if (tableNameGetter == null) {
// class does not override tableName. So just use the dart class name
// instead. Will use placed_orders for a class called PlacedOrders
return ReCase(element.name).snakeCase;
}
// we expect something like get tableName => "myTableName", the getter
// must do nothing more complicated
final tableNameDeclaration =
generator.loadElementDeclaration(tableNameGetter);
final returnExpr = returnExpressionOfMethod(
tableNameDeclaration.node as MethodDeclaration);
final tableName = readStringLiteral(returnExpr, () {
generator.state.errors.add(MoorError(
critical: true,
message:
'This getter must return a string literal, and do nothing more',
affectedElement: tableNameGetter));
});
return tableName;
}
Set<SpecifiedColumn> _readPrimaryKey(
ClassElement element, List<SpecifiedColumn> columns) {
final primaryKeyGetter = element.getGetter('primaryKey');
if (primaryKeyGetter == null) {
return null;
}
final ast = generator.loadElementDeclaration(primaryKeyGetter).node
as MethodDeclaration;
final body = ast.body;
if (body is! ExpressionFunctionBody) {
generator.state.errors.add(MoorError(
affectedElement: primaryKeyGetter,
message: 'This must return a set literal using the => syntax!'));
return null;
}
final expression = (body as ExpressionFunctionBody).expression;
final parsedPrimaryKey = <SpecifiedColumn>{};
if (expression is SetOrMapLiteral) {
// todo replace with just "elements" when dropping support for analyzer
// 0.35.0
// ignore: deprecated_member_use
for (var entry in expression.elements2) {
if (entry is Identifier) {
final column = columns
.singleWhere((column) => column.dartGetterName == entry.name);
parsedPrimaryKey.add(column);
} else {
// Don't add an error, these features aren't on a stable dart release
// yet.
print('Unexpected entry in expression.elements2: $entry');
}
}
} else {
generator.state.errors.add(MoorError(
affectedElement: primaryKeyGetter,
message: 'This must return a set literal!'));
}
return parsedPrimaryKey;
}
List<SpecifiedColumn> _parseColumns(ClassElement element) {
return element.fields
.where((field) => isColumn(field.type) && field.getter != null)
.map((field) {
final node = generator.loadElementDeclaration(field.getter).node
as MethodDeclaration;
return generator.state.columnParser.parse(node, field.getter);
}).toList();
}
}