Merge remote-tracking branch 'simolus3/develop' into fix-lint-warning

This commit is contained in:
westito 2022-08-08 11:30:57 +02:00
commit 40554c8166
39 changed files with 284 additions and 170 deletions

View File

@ -288,6 +288,12 @@ describes how to migrate stored columns between the format:
{% assign snippets = "package:drift_docs/snippets/migrations/datetime_conversion.dart.excerpt.json" | readString | json_decode %} {% assign snippets = "package:drift_docs/snippets/migrations/datetime_conversion.dart.excerpt.json" | readString | json_decode %}
Note that the JSON serialization generated by default is not affected by the
datetime mode chosen. By default, drift will serialize `DateTime` values to a
unix timestamp in milliseconds. You can change this by creating a
`ValueSerializer.defaults(serializeDateTimeValuesAsString: true)` and assigning
it to `driftRuntimeOptions.defaultSerializer`.
##### Migrating from unix timestamps to text ##### Migrating from unix timestamps to text
To migrate from using timestamps (the default option) to storing datetimes as To migrate from using timestamps (the default option) to storing datetimes as

View File

@ -8,7 +8,7 @@ template: layouts/docs/list
## Welcome to drift ## Welcome to drift
Drift is a reactive persistence library for Dart and Flutter applications. It's built on top Drift is a reactive persistence library for Dart and Flutter applications. It's built on top
of database libraries like [sqflite](https://pub.dev/packages/sqflite) or [sql.js](https://github.com/sql-js/sql.js/) of database libraries like [the sqlite3 package](https://pub.dev/packages/sqlite3), [sqflite](https://pub.dev/packages/sqflite) or [sql.js](https://github.com/sql-js/sql.js/)
and provides additional features, like: and provides additional features, like:
- __Type safety__: Instead of writing sql queries manually and parsing the `List<Map<String, dynamic>>` that they - __Type safety__: Instead of writing sql queries manually and parsing the `List<Map<String, dynamic>>` that they

View File

@ -7,47 +7,76 @@ data:
show_favorites: true show_favorites: true
--- ---
{% block "blocks/cover.html" title="Drift: Persistence library for Dart" image_anchor="top" height="med" color="indigo" %} {% block "blocks/cover.html" title="Drift: Persistence library for Dart" image_anchor="top" height="med" %}
<div class="mx-auto"> <div class="mx-auto">
<p class="h4">
Drift is <em>the</em> relational persistence library for your Dart and Flutter apps.
Write type-safe queries in Dart or SQL, enjoy auto-updating streams, easily managed transactions
and so much more to make persistence fun.
</p>
<a class="btn btn-lg btn-primary mr-3 mb-4" href="{{ 'docs/index' | pageUrl }}"> <a class="btn btn-lg btn-primary mr-3 mb-4" href="{{ 'docs/index' | pageUrl }}">
Learn more <i class="fas fa-arrow-alt-circle-right ml-2"></i> Learn more <i class="fas fa-arrow-alt-circle-right ml-2"></i>
</a> </a>
<a class="btn btn-lg btn-secondary mr-3 mb-4" href="https://pub.dev/packages/drift"> <a class="btn btn-lg btn-secondary mr-3 mb-4" href="https://pub.dev/packages/drift">
Get from pub <i class="fas fa-code ml-2 "></i> Get started <i class="fas fa-code ml-2 "></i>
</a> </a>
<p class="lead mt-5">
With a fluent query api, a powerful sql analyzer, auto-updating streams and much more,
drift makes persistence fun. Scroll down to learn about its key features, or visit the
<a href="{{ 'docs/Getting started/index' | pageUrl }}">getting started guide</a> for a step-by-step guide on using drift.
</p>
</div> </div>
{% endblock %} {% endblock %}
{% block "blocks/lead.html" color="dark" %}
Drift is an easy to use, reactive persistence library for Flutter apps. Define tables in Dart or
SQL and enjoy a fluent query API, auto-updating streams and more!
{% endblock %}
{% block "blocks/section.html" color="primary" %} {% block "blocks/section.html" color="primary" %}
{% block "blocks/feature.html" icon="fas fa-lightbulb" title="Declarative tables, fluent queries" %} {% block "blocks/feature.html" icon="fas fa-lightbulb" title="Declarative tables, fluent queries" %}
{% block "blocks/markdown.html" %} {% block "blocks/markdown.html" %}
With drift, you can write your database tables in pure Dart without having to miss out on With drift, you can declare your database tables and queries in pure Dart without having to miss out on
advanced sqlite features. Drift will take care of creating the tables and generate code advanced SQL features. Drift will take care of creating the tables and generate code
that allows you run fluent queries on your data. that allows you run fluent queries on your data.
[Get started now]({{ "docs/Getting started/index.md" | pageUrl }}) [Get started now]({{ "docs/Getting started/index.md" | pageUrl }})
{% endblock %} {% endblock %}
{% endblock %} {% endblock %}
{% block "blocks/feature.html" icon="fas fa-database" title="Prefer SQL? Drift got you covered!" %} {% block "blocks/feature.html" icon="fas fa-database" title="Definitely relational" %}
{% block "blocks/markdown.html" %} {% block "blocks/markdown.html" %}
Drift contains a powerful sql parser and analyzer, allowing it to create typesafe APIs for all your sql queries. All sql queries are Drift is not the kind of ORM that tries to hide SQL away and then breaks down at the first
aggregation or non-obvious join.
Instead, drift embraces relational databases with an Dart API that's easy to learn
while still being close to SQL. Advanced expressions or subqueries are supported out of
the box.
{% endblock %}
{% endblock %}
{% block "blocks/feature.html" icon="fas fa-lightbulb" title="Safe schemas" %}
{% block "blocks/markdown.html" %}
A well-chosen SQL schema enables type-safe queries and avoids hard-to-spot mistakes.
Thanks to drift's extensive support for schema migrations, changing schemas is a safe
and easy process.
Further, drift provides a complete test toolkit to help you test migrations
between all your revisions.
[All about schema migrations]({{ "docs/Advanced Features/migrations.md" | pageUrl }})
{% endblock %}
{% endblock %}
{% block "blocks/feature.html" icon="fas fa-database" title="Prefer SQL? Drift's got you covered!" %}
{% block "blocks/markdown.html" %}
Drift ships a powerful sql parser and analyzer, allowing it to create typesafe methods for all your sql queries. All sql queries are
validated and analyzed during build-time, so drift can provide hints about potential errors quickly and generate efficient mapping validated and analyzed during build-time, so drift can provide hints about potential errors quickly and generate efficient mapping
code. code.
Of course, you can mix SQL and Dart to your liking.
[Learn more]({{ 'docs/Using SQL/index.md' | pageUrl }}) [Using SQL with Drift]({{ 'docs/Using SQL/index.md' | pageUrl }})
{% endblock %}
{% endblock %}
{% block "blocks/feature.html" icon="fas fa-lightbulb" title="Supported on your favorite platform" %}
{% block "blocks/markdown.html" %}
Drift's core APIs are written to support a range of database libraries as backends, it doesn't even require Flutter.
Drift has primary first-class support for Android, iOS, macOS, Linux Windows and the web.
Other database libraries can easily be integrated into drift as well.
[All platforms]({{ "docs/platforms.md" | pageUrl }})
{% endblock %} {% endblock %}
{% endblock %} {% endblock %}
@ -60,25 +89,3 @@ When using drift, working with databases in Dart is fun!
{% endblock %} {% endblock %}
{% endblock %} {% endblock %}
{% block "blocks/section.html" color="light" type="section" %}
{% block "blocks/markdown.html" %}
## Key features
Here are some of the many ways drift helps you write awesome database code:
* __Auto-updating streams__: With drift, any query - no matter how complex - can be turned into a stream that emits new data as the underlying data changes.
* __Polyglot__: Drift lets you write queries in a fluent Dart api or directly in SQL - you can even embed Dart expressions in SQL.
* __Boilerplate-free__: Stop writing mapping code yourself - drift can take of that. Drift generates Dart code around your data so you can focus
on building great apps.
* __Flexible__: Want to write queries in SQL? Drift verifies them at compile time and generates Dart apis for them. Prefer to write them in Dart?
Drift will generate efficient SQL for Dart queries too.
* __Easy to learn__: Instead of having to learn yet another ORM, drift lets you write queries in SQL and generates typesafe wrappers. Queries and tables
can also be written in Dart that looks similar to SQL without loosing type-safety.
* __Fast _and_ powerful__: With the new `ffi` backend, drift can outperform key-value stores without putting any compromises on the integrity
and flexibility that relational databases provide. Drift is the only major persistence library with builtin support for multiple isolates.
* __Well tested and production ready__: Each component of drift is verified by a wide range of unit and integration tests. Drift powers many Flutter apps
in production.
* __Cross-Platform__: Drift works on iOS, Android, Linux, macOS, Windows and on the web. It doesn't even require Flutter. See [supported platforms]({{ "docs/platforms.md" | pageUrl }}).
{% endblock %}
{% endblock %}

View File

@ -38,8 +38,6 @@ dev_dependencies:
drift_dev: drift_dev:
dependency_overrides: dependency_overrides:
# Waiting for dartdoc, https://github.com/dart-lang/dartdoc/pull/3033
analyzer: ^4.0.0
moor_generator: moor_generator:
path: ../moor_generator path: ../moor_generator
drift: drift:

31
docs/templates/partials/footer.html vendored Normal file
View File

@ -0,0 +1,31 @@
{% assign links = site.links %}
<footer class="bg-dark py-5 row d-print-none">
<div class="container-fluid mx-sm-5">
<div class="row">
<div class="col-6 col-sm-4 text-xs-center order-sm-2">
{% if links.user %}
{% assign links = links.user %}
{% include "partials/footer-links-block.html" %}
{% endif %}
</div>
<div class="col-6 col-sm-4 text-right text-xs-center order-sm-3">
{% if links.developer %}
{% assign links = links.developer %}
{% include "partials/footer-links-block.html" %}
{% endif %}
</div>
<div class="col-12 col-sm-4 text-center py-2 order-sm-2">
{% if site.copyright %}
<small class="text-white"><div>&copy; {{ site.copyright }}</div>
Except where otherwise noted, content on this site is licensed under a <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="license noopener noreferrer">CC BY 4.0</a> license.
Code snippets are marked with <a href="http://creativecommons.org/publicdomain/zero/1.0" target="_blank" rel="license noopener noreferrer">CC0 1.0</a>,
drift itself is <a href="https://github.com/simolus3/drift/blob/develop/LICENSE" rel="license" target="_blank">MIT-licensed</a>.
</small>
{% endif %}
{% if site.privacy_policy %}
<small class="ml-1"><a href="{{ site.privacy_policy }}" target="_blank">{{ "footer_privacy_policy" | i18n }}</a></small>
{% endif %}
</div>
</div>
</div>
</footer>

View File

@ -158,11 +158,19 @@ abstract class Table extends HasResultSet {
ColumnBuilder<bool> boolean() => _isGenerated(); ColumnBuilder<bool> boolean() => _isGenerated();
/// Use this as the body of a getter to declare a column that holds date and /// Use this as the body of a getter to declare a column that holds date and
/// time. Note that [DateTime] values are stored on a second-accuracy. /// time.
///
/// Drift supports two modes for storing date times: As unix timestamp with
/// second accuracy (the default) and as ISO 8601 string with microsecond
/// accuracy. For more information between the modes, and information on how
/// to change them, see [the documentation].
///
/// Note that [DateTime] values are stored on a second-accuracy.
/// Example (inside the body of a table class): /// Example (inside the body of a table class):
/// ``` /// ```
/// DateTimeColumn get accountCreatedAt => dateTime()(); /// DateTimeColumn get accountCreatedAt => dateTime()();
/// ``` /// ```
/// [the documentation]: https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/#supported-column-types
@protected @protected
ColumnBuilder<DateTime> dateTime() => _isGenerated(); ColumnBuilder<DateTime> dateTime() => _isGenerated();

View File

@ -188,7 +188,14 @@ abstract class ValueSerializer {
/// ///
/// To override the default serializer drift uses, you can change the /// To override the default serializer drift uses, you can change the
/// [DriftRuntimeOptions.defaultSerializer] field. /// [DriftRuntimeOptions.defaultSerializer] field.
const factory ValueSerializer.defaults() = _DefaultValueSerializer; ///
/// The [serializeDateTimeValuesAsString] option (which defaults to `false`)
/// describes whether [DateTime] values should be serialized to a unix
/// timestamp ([DateTime.millisecondsSinceEpoch]) or a string
/// ([DateTime.toIso8601String]).
/// In either case, date time values can be _deserialized_ from both formats.
const factory ValueSerializer.defaults(
{bool serializeDateTimeValuesAsString}) = _DefaultValueSerializer;
/// Converts the [value] to something that can be passed to /// Converts the [value] to something that can be passed to
/// [JsonCodec.encode]. /// [JsonCodec.encode].
@ -200,7 +207,9 @@ abstract class ValueSerializer {
} }
class _DefaultValueSerializer extends ValueSerializer { class _DefaultValueSerializer extends ValueSerializer {
const _DefaultValueSerializer(); final bool serializeDateTimeValuesAsString;
const _DefaultValueSerializer({this.serializeDateTimeValuesAsString = false});
@override @override
T fromJson<T>(dynamic json) { T fromJson<T>(dynamic json) {
@ -211,7 +220,11 @@ class _DefaultValueSerializer extends ValueSerializer {
final _typeList = <T>[]; final _typeList = <T>[];
if (_typeList is List<DateTime?>) { if (_typeList is List<DateTime?>) {
return DateTime.fromMillisecondsSinceEpoch(json as int) as T; if (json is int) {
return DateTime.fromMillisecondsSinceEpoch(json) as T;
} else {
return DateTime.parse(json.toString()) as T;
}
} }
if (_typeList is List<double?> && json is int) { if (_typeList is List<double?> && json is int) {
@ -231,7 +244,9 @@ class _DefaultValueSerializer extends ValueSerializer {
@override @override
dynamic toJson<T>(T value) { dynamic toJson<T>(T value) {
if (value is DateTime) { if (value is DateTime) {
return value.millisecondsSinceEpoch; return serializeDateTimeValuesAsString
? value.toIso8601String()
: value.millisecondsSinceEpoch;
} }
return value; return value;

View File

@ -48,6 +48,12 @@ void _testWith(TodoDb Function() openDb, {bool dateTimeAsText = false}) {
expect(await eval(Variable(local)), local); expect(await eval(Variable(local)), local);
}, },
); );
test('preserves milliseconds', () async {
final local = DateTime(2020, 09, 03, 23, 55, 0, 123);
expect(await eval(Variable(local)), local);
});
} }
test('plus and minus', () async { test('plus and minus', () async {

View File

@ -17,8 +17,8 @@ void main() {
await expectLater( await expectLater(
db.validateDatabaseSchema(), db.validateDatabaseSchema(),
throwsA(isA<SchemaMismatch>().having( throwsA(isA<SchemaMismatch>().having((e) => e.toString(), 'toString()',
(e) => e.toString(), 'toString()', contains('TEXT and INTEGER'))), contains('Expected TEXT, got INTEGER'))),
); );
}); });
} }

View File

@ -2,29 +2,37 @@ import 'package:drift/drift.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'generated/todos.dart'; import 'generated/todos.dart';
final DateTime someDate = DateTime(2019, 06, 08); final DateTime _someDate = DateTime(2019, 06, 08);
final TodoEntry someTodoEntry = TodoEntry( final TodoEntry _someTodoEntry = TodoEntry(
id: 3, id: 3,
title: null, title: null,
content: 'content', content: 'content',
targetDate: someDate, targetDate: _someDate,
category: 3, category: 3,
); );
final Map<String, dynamic> regularSerialized = { final Map<String, dynamic> _regularSerialized = {
'id': 3, 'id': 3,
'title': null, 'title': null,
'content': 'content', 'content': 'content',
'target_date': someDate.millisecondsSinceEpoch, 'target_date': _someDate.millisecondsSinceEpoch,
'category': 3, 'category': 3,
}; };
final Map<String, dynamic> customSerialized = { final Map<String, dynamic> _asTextSerialized = {
'id': 3,
'title': null,
'content': 'content',
'target_date': _someDate.toIso8601String(),
'category': 3,
};
final Map<String, dynamic> _customSerialized = {
'id': 3, 'id': 3,
'title': 'set to null', 'title': 'set to null',
'content': 'content', 'content': 'content',
'target_date': someDate.toIso8601String(), 'target_date': _someDate.toIso8601String(),
'category': 3, 'category': 3,
}; };
@ -61,7 +69,16 @@ void main() {
group('serialization', () { group('serialization', () {
test('with defaults', () { test('with defaults', () {
expect(someTodoEntry.toJson(), equals(regularSerialized)); expect(_someTodoEntry.toJson(), equals(_regularSerialized));
});
test('with default serializer, date as text', () {
expect(
_someTodoEntry.toJson(
serializer: const ValueSerializer.defaults(
serializeDateTimeValuesAsString: true)),
equals(_asTextSerialized),
);
}); });
test('applies json type converter', () { test('applies json type converter', () {
@ -71,20 +88,31 @@ void main() {
}); });
test('with custom serializer', () { test('with custom serializer', () {
expect(someTodoEntry.toJson(serializer: CustomSerializer()), expect(_someTodoEntry.toJson(serializer: CustomSerializer()),
equals(customSerialized)); equals(_customSerialized));
}); });
}); });
group('deserialization', () { group('deserialization', () {
test('with defaults', () { test('with defaults', () {
expect(TodoEntry.fromJson(regularSerialized), equals(someTodoEntry)); expect(TodoEntry.fromJson(_regularSerialized), equals(_someTodoEntry));
expect(TodoEntry.fromJson(_asTextSerialized), equals(_someTodoEntry));
});
test('with date-as-text serializer', () {
const serializer =
ValueSerializer.defaults(serializeDateTimeValuesAsString: true);
expect(TodoEntry.fromJson(_regularSerialized, serializer: serializer),
equals(_someTodoEntry));
expect(TodoEntry.fromJson(_asTextSerialized, serializer: serializer),
equals(_someTodoEntry));
}); });
test('with custom serializer', () { test('with custom serializer', () {
expect( expect(
TodoEntry.fromJson(customSerialized, serializer: CustomSerializer()), TodoEntry.fromJson(_customSerialized, serializer: CustomSerializer()),
equals(someTodoEntry)); equals(_someTodoEntry));
}); });
}); });
} }

View File

@ -337,7 +337,7 @@ class ColumnParser {
if (foundExplicitName != null) { if (foundExplicitName != null) {
name = ColumnName.explicitly(foundExplicitName); name = ColumnName.explicitly(foundExplicitName);
} else { } else {
name = ColumnName.implicitly(ReCase(getter.name.name).snakeCase); name = ColumnName.implicitly(ReCase(getter.name2.lexeme).snakeCase);
} }
final columnType = _startMethodToColumnType(foundStartMethod); final columnType = _startMethodToColumnType(foundStartMethod);
@ -424,7 +424,7 @@ class ColumnParser {
getter.documentationComment?.tokens.map((t) => t.toString()).join('\n'); getter.documentationComment?.tokens.map((t) => t.toString()).join('\n');
return DriftColumn( return DriftColumn(
type: columnType, type: columnType,
dartGetterName: getter.name.name, dartGetterName: getter.name2.lexeme,
name: name, name: name,
overriddenJsonName: _readJsonKey(element), overriddenJsonName: _readJsonKey(element),
customConstraints: foundCustomConstraint, customConstraints: foundCustomConstraint,
@ -456,9 +456,11 @@ class ColumnParser {
final annotations = getter.metadata; final annotations = getter.metadata;
final object = annotations.firstWhereOrNull((e) { final object = annotations.firstWhereOrNull((e) {
final value = e.computeConstantValue(); final value = e.computeConstantValue();
return value != null && final valueType = value?.type;
isFromMoor(value.type!) &&
value.type!.element!.name == 'JsonKey'; return valueType is InterfaceType &&
isFromDrift(valueType) &&
valueType.element2.name == 'JsonKey';
}); });
if (object == null) return null; if (object == null) return null;

View File

@ -85,7 +85,7 @@ class TableParser {
for (final annotation in element.metadata) { for (final annotation in element.metadata) {
final computed = annotation.computeConstantValue(); final computed = annotation.computeConstantValue();
final annotationClass = computed!.type!.element!.name; final annotationClass = computed!.type!.nameIfInterfaceType;
if (annotationClass == 'DataClassName') { if (annotationClass == 'DataClassName') {
dataClassName = computed; dataClassName = computed;
@ -124,8 +124,8 @@ class TableParser {
useRowClass.getField('generateInsertable')!.toBoolValue()!; useRowClass.getField('generateInsertable')!.toBoolValue()!;
if (type is InterfaceType) { if (type is InterfaceType) {
existingClass = FoundDartClass(type.element, type.typeArguments); existingClass = FoundDartClass(type.element2, type.typeArguments);
name = type.element.name; name = type.element2.name;
} else { } else {
base.step.reportError(ErrorInDartCode( base.step.reportError(ErrorInDartCode(
message: 'The @UseRowClass annotation must be used with a class', message: 'The @UseRowClass annotation must be used with a class',
@ -309,7 +309,7 @@ class TableParser {
Future<Iterable<DriftColumn>> _parseColumns(ClassElement element) async { Future<Iterable<DriftColumn>> _parseColumns(ClassElement element) async {
final columnNames = element.allSupertypes final columnNames = element.allSupertypes
.map((t) => t.element) .map((t) => t.element2)
.followedBy([element]) .followedBy([element])
.expand((e) => e.fields) .expand((e) => e.fields)
.where((field) => .where((field) =>
@ -350,10 +350,10 @@ class _DataClassInformation {
extension on Element { extension on Element {
bool get isFromDefaultTable { bool get isFromDefaultTable {
final parent = enclosingElement2; final parent = enclosingElement3;
return parent is ClassElement && return parent is ClassElement &&
parent.name == 'Table' && parent.name == 'Table' &&
isFromMoor(parent.thisType); isFromDrift(parent.thisType);
} }
} }

View File

@ -9,7 +9,7 @@ class UseDaoParser {
/// declared by that class and the referenced tables. /// declared by that class and the referenced tables.
Future<Dao?> parseDao(ClassElement element, ConstantReader annotation) async { Future<Dao?> parseDao(ClassElement element, ConstantReader annotation) async {
final dbType = element.allSupertypes final dbType = element.allSupertypes
.firstWhereOrNull((i) => i.element.name == 'DatabaseAccessor'); .firstWhereOrNull((i) => i.element2.name == 'DatabaseAccessor');
if (dbType == null) { if (dbType == null) {
step.reportError(ErrorInDartCode( step.reportError(ErrorInDartCode(

View File

@ -41,7 +41,7 @@ class ViewParser {
for (final annotation in element.metadata) { for (final annotation in element.metadata) {
final computed = annotation.computeConstantValue(); final computed = annotation.computeConstantValue();
final annotationClass = computed!.type!.element!.name; final annotationClass = computed!.type!.nameIfInterfaceType;
if (annotationClass == 'DriftView') { if (annotationClass == 'DriftView') {
driftView = computed; driftView = computed;
@ -80,8 +80,8 @@ class ViewParser {
useRowClass.getField('generateInsertable')!.toBoolValue()!; useRowClass.getField('generateInsertable')!.toBoolValue()!;
if (type is InterfaceType) { if (type is InterfaceType) {
existingClass = FoundDartClass(type.element, type.typeArguments); existingClass = FoundDartClass(type.element2, type.typeArguments);
name = type.element.name; name = type.element2.name;
} else { } else {
base.step.reportError(ErrorInDartCode( base.step.reportError(ErrorInDartCode(
message: 'The @UseRowClass annotation must be used with a class', message: 'The @UseRowClass annotation must be used with a class',
@ -100,7 +100,7 @@ class ViewParser {
Future<String> _parseViewName(ClassElement element) async { Future<String> _parseViewName(ClassElement element) async {
for (final annotation in element.metadata) { for (final annotation in element.metadata) {
final computed = annotation.computeConstantValue(); final computed = annotation.computeConstantValue();
final annotationClass = computed!.type!.element!.name; final annotationClass = computed!.type!.nameIfInterfaceType;
if (annotationClass == 'DriftView') { if (annotationClass == 'DriftView') {
final name = computed.getField('name')?.toStringValue(); final name = computed.getField('name')?.toStringValue();
@ -116,7 +116,7 @@ class ViewParser {
Future<Iterable<DriftColumn>> _parseColumns(ClassElement element) async { Future<Iterable<DriftColumn>> _parseColumns(ClassElement element) async {
final columnNames = element.allSupertypes final columnNames = element.allSupertypes
.map((t) => t.element) .map((t) => t.element2)
.followedBy([element]) .followedBy([element])
.expand((e) => e.fields) .expand((e) => e.fields)
.where((field) => .where((field) =>
@ -134,7 +134,7 @@ class ViewParser {
final results = await Future.wait(fields.map((field) async { final results = await Future.wait(fields.map((field) async {
final dartType = (field.type as InterfaceType).typeArguments[0]; final dartType = (field.type as InterfaceType).typeArguments[0];
final typeName = dartType.element!.name!; final typeName = dartType.nameIfInterfaceType!;
final sqlType = _dartTypeToColumnType(typeName); final sqlType = _dartTypeToColumnType(typeName);
if (sqlType == null) { if (sqlType == null) {
@ -181,7 +181,7 @@ class ViewParser {
Future<List<TableReferenceInDartView>> _parseStaticReferences( Future<List<TableReferenceInDartView>> _parseStaticReferences(
ClassElement element, List<DriftTable> tables) async { ClassElement element, List<DriftTable> tables) async {
return await Stream.fromIterable(element.allSupertypes return await Stream.fromIterable(element.allSupertypes
.map((t) => t.element) .map((t) => t.element2)
.followedBy([element]).expand((e) => e.fields)) .followedBy([element]).expand((e) => e.fields))
.asyncMap((field) => _getStaticReference(field, tables)) .asyncMap((field) => _getStaticReference(field, tables))
.where((ref) => ref != null) .where((ref) => ref != null)
@ -198,7 +198,7 @@ class ViewParser {
final type = tables.firstWhereOrNull( final type = tables.firstWhereOrNull(
(tbl) => tbl.fromClass!.name == node.returnType.toString()); (tbl) => tbl.fromClass!.name == node.returnType.toString());
if (type != null) { if (type != null) {
final name = node.name.toString(); final name = node.name2.lexeme;
return TableReferenceInDartView(type, name); return TableReferenceInDartView(type, name);
} }
} }

View File

@ -11,7 +11,7 @@ import 'package:drift_dev/src/analyzer/runner/steps.dart';
import 'helper.dart'; import 'helper.dart';
class FoundDartClass { class FoundDartClass {
final ClassElement classElement; final InterfaceElement classElement;
/// The instantiation of the [classElement], if the found type was a generic /// The instantiation of the [classElement], if the found type was a generic
/// typedef. /// typedef.
@ -230,8 +230,8 @@ void _checkType(
final isAllowedUint8List = typeConverter == null && final isAllowedUint8List = typeConverter == null &&
columnType == DriftSqlType.blob && columnType == DriftSqlType.blob &&
typeToCheck is InterfaceType && typeToCheck is InterfaceType &&
typeToCheck.element.name == 'Uint8List' && typeToCheck.element2.name == 'Uint8List' &&
typeToCheck.element.library.name == 'dart.typed_data'; typeToCheck.element2.library.name == 'dart.typed_data';
if (!typeSystem.isAssignableTo(expectedDartType.type, typeToCheck) && if (!typeSystem.isAssignableTo(expectedDartType.type, typeToCheck) &&
!isAllowedUint8List) { !isAllowedUint8List) {
@ -246,14 +246,14 @@ extension on TypeProvider {
case DriftSqlType.int: case DriftSqlType.int:
return intType; return intType;
case DriftSqlType.bigInt: case DriftSqlType.bigInt:
return intElement.library.getType('BigInt')!.instantiate( return intElement.library.getClass('BigInt')!.instantiate(
typeArguments: const [], nullabilitySuffix: NullabilitySuffix.none); typeArguments: const [], nullabilitySuffix: NullabilitySuffix.none);
case DriftSqlType.string: case DriftSqlType.string:
return stringType; return stringType;
case DriftSqlType.bool: case DriftSqlType.bool:
return boolType; return boolType;
case DriftSqlType.dateTime: case DriftSqlType.dateTime:
return intElement.library.getType('DateTime')!.instantiate( return intElement.library.getClass('DateTime')!.instantiate(
typeArguments: const [], nullabilitySuffix: NullabilitySuffix.none); typeArguments: const [], nullabilitySuffix: NullabilitySuffix.none);
case DriftSqlType.blob: case DriftSqlType.blob:
return listType(intType); return listType(intType);

View File

@ -28,8 +28,8 @@ String? parseCustomParentClass(String dartTypeName, DartObject dataClassName,
if (extending != null && !extending.isNull) { if (extending != null && !extending.isNull) {
final extendingType = extending.toTypeValue(); final extendingType = extending.toTypeValue();
if (extendingType is InterfaceType) { if (extendingType is InterfaceType) {
final superType = extendingType.allSupertypes final superType = extendingType.allSupertypes.any(
.any((type) => isFromMoor(type) && type.element.name == 'DataClass'); (type) => isFromDrift(type) && type.element2.name == 'DataClass');
if (!superType) { if (!superType) {
base.step.reportError( base.step.reportError(
ErrorInDartCode( ErrorInDartCode(
@ -52,10 +52,10 @@ String? parseCustomParentClass(String dartTypeName, DartObject dataClassName,
return null; return null;
} }
final className = extendingType.element.name; final className = extendingType.nameIfInterfaceType;
if (extendingType.typeArguments.length == 1) { if (extendingType.typeArguments.length == 1) {
final genericType = extendingType.typeArguments[0].element?.name; final genericType = extendingType.typeArguments[0];
if (genericType == 'Object' || genericType == 'dynamic') { if (genericType.isDartCoreObject || genericType.isDynamic) {
return '$className<$dartTypeName>'; return '$className<$dartTypeName>';
} else { } else {
base.step.reportError( base.step.reportError(

View File

@ -28,7 +28,7 @@ Future<FoundDartClass?> findDartClass(
} else if (foundElement is TypeAliasElement) { } else if (foundElement is TypeAliasElement) {
final innerType = foundElement.aliasedType; final innerType = foundElement.aliasedType;
if (innerType is InterfaceType) { if (innerType is InterfaceType) {
return FoundDartClass(innerType.element, innerType.typeArguments); return FoundDartClass(innerType.element2, innerType.typeArguments);
} }
} }
} }

View File

@ -122,7 +122,7 @@ class ParseDartStep extends Step {
Future<List<DriftTable>> parseTables( Future<List<DriftTable>> parseTables(
Iterable<DartType> types, Element initializedBy) { Iterable<DartType> types, Element initializedBy) {
return Future.wait(types.map((type) { return Future.wait(types.map((type) {
if (!_tableTypeChecker.isAssignableFrom(type.element!)) { if (!_tableTypeChecker.isAssignableFromType(type)) {
reportError(ErrorInDartCode( reportError(ErrorInDartCode(
severity: Severity.criticalError, severity: Severity.criticalError,
message: 'The type $type is not a moor table', message: 'The type $type is not a moor table',
@ -130,7 +130,7 @@ class ParseDartStep extends Step {
)); ));
return Future.value(null); return Future.value(null);
} else { } else {
return _parseTable(type.element as ClassElement); return _parseTable((type as InterfaceType).element2 as ClassElement);
} }
})).then((list) { })).then((list) {
// only keep tables that were resolved successfully // only keep tables that were resolved successfully
@ -145,7 +145,7 @@ class ParseDartStep extends Step {
Future<List<MoorView>> parseViews(Iterable<DartType> types, Future<List<MoorView>> parseViews(Iterable<DartType> types,
Element initializedBy, List<DriftTable> tables) { Element initializedBy, List<DriftTable> tables) {
return Future.wait(types.map((type) { return Future.wait(types.map((type) {
if (!_viewTypeChecker.isAssignableFrom(type.element!)) { if (!_viewTypeChecker.isAssignableFromType(type)) {
reportError(ErrorInDartCode( reportError(ErrorInDartCode(
severity: Severity.criticalError, severity: Severity.criticalError,
message: 'The type $type is not a drift view', message: 'The type $type is not a drift view',
@ -153,7 +153,8 @@ class ParseDartStep extends Step {
)); ));
return Future.value(null); return Future.value(null);
} else { } else {
return _parseView(type.element as ClassElement, tables); return _parseView(
(type as InterfaceType).element2 as ClassElement, tables);
} }
})).then((list) { })).then((list) {
// only keep tables that were resolved successfully // only keep tables that were resolved successfully

View File

@ -21,7 +21,7 @@ class DaoGenerator extends Generator implements BaseGenerator {
final daoName = element!.displayName; final daoName = element!.displayName;
final dbTypeName = dao.dbClass.codeString(writer.generationOptions); final dbTypeName = dao.dbClass.codeString();
classScope.leaf().write('mixin _\$${daoName}Mixin on ' classScope.leaf().write('mixin _\$${daoName}Mixin on '
'DatabaseAccessor<$dbTypeName> {\n'); 'DatabaseAccessor<$dbTypeName> {\n');

View File

@ -413,7 +413,8 @@ class _Moor2DriftDartRewriter extends GeneralizingAstVisitor<void> {
if (type is! InterfaceType) continue; if (type is! InterfaceType) continue;
if (type.element.library.isDartCore && type.element.name == 'pragma') { if (type.element2.library.isDartCore &&
type.element2.name == 'pragma') {
final name = value.getField('name')!.toStringValue()!; final name = value.getField('name')!.toStringValue()!;
if (name == 'moor2drift') { if (name == 'moor2drift') {
@ -452,8 +453,8 @@ class _Moor2DriftDartRewriter extends GeneralizingAstVisitor<void> {
if (type is! InterfaceType || if (type is! InterfaceType ||
// note that even old moor code uses these names since UseMoor/UseDao // note that even old moor code uses these names since UseMoor/UseDao
// are type aliases to the new interfaces. // are type aliases to the new interfaces.
(type.element.name != 'DriftDatabase' && (type.element2.name != 'DriftDatabase' &&
type.element.name != 'DriftAccessor')) { type.element2.name != 'DriftAccessor')) {
return; return;
} }

View File

@ -40,7 +40,7 @@ abstract class DriftEntityWithResultSet extends DriftSchemaEntity {
/// The type name of the Dart row class for this result set. /// The type name of the Dart row class for this result set.
/// ///
/// This may contain generics. /// This may contain generics.
String dartTypeCode([GenerationOptions options = const GenerationOptions()]); String dartTypeCode();
/// The name of the Dart class storing additional properties like type /// The name of the Dart class storing additional properties like type
/// converters. /// converters.
@ -65,7 +65,7 @@ abstract class DriftEntityWithResultSet extends DriftSchemaEntity {
/// Information used by the generator to generate code for a custom data class /// Information used by the generator to generate code for a custom data class
/// written by users. /// written by users.
class ExistingRowClass { class ExistingRowClass {
final ClassElement targetClass; final InterfaceElement targetClass;
/// The Dart types that should be used to instantiate the [targetClass]. /// The Dart types that should be used to instantiate the [targetClass].
final List<DartType> typeInstantiation; final List<DartType> typeInstantiation;

View File

@ -142,11 +142,11 @@ abstract class SqlQuery {
} }
if (resultSet.matchingTable != null) { if (resultSet.matchingTable != null) {
return resultSet.matchingTable!.table.dartTypeCode(options); return resultSet.matchingTable!.table.dartTypeCode();
} }
if (resultSet.singleColumn) { if (resultSet.singleColumn) {
return resultSet.columns.single.dartTypeCode(options); return resultSet.columns.single.dartTypeCode();
} }
return resultClassName; return resultClassName;
@ -593,7 +593,7 @@ abstract class FoundElement {
bool get hidden => false; bool get hidden => false;
/// Dart code for a type representing tis element. /// Dart code for a type representing tis element.
String dartTypeCode([GenerationOptions options = const GenerationOptions()]); String dartTypeCode();
} }
/// A semantic interpretation of a [Variable] in a sql statement. /// A semantic interpretation of a [Variable] in a sql statement.
@ -676,8 +676,8 @@ class FoundVariable extends FoundElement implements HasType {
} }
@override @override
String dartTypeCode([GenerationOptions options = const GenerationOptions()]) { String dartTypeCode() {
return OperationOnTypes(this).dartTypeCode(options); return OperationOnTypes(this).dartTypeCode();
} }
} }

View File

@ -2,7 +2,6 @@ import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/dart/element/type.dart';
import 'package:drift_dev/src/model/model.dart'; import 'package:drift_dev/src/model/model.dart';
import 'package:drift_dev/src/utils/type_utils.dart'; import 'package:drift_dev/src/utils/type_utils.dart';
import 'package:drift_dev/writer.dart';
/// Something that has a type. /// Something that has a type.
/// ///
@ -60,14 +59,14 @@ class DriftDartType {
return type.getDisplayString(withNullability: withNullability); return type.getDisplayString(withNullability: withNullability);
} }
String codeString([GenerationOptions options = const GenerationOptions()]) { String codeString() {
if (overiddenSource != null) { if (overiddenSource != null) {
if (nullabilitySuffix == NullabilitySuffix.star) { if (nullabilitySuffix == NullabilitySuffix.star) {
return getDisplayString(withNullability: false); return getDisplayString(withNullability: false);
} }
return getDisplayString(withNullability: true); return getDisplayString(withNullability: true);
} else { } else {
return type.codeString(options); return type.codeString();
} }
} }
} }
@ -127,10 +126,10 @@ extension OperationOnTypes on HasType {
/// The dart type that matches the values of this column. For instance, if a /// The dart type that matches the values of this column. For instance, if a
/// table has declared an `IntColumn`, the matching dart type name would be /// table has declared an `IntColumn`, the matching dart type name would be
/// [int]. /// [int].
String dartTypeCode([GenerationOptions options = const GenerationOptions()]) { String dartTypeCode() {
final converter = typeConverter; final converter = typeConverter;
if (converter != null) { if (converter != null) {
var inner = converter.dartType.codeString(options); var inner = converter.dartType.codeString();
if (converter.canBeSkippedForNulls && nullable) inner += '?'; if (converter.canBeSkippedForNulls && nullable) inner += '?';
return isArray ? 'List<$inner>' : inner; return isArray ? 'List<$inner>' : inner;
} }

View File

@ -4,7 +4,6 @@ import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart'; import 'package:analyzer/dart/element/type_provider.dart';
import 'package:drift_dev/src/model/table.dart'; import 'package:drift_dev/src/model/table.dart';
import 'package:drift_dev/src/utils/type_utils.dart'; import 'package:drift_dev/src/utils/type_utils.dart';
import 'package:drift_dev/src/writer/writer.dart';
import 'types.dart'; import 'types.dart';
@ -92,12 +91,12 @@ class UsedTypeConverter {
bool nullable, bool nullable,
TypeProvider typeProvider, TypeProvider typeProvider,
) { ) {
if (enumType.element is! ClassElement) { if (enumType is! InterfaceType) {
throw InvalidTypeForEnumConverterException('Not a class', enumType); throw InvalidTypeForEnumConverterException('Not a class', enumType);
} }
final creatingClass = enumType.element as ClassElement; final creatingClass = enumType.element2;
if (!creatingClass.isEnum) { if (creatingClass is! EnumElement) {
throw InvalidTypeForEnumConverterException('Not an enum', enumType); throw InvalidTypeForEnumConverterException('Not an enum', enumType);
} }
@ -132,23 +131,22 @@ class UsedTypeConverter {
return dartTypeIsNullable || (canBeSkippedForNulls && nullableInSql); return dartTypeIsNullable || (canBeSkippedForNulls && nullableInSql);
} }
String dartTypeCode(GenerationOptions options, bool nullableInSql) { String dartTypeCode(bool nullableInSql) {
var type = dartType.codeString(options); var type = dartType.codeString();
if (canBeSkippedForNulls && nullableInSql) type += '?'; if (canBeSkippedForNulls && nullableInSql) type += '?';
return type; return type;
} }
/// A suitable typename to store an instance of the type converter used here. /// A suitable typename to store an instance of the type converter used here.
String converterNameInCode(GenerationOptions options, String converterNameInCode({bool makeNullable = false}) {
{bool makeNullable = false}) {
var sqlDartType = sqlType.getDisplayString(withNullability: true); var sqlDartType = sqlType.getDisplayString(withNullability: true);
if (makeNullable) sqlDartType += '?'; if (makeNullable) sqlDartType += '?';
final className = final className =
alsoAppliesToJsonConversion ? 'JsonTypeConverter' : 'TypeConverter'; alsoAppliesToJsonConversion ? 'JsonTypeConverter' : 'TypeConverter';
return '$className<${dartTypeCode(options, makeNullable)}, $sqlDartType>'; return '$className<${dartTypeCode(makeNullable)}, $sqlDartType>';
} }
} }

View File

@ -166,7 +166,7 @@ class FindSchemaDifferences {
if (refType != actType) { if (refType != actType) {
return FoundDifference( return FoundDifference(
'Different types: ${ref.typeName} and ${act.typeName}'); 'Different types: Expected ${ref.typeName}, got ${act.typeName}');
} }
try { try {
@ -174,19 +174,20 @@ class FindSchemaDifferences {
} catch (e) { } catch (e) {
final firstSpan = ref.constraints.spanOrNull?.text ?? ''; final firstSpan = ref.constraints.spanOrNull?.text ?? '';
final secondSpan = act.constraints.spanOrNull?.text ?? ''; final secondSpan = act.constraints.spanOrNull?.text ?? '';
return FoundDifference('Not equal: `$firstSpan` and `$secondSpan`'); return FoundDifference(
'Not equal: `$firstSpan` (expected) and `$secondSpan` (actual)');
} }
return const Success(); return const Success();
} }
CompareResult _compareByAst(AstNode a, AstNode b) { CompareResult _compareByAst(AstNode reference, AstNode actual) {
try { try {
enforceEqual(a, b); enforceEqual(reference, actual);
return const Success(); return const Success();
} catch (e) { } catch (e) {
return FoundDifference( return FoundDifference('Not equal: Expected `${reference.span?.text}`, '
'Not equal: `${a.span?.text}` and `${b.span?.text}`'); 'got `${actual.span?.text}`');
} }
} }

View File

@ -1,34 +1,40 @@
import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/dart/element/type.dart';
import 'package:drift_dev/src/writer/writer.dart';
bool isFromMoor(DartType type) { bool isFromDrift(DartType type) {
final firstComponent = type.element?.library?.location?.components.first; if (type is! InterfaceType) return false;
final firstComponent = type.element2.library.location?.components.first;
if (firstComponent == null) return false; if (firstComponent == null) return false;
return firstComponent.contains('drift') || firstComponent.contains('moor'); return firstComponent.contains('drift') || firstComponent.contains('moor');
} }
bool isColumn(DartType type) { bool isColumn(DartType type) {
final name = type.element?.name ?? ''; final name = type.nameIfInterfaceType;
return isFromMoor(type) && return isFromDrift(type) &&
name != null &&
name.contains('Column') && name.contains('Column') &&
!name.contains('Builder'); !name.contains('Builder');
} }
bool isExpression(DartType type) { bool isExpression(DartType type) {
final name = type.element?.name ?? ''; final name = type.nameIfInterfaceType;
return isFromMoor(type) && name.startsWith('Expression'); return name != null && isFromDrift(type) && name.startsWith('Expression');
} }
extension TypeUtils on DartType { extension TypeUtils on DartType {
String? get nameIfInterfaceType {
final $this = this;
return $this is InterfaceType ? $this.element2.name : null;
}
String get userVisibleName => getDisplayString(withNullability: true); String get userVisibleName => getDisplayString(withNullability: true);
/// How this type should look like in generated code. /// How this type should look like in generated code.
String codeString([GenerationOptions options = const GenerationOptions()]) { String codeString() {
if (nullabilitySuffix == NullabilitySuffix.star) { if (nullabilitySuffix == NullabilitySuffix.star) {
// We can't actually use the legacy star in code, so don't show it. // We can't actually use the legacy star in code, so don't show it.
return getDisplayString(withNullability: false); return getDisplayString(withNullability: false);

View File

@ -99,7 +99,7 @@ class DatabaseWriter {
// Write fields to access an dao. We use a lazy getter for that. // Write fields to access an dao. We use a lazy getter for that.
for (final dao in db.daos) { for (final dao in db.daos) {
final typeName = dao.codeString(scope.generationOptions); final typeName = dao.codeString();
final getterName = ReCase(typeName).camelCase; final getterName = ReCase(typeName).camelCase;
final databaseImplName = db.fromClass!.name; final databaseImplName = db.fromClass!.name;

View File

@ -293,7 +293,7 @@ class QueryWriter {
final namedElements = <FoundElement>[]; final namedElements = <FoundElement>[];
String typeFor(FoundElement element) { String typeFor(FoundElement element) {
var type = element.dartTypeCode(scope.generationOptions); var type = element.dartTypeCode();
if (element is FoundDartPlaceholder && if (element is FoundDartPlaceholder &&
element.writeAsScopedFunction(options)) { element.writeAsScopedFunction(options)) {

View File

@ -30,7 +30,7 @@ class ResultSetWriter {
// write fields // write fields
for (final column in resultSet.columns) { for (final column in resultSet.columns) {
final name = resultSet.dartNameFor(column); final name = resultSet.dartNameFor(column);
final runtimeType = column.dartTypeCode(scope.generationOptions); final runtimeType = column.dartTypeCode();
into.write('$modifier $runtimeType $name\n;'); into.write('$modifier $runtimeType $name\n;');
@ -40,7 +40,7 @@ class ResultSetWriter {
for (final nested in resultSet.nestedResults) { for (final nested in resultSet.nestedResults) {
if (nested is NestedResultTable) { if (nested is NestedResultTable) {
var typeName = nested.table.dartTypeCode(scope.generationOptions); var typeName = nested.table.dartTypeCode();
final fieldName = nested.dartFieldName; final fieldName = nested.dartFieldName;
if (nested.isNullable) { if (nested.isNullable) {

View File

@ -48,7 +48,7 @@ class DataClassWriter {
_buffer.write('${column.documentationComment}\n'); _buffer.write('${column.documentationComment}\n');
} }
final modifier = scope.options.fieldModifier; final modifier = scope.options.fieldModifier;
_buffer.write('$modifier ${column.dartTypeCode(scope.generationOptions)} ' _buffer.write('$modifier ${column.dartTypeCode()} '
'${column.dartGetterName}; \n'); '${column.dartGetterName}; \n');
} }
@ -120,7 +120,7 @@ class DataClassWriter {
typeConverter.tableAndField(forNullableColumn: column.nullable); typeConverter.tableAndField(forNullableColumn: column.nullable);
deserialized = '$converterField.fromJson($fromConverter)'; deserialized = '$converterField.fromJson($fromConverter)';
} else { } else {
final type = column.dartTypeCode(scope.generationOptions); final type = column.dartTypeCode();
deserialized = "serializer.fromJson<$type>(json['$jsonKey'])"; deserialized = "serializer.fromJson<$type>(json['$jsonKey'])";
} }
@ -151,7 +151,7 @@ class DataClassWriter {
final getter = column.dartGetterName; final getter = column.dartGetterName;
final needsThis = getter == 'serializer'; final needsThis = getter == 'serializer';
var value = needsThis ? 'this.$getter' : getter; var value = needsThis ? 'this.$getter' : getter;
var dartType = column.dartTypeCode(scope.generationOptions); var dartType = column.dartTypeCode();
final typeConverter = column.typeConverter; final typeConverter = column.typeConverter;
if (typeConverter != null && typeConverter.alsoAppliesToJsonConversion) { if (typeConverter != null && typeConverter.alsoAppliesToJsonConversion) {
@ -177,7 +177,7 @@ class DataClassWriter {
final last = i == columns.length - 1; final last = i == columns.length - 1;
final isNullable = column.nullableInDart; final isNullable = column.nullableInDart;
final typeName = column.dartTypeCode(scope.generationOptions); final typeName = column.dartTypeCode();
if (wrapNullableInValue && isNullable) { if (wrapNullableInValue && isNullable) {
_buffer _buffer
..write('Value<$typeName> ${column.dartGetterName} ') ..write('Value<$typeName> ${column.dartGetterName} ')

View File

@ -100,7 +100,7 @@ abstract class TableOrViewWriter {
if (converter != null) { if (converter != null) {
// Generate a GeneratedColumnWithTypeConverter instance, as it has // Generate a GeneratedColumnWithTypeConverter instance, as it has
// additional methods to check for equality against a mapped value. // additional methods to check for equality against a mapped value.
final mappedType = converter.dartTypeCode(options, column.nullable); final mappedType = converter.dartTypeCode(column.nullable);
final converterCode = final converterCode =
converter.tableAndField(forNullableColumn: column.nullable); converter.tableAndField(forNullableColumn: column.nullable);
@ -134,7 +134,7 @@ abstract class TableOrViewWriter {
return; return;
} }
final dataClassName = tableOrView.dartTypeCode(scope.generationOptions); final dataClassName = tableOrView.dartTypeCode();
buffer buffer
..write('@override\n$dataClassName map(Map<String, dynamic> data, ' ..write('@override\n$dataClassName map(Map<String, dynamic> data, '
@ -321,7 +321,7 @@ class TableWriter extends TableOrViewWriter {
void _writeConvertersAsStaticFields() { void _writeConvertersAsStaticFields() {
for (final converter in table.converters) { for (final converter in table.converters) {
final typeName = converter.converterNameInCode(scope.generationOptions); final typeName = converter.converterNameInCode();
final code = converter.expression; final code = converter.expression;
buffer.write('static $typeName ${converter.fieldName} = $code;'); buffer.write('static $typeName ${converter.fieldName} = $code;');
@ -334,8 +334,8 @@ class TableWriter extends TableOrViewWriter {
if (converter != null && if (converter != null &&
converter.canBeSkippedForNulls && converter.canBeSkippedForNulls &&
column.nullable) { column.nullable) {
final nullableTypeName = converter final nullableTypeName =
.converterNameInCode(scope.generationOptions, makeNullable: true); converter.converterNameInCode(makeNullable: true);
final wrap = converter.alsoAppliesToJsonConversion final wrap = converter.alsoAppliesToJsonConversion
? 'JsonTypeConverter.asNullable' ? 'JsonTypeConverter.asNullable'

View File

@ -43,7 +43,7 @@ class UpdateCompanionWriter {
void _writeFields() { void _writeFields() {
for (final column in columns) { for (final column in columns) {
final modifier = scope.options.fieldModifier; final modifier = scope.options.fieldModifier;
final type = column.dartTypeCode(scope.generationOptions); final type = column.dartTypeCode();
_buffer.write('$modifier Value<$type> ${column.dartGetterName};\n'); _buffer.write('$modifier Value<$type> ${column.dartGetterName};\n');
} }
} }
@ -83,7 +83,7 @@ class UpdateCompanionWriter {
if (table.isColumnRequiredForInsert(column)) { if (table.isColumnRequiredForInsert(column)) {
requiredColumns.add(column); requiredColumns.add(column);
final typeName = column.dartTypeCode(scope.generationOptions); final typeName = column.dartTypeCode();
_buffer.write('required $typeName $param,'); _buffer.write('required $typeName $param,');
} else { } else {
@ -151,7 +151,7 @@ class UpdateCompanionWriter {
} }
first = false; first = false;
final typeName = column.dartTypeCode(scope.generationOptions); final typeName = column.dartTypeCode();
_buffer.write('Value<$typeName>? ${column.dartGetterName}'); _buffer.write('Value<$typeName>? ${column.dartGetterName}');
} }

View File

@ -30,7 +30,7 @@ dependencies:
sqlparser: ^0.22.0 sqlparser: ^0.22.0
# Dart analysis # Dart analysis
analyzer: "^4.3.0" analyzer: "^4.4.0"
analyzer_plugin: ^0.11.0 analyzer_plugin: ^0.11.0
source_span: ^1.5.5 source_span: ^1.5.5
package_config: ^2.0.0 package_config: ^2.0.0

View File

@ -44,7 +44,7 @@ void main() {
expect(parser.returnExpressionOfMethod(node)!.toSource(), source); expect(parser.returnExpressionOfMethod(node)!.toSource(), source);
} }
final testClass = library.getType('Test'); final testClass = library.getClass('Test');
await _verifyReturnExpressionMatches( await _verifyReturnExpressionMatches(
testClass!.getGetter('getter')!, "'foo'"); testClass!.getGetter('getter')!, "'foo'");

View File

@ -146,7 +146,7 @@ void main() {
}); });
Future<DriftTable?> parse(String name) async { Future<DriftTable?> parse(String name) async {
return parser.parseTable(dartStep.library.getType(name)!); return parser.parseTable(dartStep.library.getClass(name)!);
} }
group('table names', () { group('table names', () {

View File

@ -1,5 +1,6 @@
@Tags(['analyzer']) @Tags(['analyzer'])
import 'package:drift_dev/src/analyzer/runner/results.dart'; import 'package:drift_dev/src/analyzer/runner/results.dart';
import 'package:drift_dev/src/utils/type_utils.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../utils.dart'; import '../utils.dart';
@ -48,7 +49,7 @@ class ProductsDao extends BaseProductsDao with _$ProductDaoMixin {
expect(file.errors.errors, isEmpty); expect(file.errors.errors, isEmpty);
final dao = (file.currentResult as ParsedDartFile).declaredDaos.single; final dao = (file.currentResult as ParsedDartFile).declaredDaos.single;
expect(dao.dbClass.element!.name, 'MyDatabase'); expect(dao.dbClass.nameIfInterfaceType, 'MyDatabase');
state.close(); state.close();
}); });

View File

@ -70,7 +70,7 @@ void main() {
expect(result, hasChanges); expect(result, hasChanges);
expect( expect(
result.describe(), result.describe(),
contains('Different types: TEXT and INTEGER'), contains('Different types: Expected TEXT, got INTEGER'),
); );
}); });
@ -83,7 +83,8 @@ void main() {
expect(result, hasChanges); expect(result, hasChanges);
expect( expect(
result.describe(), result.describe(),
contains('Not equal: `PRIMARY KEY NOT NULL` and ``'), contains(
'Not equal: `PRIMARY KEY NOT NULL` (expected) and `` (actual)'),
); );
}); });
}); });

View File

@ -4,6 +4,7 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/memory_file_system.dart'; import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:build_test/build_test.dart'; import 'package:build_test/build_test.dart';
import 'package:collection/collection.dart';
import 'package:drift_dev/src/backends/build/drift_builder.dart'; import 'package:drift_dev/src/backends/build/drift_builder.dart';
import 'package:pub_semver/pub_semver.dart'; import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -81,15 +82,18 @@ class _GeneratesConstDataClasses extends Matcher {
final definedClasses = parsed.declarations.whereType<ClassDeclaration>(); final definedClasses = parsed.declarations.whereType<ClassDeclaration>();
for (final definedClass in definedClasses) { for (final definedClass in definedClasses) {
if (expectedWithConstConstructor.contains(definedClass.name.name)) { if (expectedWithConstConstructor.contains(definedClass.name2.lexeme)) {
final constructor = definedClass.getConstructor(null); final constructor = definedClass.members
.whereType<ConstructorDeclaration>()
.firstWhereOrNull((e) => e.name2 == null);
if (constructor?.constKeyword == null) { if (constructor?.constKeyword == null) {
matchState['desc'] = 'Constructor ${definedClass.name.name} is not ' matchState['desc'] =
'Constructor ${definedClass.name2.lexeme} is not '
'const.'; 'const.';
return false; return false;
} }
remaining.remove(definedClass.name.name); remaining.remove(definedClass.name2.lexeme);
} }
} }

View File

@ -80,25 +80,26 @@ class _GeneratesWithoutFinalFields extends Matcher {
final definedClasses = parsed.declarations.whereType<ClassDeclaration>(); final definedClasses = parsed.declarations.whereType<ClassDeclaration>();
for (final definedClass in definedClasses) { for (final definedClass in definedClasses) {
if (expectedWithoutFinals.contains(definedClass.name.name)) { final definedClassName = definedClass.name2.lexeme;
if (expectedWithoutFinals.contains(definedClassName)) {
for (final member in definedClass.members) { for (final member in definedClass.members) {
if (member is FieldDeclaration) { if (member is FieldDeclaration) {
if (member.fields.isFinal) { if (member.fields.isFinal) {
matchState['desc'] = matchState['desc'] =
'Field ${member.fields.variables.first.name.name} in ' 'Field ${member.fields.variables.first.name2.lexeme} in '
'${definedClass.name.name} is final.'; '$definedClassName is final.';
return false; return false;
} }
} else if (member is ConstructorDeclaration) { } else if (member is ConstructorDeclaration) {
if (member.constKeyword != null) { if (member.constKeyword != null) {
matchState['desc'] = 'Constructor ${member.name?.name ?? ''} in ' matchState['desc'] = 'Constructor ${member.name2?.lexeme ?? ''} '
'${definedClass.name.name} is constant.'; 'in $definedClassName is constant.';
return false; return false;
} }
} }
} }
remaining.remove(definedClass.name.name); remaining.remove(definedClassName);
} }
} }