Generate data classes based on specified tables

This commit is contained in:
Simon Binder 2019-02-07 21:13:05 +01:00
parent b52906ad31
commit b5e788d956
12 changed files with 155 additions and 71 deletions

View File

@ -42,7 +42,6 @@
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build-1.1.0/lib" />
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build-1.1.1/lib" />
</list>
</value>
</entry>
@ -64,7 +63,6 @@
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_resolvers-0.2.3/lib" />
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_resolvers-1.0.0/lib" />
</list>
</value>
</entry>
@ -72,7 +70,6 @@
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner-1.2.3/lib" />
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner-1.2.5/lib" />
</list>
</value>
</entry>
@ -80,7 +77,6 @@
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner_core-2.0.1/lib" />
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner_core-2.0.2/lib" />
</list>
</value>
</entry>
@ -544,15 +540,11 @@
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.0.8/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build-1.1.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_config-0.3.1+4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_daemon-0.2.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_resolvers-0.2.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_resolvers-1.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner-1.2.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner-1.2.5/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner_core-2.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_runner_core-2.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/build_test-0.10.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/built_collection-4.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/built_value-6.3.0/lib" />

View File

@ -1,5 +1,6 @@
import 'package:sally/sally.dart';
part 'example.g.dart.custom';
part 'example.g.dart';
class Products extends Table {
@ -16,7 +17,7 @@ class Users extends Table {
}
@UseData(tables: [Products, Users])
@UseSally(tables: [Products, Users])
class ShopDb extends _$ShopDb {
ShopDb(SqlTypeSystem typeSystem, QueryExecutor executor) : super(typeSystem, executor);

View File

@ -1,48 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'example.dart';
class _$ShopDb extends GeneratedDatabase {
_$ShopDb(SqlTypeSystem typeSystem, QueryExecutor executor) : super(typeSystem, executor);
UsersTable get users => null;
}
class User {
// **************************************************************************
// SallyGenerator
// **************************************************************************
class Products_Data {
final int id;
final String name;
User(this.id, this.name);
Products_Data({this.id, this.name});
}
class UsersTable extends Users implements TableInfo<Users, User> {
final GeneratedDatabase db;
UsersTable(this.db);
@override
List<Column> get $columns => [id, name];
@override
String get $tableName => "users";
@override
IntColumn get id => GeneratedIntColumn("id");
@override
TextColumn get name => GeneratedTextColumn("name");
@override
Users get asDslTable => this;
@override
User map(Map<String, dynamic> data) {
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return User(intType.mapFromDatabaseResponse(data["id"]), stringType.mapFromDatabaseResponse(data["name"]));
}
class Users_Data {
final int id;
final String name;
Users_Data({this.id, this.name});
}

View File

@ -0,0 +1,50 @@
part of 'example.dart';
abstract class _$ShopDb extends GeneratedDatabase {
_$ShopDb(SqlTypeSystem typeSystem, QueryExecutor executor) : super(typeSystem, executor);
UsersTable get users => null;
}
class User {
final int id;
final String name;
User(this.id, this.name);
}
class UsersTable extends Users implements TableInfo<Users, User> {
final GeneratedDatabase db;
UsersTable(this.db);
@override
List<Column> get $columns => [id, name];
@override
String get $tableName => "users";
@override
IntColumn get id => GeneratedIntColumn("id", true);
@override
TextColumn get name => GeneratedTextColumn("name", false);
@override
Users get asDslTable => this;
@override
User map(Map<String, dynamic> data) {
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return User(intType.mapFromDatabaseResponse(data["id"]), stringType.mapFromDatabaseResponse(data["name"]));
}
@override
Set<Column> get $primaryKey => Set()..add(id);
}

View File

@ -1,6 +1,5 @@
class UseData {
class UseSally {
final List<Type> tables;
final int schemaVersion;
const UseData({this.tables, this.schemaVersion = 1});
const UseSally({this.tables});
}

View File

@ -1,8 +1,8 @@
#builders:
# sally_generator:
# import: "package:sally_generator/generator.dart"
# builder_factories: ["sallyBuilder"]
# build_extensions: {".dart": [".sally.g.part"]}
# auto_apply: dependents
# build_to: cache
# applies_builders: ["source_gen|combining_builder"]
builders:
sally_generator:
import: "package:sally_generator/generator.dart"
builder_factories: ["sallyBuilder"]
build_extensions: {".dart": [".sally.g.part"]}
auto_apply: dependents
build_to: cache
applies_builders: ["source_gen|combining_builder"]

View File

@ -9,20 +9,24 @@ abstract class ColumnName implements Built<ColumnName, ColumnNameBuilder> {
/// field name in the table class. It's explicit if `.named()` was called in
/// the column builder.
bool get implicit;
String get name;
ColumnName._();
factory ColumnName([updates(ColumnNameBuilder b)]) = _$ColumnName;
factory ColumnName.implicitly(String name) => ColumnName((b) => b
..implicit = true
..name = name);
factory ColumnName.explicitly(String name) => ColumnName((b) => b
..implicit = false
..name = name);
}
class SpecifiedColumn {
final String dartGetterName;
final ColumnType type;
final ColumnName name;
@ -34,8 +38,15 @@ class SpecifiedColumn {
final bool declaredAsPrimaryKey;
final List<ColumnFeature> features;
String get dartTypeName => {
ColumnType.boolean: 'bool',
ColumnType.text: 'String',
ColumnType.integer: 'int'
}[type];
const SpecifiedColumn(
{this.type,
this.dartGetterName,
this.name,
this.declaredAsPrimaryKey = false,
this.features = const []});
@ -63,10 +74,12 @@ abstract class LimitingTextLength extends ColumnFeature
implements Built<LimitingTextLength, LimitingTextLengthBuilder> {
@nullable
int get minLength;
@nullable
int get maxLength;
LimitingTextLength._();
factory LimitingTextLength(void updates(LimitingTextLengthBuilder b)) =
_$LimitingTextLength;

View File

@ -3,6 +3,7 @@ import 'package:sally_generator/src/errors.dart';
import 'package:sally_generator/src/model/specified_column.dart';
import 'package:sally_generator/src/parser/parser.dart';
import 'package:sally_generator/src/sally_generator.dart';
import 'package:recase/recase.dart';
const String startInt = 'integer';
const String startString = 'text';
@ -114,11 +115,12 @@ class ColumnParser extends ParserBase {
if (foundExplicitName != null) {
name = ColumnName.explicitly(foundExplicitName);
} else {
name = ColumnName.implicitly(getter.name.name);
name = ColumnName.implicitly(ReCase(getter.name.name).snakeCase);
}
return SpecifiedColumn(
type: _startMethodToColumnType(foundStartMethod),
dartGetterName: getter.name.name,
name: name,
declaredAsPrimaryKey: wasDeclaredAsPrimaryKey,
features: foundFeatures);

View File

@ -1,19 +1,27 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:sally/sally.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/src/dart/analysis/results.dart'; // ignore: implementation_imports
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:sally_generator/src/errors.dart';
import 'package:sally_generator/src/model/specified_table.dart';
import 'package:sally_generator/src/parser/column_parser.dart';
import 'package:sally_generator/src/parser/table_parser.dart';
import 'package:sally_generator/src/writer/table_writer.dart';
import 'package:source_gen/source_gen.dart';
class SallyGenerator extends Generator {
class SallyGenerator extends GeneratorForAnnotation<UseSally> {
final Map<String, ParsedLibraryResult> _astForLibs = {};
final ErrorStore errors = ErrorStore();
TableParser tableParser;
ColumnParser columnParser;
final tableTypeChecker = const TypeChecker.fromRuntime(Table);
final Map<DartType, SpecifiedTable> _foundTables = {};
ElementDeclarationResult loadElementDeclaration(Element element) {
final result = _astForLibs.putIfAbsent(element.library.name, () {
// ignore: deprecated_member_use
@ -24,13 +32,33 @@ class SallyGenerator extends Generator {
}
@override
String generate(LibraryReader library, BuildStep buildStep) {
final testUsers = library.findType('Users');
generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) {
final types =
annotation.peek('tables').listValue.map((obj) => obj.toTypeValue());
if (testUsers == null) return '';
tableParser ??= TableParser(this);
columnParser ??= ColumnParser(this);
TableParser(this).parse(testUsers);
for (var table in types) {
if (!tableTypeChecker.isAssignableFrom(table.element)) {
errors.add(SallyError(
critical: true,
message: 'The type $table is not a sally table',
affectedElement: element));
} else {
_foundTables[table] = tableParser.parse(table.element as ClassElement);
}
}
return '';
if (_foundTables.isEmpty)
return '';
final buffer = StringBuffer();
for (var tbl in _foundTables.values) {
TableWriter(tbl).writeInto(buffer);
}
return buffer.toString();
}
}

View File

@ -0,0 +1,30 @@
import 'package:sally_generator/src/model/specified_table.dart';
class TableWriter {
final SpecifiedTable table;
TableWriter(this.table);
void writeInto(StringBuffer buffer) {
writeDataClass(buffer);
}
void writeDataClass(StringBuffer buffer) {
buffer.write('class ${table.dartTypeName} {\n');
// write individual fields
for (var column in table.columns) {
buffer.write('final ${column.dartTypeName} ${column.dartGetterName}; \n');
}
// write constructor with named optional fields
buffer
..write(table.dartTypeName)
..write('({')
..write(table.columns
.map((column) => 'this.${column.dartGetterName}')
.join(', '))
..write('});')
..write('\n}');
}
}

View File

@ -12,6 +12,8 @@ dependencies:
recase:
built_value:
source_gen:
build_runner:
build_config:
sally:
path:
../sally
@ -19,6 +21,4 @@ dependencies:
dev_dependencies:
test: ^1.0.0
built_value_generator:
build_runner:
build_config:
build_test: '>=0.10.0 <0.11.0'

View File

@ -1,6 +1,5 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:sally_generator/src/model/specified_column.dart';
import 'package:sally_generator/src/parser/column_parser.dart';
import 'package:sally_generator/src/parser/table_parser.dart';
import 'package:sally_generator/src/sally_generator.dart';
import 'package:test_api/test_api.dart';
@ -41,7 +40,6 @@ void main() async {
setUp(() {
generator = SallyGenerator();
generator.columnParser = ColumnParser(generator);
});
group('SQL table name', () {