mirror of https://github.com/AMT-Cheif/drift.git
Begin optional modular code generation
This commit is contained in:
parent
f41cff5fc6
commit
f4e45584ec
|
@ -0,0 +1,66 @@
|
||||||
|
/// Internal library used by generated code when drift's modular code generation
|
||||||
|
/// is enabled.
|
||||||
|
///
|
||||||
|
/// This library is not part of drift's public API and should not be imported
|
||||||
|
/// manually.
|
||||||
|
library drift.internal.modules;
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
final _databaseElementCache = Expando<_DatabaseElementCache>();
|
||||||
|
|
||||||
|
/// A database accessor implicitly created by a `.drift` file.
|
||||||
|
///
|
||||||
|
/// When modular code generation is enabled, drift will emit a file with a
|
||||||
|
/// [ModularAccessor] for each drift file instead of generating all the code for
|
||||||
|
/// a database into a single file.
|
||||||
|
class ModularAccessor extends DatabaseAccessor<GeneratedDatabase> {
|
||||||
|
/// Default constructor - create an accessor from the [attachedDatabase].
|
||||||
|
ModularAccessor(super.attachedDatabase);
|
||||||
|
|
||||||
|
/// Find a result set by its [name] in the database. The result is cached.
|
||||||
|
T resultSet<T extends ResultSetImplementation>(String name) {
|
||||||
|
return attachedDatabase.resultSet(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find an accessor by its [name] in the database, or create it with
|
||||||
|
/// [create]. The result will be cached.
|
||||||
|
T accessor<T extends DatabaseAccessor>(
|
||||||
|
String name, T Function(GeneratedDatabase) create) {
|
||||||
|
return attachedDatabase.accessor<T>(name, create);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look up cached elements or accessors from a database.
|
||||||
|
/// This extension is meant to be used by drift-generated code.
|
||||||
|
extension ReadDatabaseContainer on GeneratedDatabase {
|
||||||
|
_DatabaseElementCache get _cache {
|
||||||
|
return _databaseElementCache[attachedDatabase] ??=
|
||||||
|
_DatabaseElementCache(attachedDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a result set by its [name] in the database. The result is cached.
|
||||||
|
T resultSet<T extends ResultSetImplementation>(String name) {
|
||||||
|
return _cache.knownEntities[name]! as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find an accessor by its [name] in the database, or create it with
|
||||||
|
/// [create]. The result will be cached.
|
||||||
|
T accessor<T extends DatabaseAccessor>(
|
||||||
|
String name, T Function(GeneratedDatabase) create) {
|
||||||
|
final cache = _cache.knownAccessors;
|
||||||
|
|
||||||
|
return cache.putIfAbsent(name, () => create(attachedDatabase)) as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DatabaseElementCache {
|
||||||
|
final Map<String, DatabaseSchemaEntity> knownEntities;
|
||||||
|
final Map<String, DatabaseAccessor> knownAccessors = {};
|
||||||
|
|
||||||
|
_DatabaseElementCache(GeneratedDatabase database)
|
||||||
|
: knownEntities = {
|
||||||
|
for (final entity in database.allSchemaEntities)
|
||||||
|
entity.entityName: entity
|
||||||
|
};
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ builders:
|
||||||
build_to: cache
|
build_to: cache
|
||||||
applies_builders: ["drift_dev:cleanup"]
|
applies_builders: ["drift_dev:cleanup"]
|
||||||
|
|
||||||
|
# Regular build flow, emitting a shared part file for source_gen to pick up
|
||||||
drift_dev:
|
drift_dev:
|
||||||
import: "package:drift_dev/integrations/build.dart"
|
import: "package:drift_dev/integrations/build.dart"
|
||||||
builder_factories: ["analyzer", "driftBuilder"]
|
builder_factories: ["analyzer", "driftBuilder"]
|
||||||
|
@ -31,6 +32,8 @@ builders:
|
||||||
required_inputs: [".drift_prep.json"]
|
required_inputs: [".drift_prep.json"]
|
||||||
applies_builders: ["source_gen:combining_builder", ":preparing_builder"]
|
applies_builders: ["source_gen:combining_builder", ":preparing_builder"]
|
||||||
|
|
||||||
|
# Similar to the regular builder, but emitting a direct part file instead of a
|
||||||
|
# shared part file.
|
||||||
not_shared:
|
not_shared:
|
||||||
import: "package:drift_dev/integrations/build.dart"
|
import: "package:drift_dev/integrations/build.dart"
|
||||||
builder_factories: ["driftBuilderNotShared"]
|
builder_factories: ["driftBuilderNotShared"]
|
||||||
|
@ -39,6 +42,26 @@ builders:
|
||||||
auto_apply: none
|
auto_apply: none
|
||||||
required_inputs: [".drift_prep.json"]
|
required_inputs: [".drift_prep.json"]
|
||||||
|
|
||||||
|
# A work-in-progress builder that emits standalone Dart libraries for each
|
||||||
|
# .drift file.
|
||||||
|
modular:
|
||||||
|
import: "package:drift_dev/integrations/build.dart"
|
||||||
|
builder_factories: ["modular"]
|
||||||
|
build_extensions: {".dart": [".drift.dart"]}
|
||||||
|
build_to: source
|
||||||
|
auto_apply: none
|
||||||
|
required_inputs: [".drift.drift_module.json"]
|
||||||
|
applies_builders: [":analyzer"]
|
||||||
|
analyzer:
|
||||||
|
import: "package:drift_dev/integrations/build.dart"
|
||||||
|
builder_factories: ["analyzer"]
|
||||||
|
build_extensions:
|
||||||
|
".dart": [".dart.drift_module.json"]
|
||||||
|
".drift": [".drift.drift_module.json"]
|
||||||
|
build_to: cache
|
||||||
|
auto_apply: none
|
||||||
|
required_inputs: [".drift_prep.json"]
|
||||||
|
|
||||||
post_process_builders:
|
post_process_builders:
|
||||||
cleanup:
|
cleanup:
|
||||||
import: "package:drift_dev/integrations/build.dart"
|
import: "package:drift_dev/integrations/build.dart"
|
||||||
|
|
|
@ -13,6 +13,9 @@ Builder driftBuilder(BuilderOptions options) =>
|
||||||
Builder driftBuilderNotShared(BuilderOptions options) =>
|
Builder driftBuilderNotShared(BuilderOptions options) =>
|
||||||
DriftBuilder(DriftGenerationMode.monolithicPart, options);
|
DriftBuilder(DriftGenerationMode.monolithicPart, options);
|
||||||
|
|
||||||
|
Builder modular(BuilderOptions options) =>
|
||||||
|
DriftBuilder(DriftGenerationMode.modular, options);
|
||||||
|
|
||||||
PostProcessBuilder driftCleanup(BuilderOptions options) {
|
PostProcessBuilder driftCleanup(BuilderOptions options) {
|
||||||
return const FileDeletingBuilder(['.temp.dart']);
|
return const FileDeletingBuilder(['.temp.dart']);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:sqlparser/sqlparser.dart' hide AnalysisError;
|
||||||
import '../results/database.dart';
|
import '../results/database.dart';
|
||||||
import '../results/element.dart';
|
import '../results/element.dart';
|
||||||
import '../results/file_results.dart';
|
import '../results/file_results.dart';
|
||||||
|
import '../results/query.dart';
|
||||||
import 'error.dart';
|
import 'error.dart';
|
||||||
|
|
||||||
class FileState {
|
class FileState {
|
||||||
|
@ -26,6 +27,15 @@ class FileState {
|
||||||
return analyzedElements.any((e) => e is BaseDriftAccessor);
|
return analyzedElements.any((e) => e is BaseDriftAccessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether an accessor class making queries and imports available should be
|
||||||
|
/// written for this file if modular analysis is enabled.
|
||||||
|
bool get hasModularDriftAccessor {
|
||||||
|
final hasImports = discovery?.importDependencies.isNotEmpty == true;
|
||||||
|
final hasQuery = analyzedElements.any((e) => e is DefinedSqlQuery);
|
||||||
|
|
||||||
|
return hasImports || hasQuery;
|
||||||
|
}
|
||||||
|
|
||||||
/// All analyzed [DriftElement]s found in this library.
|
/// All analyzed [DriftElement]s found in this library.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Iterable<DriftElement> get analyzedElements {
|
Iterable<DriftElement> get analyzedElements {
|
||||||
|
|
|
@ -24,6 +24,12 @@ class DriftElementId {
|
||||||
|
|
||||||
Map<String, Object?> toJson() => _$DriftElementIdToJson(this);
|
Map<String, Object?> toJson() => _$DriftElementIdToJson(this);
|
||||||
|
|
||||||
|
Uri get modularImportUri {
|
||||||
|
final path = url.withoutExtension(libraryUri.path);
|
||||||
|
|
||||||
|
return libraryUri.replace(path: '$path.drift.dart');
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(DriftElementId, libraryUri, name);
|
int get hashCode => Object.hash(DriftElementId, libraryUri, name);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
import 'package:dart_style/dart_style.dart';
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
import 'package:drift_dev/src/writer/tables/table_writer.dart';
|
||||||
import 'package:pub_semver/pub_semver.dart';
|
import 'package:pub_semver/pub_semver.dart';
|
||||||
|
|
||||||
import '../../analysis/custom_result_class.dart';
|
import '../../analysis/custom_result_class.dart';
|
||||||
|
@ -11,6 +12,8 @@ import '../../utils/string_escaper.dart';
|
||||||
import '../../writer/database_writer.dart';
|
import '../../writer/database_writer.dart';
|
||||||
import '../../writer/drift_accessor_writer.dart';
|
import '../../writer/drift_accessor_writer.dart';
|
||||||
import '../../writer/import_manager.dart';
|
import '../../writer/import_manager.dart';
|
||||||
|
import '../../writer/modules.dart';
|
||||||
|
import '../../writer/tables/view_writer.dart';
|
||||||
import '../../writer/writer.dart';
|
import '../../writer/writer.dart';
|
||||||
import 'backend.dart';
|
import 'backend.dart';
|
||||||
|
|
||||||
|
@ -26,16 +29,29 @@ enum DriftGenerationMode {
|
||||||
///
|
///
|
||||||
/// Drift will generate a single part file for the main database file and each
|
/// Drift will generate a single part file for the main database file and each
|
||||||
/// DAO-defining file.
|
/// DAO-defining file.
|
||||||
monolithicSharedPart,
|
monolithicSharedPart(true, true),
|
||||||
|
|
||||||
/// Like [monolithicSharedPart], except that drift will generate a single
|
/// Like [monolithicSharedPart], except that drift will generate a single
|
||||||
/// part file on its own instead of generating a part file for `source_gen`
|
/// part file on its own instead of generating a part file for `source_gen`
|
||||||
/// to process later.
|
/// to process later.
|
||||||
monolithicPart;
|
monolithicPart(true, true),
|
||||||
|
|
||||||
bool get isMonolithic => true;
|
/// Generates a separate Dart library (no `part of` directive) for each input
|
||||||
|
/// (.drift file or .dart file with databases / tables).
|
||||||
|
modular(false, false);
|
||||||
|
|
||||||
bool get isPartFile => true;
|
/// Whether this mode defines a "monolithic" build.
|
||||||
|
///
|
||||||
|
/// In a monolithic build, drift will generate all code into a single file,
|
||||||
|
/// even if tables and queries are defined across multiple `.drift` files.
|
||||||
|
/// In modular (non-monolithic) builds, files are generated for each input
|
||||||
|
/// defining drift elements instead.
|
||||||
|
final bool isMonolithic;
|
||||||
|
|
||||||
|
/// Whether this build mode generates a part file.
|
||||||
|
final bool isPartFile;
|
||||||
|
|
||||||
|
const DriftGenerationMode(this.isMonolithic, this.isPartFile);
|
||||||
|
|
||||||
/// Whether the analysis happens in the generating build step.
|
/// Whether the analysis happens in the generating build step.
|
||||||
///
|
///
|
||||||
|
@ -47,8 +63,6 @@ enum DriftGenerationMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DriftBuilder extends Builder {
|
class DriftBuilder extends Builder {
|
||||||
static final Version _minimalDartLanguageVersion = Version(2, 12, 0);
|
|
||||||
|
|
||||||
final DriftOptions options;
|
final DriftOptions options;
|
||||||
final DriftGenerationMode generationMode;
|
final DriftGenerationMode generationMode;
|
||||||
|
|
||||||
|
@ -71,11 +85,93 @@ class DriftBuilder extends Builder {
|
||||||
return {
|
return {
|
||||||
'.dart': ['.drift.dart']
|
'.dart': ['.drift.dart']
|
||||||
};
|
};
|
||||||
|
case DriftGenerationMode.modular:
|
||||||
|
return {
|
||||||
|
'.dart': ['.drift.dart'],
|
||||||
|
'.drift': ['.drift.dart'],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> build(BuildStep buildStep) async {
|
Future<void> build(BuildStep buildStep) async {
|
||||||
|
final run = _DriftBuildRun(options, generationMode, buildStep);
|
||||||
|
await run.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on Version {
|
||||||
|
String get majorMinor => '$major.$minor';
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DriftBuildRun {
|
||||||
|
final DriftOptions options;
|
||||||
|
final DriftGenerationMode mode;
|
||||||
|
final BuildStep buildStep;
|
||||||
|
|
||||||
|
final DriftAnalysisDriver driver;
|
||||||
|
|
||||||
|
/// When emitting a direct part file, contains the `// @dart` language version
|
||||||
|
/// comment from the main library. We need to apply it to the part file as
|
||||||
|
/// well.
|
||||||
|
Version? overriddenLanguageVersion;
|
||||||
|
|
||||||
|
/// The Dart language version from the package. When it's too old and we're
|
||||||
|
/// generating libraries, we need to apply a `// @dart` version comment to get
|
||||||
|
/// a suitable version.
|
||||||
|
Version? packageLanguageVersion;
|
||||||
|
|
||||||
|
late Writer writer;
|
||||||
|
|
||||||
|
Set<Uri> analyzedUris = {};
|
||||||
|
|
||||||
|
_DriftBuildRun(this.options, this.mode, this.buildStep)
|
||||||
|
: driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options)
|
||||||
|
..cacheReader = BuildCacheReader(buildStep);
|
||||||
|
|
||||||
|
Future<void> run() async {
|
||||||
|
await _warnAboutDeprecatedOptions();
|
||||||
|
if (!await _checkForElementsToBuild()) return;
|
||||||
|
|
||||||
|
await _checkForLanguageVersions();
|
||||||
|
|
||||||
|
final fileResult =
|
||||||
|
await _analyze(buildStep.inputId.uri, isEntrypoint: true);
|
||||||
|
|
||||||
|
// For the monolithic build modes, we only generate code for databases and
|
||||||
|
// crawl the tables from there.
|
||||||
|
if (mode.isMonolithic && !fileResult.containsDatabaseAccessor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createWriter();
|
||||||
|
if (mode.isMonolithic) {
|
||||||
|
await _generateMonolithic(fileResult);
|
||||||
|
} else {
|
||||||
|
await _generateModular(fileResult);
|
||||||
|
}
|
||||||
|
await _emitCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FileState> _analyze(Uri uri, {bool isEntrypoint = false}) async {
|
||||||
|
final result = await driver.fullyAnalyze(uri);
|
||||||
|
|
||||||
|
// If we're doing a monolithic build, we need to warn about errors in
|
||||||
|
// imports too.
|
||||||
|
final printErrors =
|
||||||
|
isEntrypoint || (mode.isMonolithic && analyzedUris.add(result.ownUri));
|
||||||
|
if (printErrors) {
|
||||||
|
for (final error in result.fileAnalysis?.analysisErrors ?? const []) {
|
||||||
|
log.warning(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Once per build, prints a warning about deprecated build options if they
|
||||||
|
/// are applied to this builder.
|
||||||
|
Future<void> _warnAboutDeprecatedOptions() async {
|
||||||
final flags = await buildStep.fetchResource(_flags);
|
final flags = await buildStep.fetchResource(_flags);
|
||||||
if (!flags.didWarnAboutDeprecatedOptions &&
|
if (!flags.didWarnAboutDeprecatedOptions &&
|
||||||
options.enabledDeprecatedOption) {
|
options.enabledDeprecatedOption) {
|
||||||
|
@ -84,11 +180,11 @@ class DriftBuilder extends Builder {
|
||||||
'Consider removing the option from your build.yaml.');
|
'Consider removing the option from your build.yaml.');
|
||||||
flags.didWarnAboutDeprecatedOptions = true;
|
flags.didWarnAboutDeprecatedOptions = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options)
|
/// Checks if the input file contains elements drift should generate code for.
|
||||||
..cacheReader = BuildCacheReader(buildStep);
|
Future<bool> _checkForElementsToBuild() async {
|
||||||
|
if (!mode.embeddedAnalyzer) {
|
||||||
if (!generationMode.embeddedAnalyzer) {
|
|
||||||
// An analysis step should have already run for this asset. If we can't
|
// An analysis step should have already run for this asset. If we can't
|
||||||
// pick up results from that, there is no code for drift to generate.
|
// pick up results from that, there is no code for drift to generate.
|
||||||
final fromCache =
|
final fromCache =
|
||||||
|
@ -97,15 +193,17 @@ class DriftBuilder extends Builder {
|
||||||
if (fromCache == null) {
|
if (fromCache == null) {
|
||||||
// Don't do anything! There are no analysis results for this file, so
|
// Don't do anything! There are no analysis results for this file, so
|
||||||
// there's nothing for drift to generate code for.
|
// there's nothing for drift to generate code for.
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we actually have something to generate. We're generating code
|
return true;
|
||||||
// needing version 2.12 (or later) of the Dart _language_. This property is
|
}
|
||||||
// inherited from the main file, so let's check that.
|
|
||||||
Version? overriddenLanguageVersion;
|
/// Prints a warning if the used Dart version is incompatible with drift's
|
||||||
if (generationMode.isPartFile) {
|
/// minimal version constraints.
|
||||||
|
Future<void> _checkForLanguageVersions() async {
|
||||||
|
if (mode.isPartFile) {
|
||||||
final library = await buildStep.inputLibrary;
|
final library = await buildStep.inputLibrary;
|
||||||
overriddenLanguageVersion = library.languageVersion.override;
|
overriddenLanguageVersion = library.languageVersion.override;
|
||||||
|
|
||||||
|
@ -123,43 +221,41 @@ class DriftBuilder extends Builder {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Set<Uri> analyzedUris = {};
|
Future<void> _generateModular(FileState entrypointState) async {
|
||||||
Future<FileState> analyze(Uri uri) async {
|
for (final element in entrypointState.analysis.values) {
|
||||||
final fileResult = await driver.fullyAnalyze(uri);
|
final result = element.result;
|
||||||
if (analyzedUris.add(fileResult.ownUri)) {
|
|
||||||
for (final error
|
if (result is DriftTable) {
|
||||||
in fileResult.fileAnalysis?.analysisErrors ?? const []) {
|
TableWriter(result, writer.child()).writeInto();
|
||||||
log.warning(error);
|
} else if (result is DriftView) {
|
||||||
}
|
ViewWriter(result, writer.child(), null).write();
|
||||||
|
} else if (result is DriftDatabase) {
|
||||||
|
final resolved =
|
||||||
|
entrypointState.fileAnalysis!.resolvedDatabases[result.id]!;
|
||||||
|
final input = DatabaseGenerationInput(result, resolved, const {});
|
||||||
|
DatabaseWriter(input, writer.child()).write();
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final fileResult = await analyze(buildStep.inputId.uri);
|
if (entrypointState.hasModularDriftAccessor) {
|
||||||
|
ModularAccessorWriter(writer.child(), entrypointState).write();
|
||||||
// For the monolithic build modes, we only generate code for databases and
|
|
||||||
// crawl the tables from there.
|
|
||||||
if (generationMode.isMonolithic && !fileResult.containsDatabaseAccessor) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final generationOptions = GenerationOptions(
|
Future<void> _generateMonolithic(FileState entrypointState) async {
|
||||||
imports: ImportManagerForPartFiles(),
|
for (final element in entrypointState.analysis.values) {
|
||||||
);
|
|
||||||
final writer = Writer(options, generationOptions: generationOptions);
|
|
||||||
|
|
||||||
for (final element in fileResult.analysis.values) {
|
|
||||||
final result = element.result;
|
final result = element.result;
|
||||||
|
|
||||||
if (result is BaseDriftAccessor) {
|
if (result is BaseDriftAccessor) {
|
||||||
final resolved = fileResult.fileAnalysis!.resolvedDatabases[result.id]!;
|
final resolved =
|
||||||
|
entrypointState.fileAnalysis!.resolvedDatabases[result.id]!;
|
||||||
var importedQueries = <DefinedSqlQuery, SqlQuery>{};
|
var importedQueries = <DefinedSqlQuery, SqlQuery>{};
|
||||||
|
|
||||||
for (final query
|
for (final query
|
||||||
in resolved.availableElements.whereType<DefinedSqlQuery>()) {
|
in resolved.availableElements.whereType<DefinedSqlQuery>()) {
|
||||||
final resolvedFile = await analyze(query.id.libraryUri);
|
final resolvedFile = await _analyze(query.id.libraryUri);
|
||||||
final resolvedQuery =
|
final resolvedQuery =
|
||||||
resolvedFile.fileAnalysis?.resolvedQueries[query.id];
|
resolvedFile.fileAnalysis?.resolvedQueries[query.id];
|
||||||
|
|
||||||
|
@ -189,34 +285,51 @@ class DriftBuilder extends Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _createWriter() {
|
||||||
|
if (mode.isMonolithic) {
|
||||||
|
final generationOptions = GenerationOptions(
|
||||||
|
imports: ImportManagerForPartFiles(),
|
||||||
|
);
|
||||||
|
writer = Writer(options, generationOptions: generationOptions);
|
||||||
|
} else {
|
||||||
|
final imports = LibraryInputManager();
|
||||||
|
final generationOptions = GenerationOptions(
|
||||||
|
imports: imports,
|
||||||
|
isModular: true,
|
||||||
|
);
|
||||||
|
writer = Writer(options, generationOptions: generationOptions);
|
||||||
|
imports.linkToWriter(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _emitCode() {
|
||||||
final output = StringBuffer();
|
final output = StringBuffer();
|
||||||
output.writeln('// ignore_for_file: type=lint');
|
output.writeln('// ignore_for_file: type=lint');
|
||||||
|
|
||||||
if (generationMode == DriftGenerationMode.monolithicPart) {
|
if (mode == DriftGenerationMode.monolithicPart) {
|
||||||
final originalFile = buildStep.inputId.pathSegments.last;
|
final originalFile = buildStep.inputId.pathSegments.last;
|
||||||
|
|
||||||
if (overriddenLanguageVersion != null) {
|
if (overriddenLanguageVersion != null) {
|
||||||
// Part files need to have the same version as the main library.
|
// Part files need to have the same version as the main library.
|
||||||
output.writeln('// @dart=${overriddenLanguageVersion.majorMinor}');
|
output.writeln('// @dart=${overriddenLanguageVersion!.majorMinor}');
|
||||||
}
|
}
|
||||||
|
|
||||||
output.writeln('part of ${asDartLiteral(originalFile)};');
|
output.writeln('part of ${asDartLiteral(originalFile)};');
|
||||||
}
|
}
|
||||||
output.write(writer.writeGenerated());
|
output.write(writer.writeGenerated());
|
||||||
|
|
||||||
var generated = output.toString();
|
var code = output.toString();
|
||||||
try {
|
try {
|
||||||
generated = DartFormatter().format(generated);
|
code = DartFormatter().format(code);
|
||||||
} on FormatterException {
|
} on FormatterException {
|
||||||
log.warning('Could not format generated source. The generated code is '
|
log.warning('Could not format generated source. The generated code is '
|
||||||
'probably invalid, and this is most likely a bug in drift_dev.');
|
'probably invalid, and this is most likely a bug in drift_dev.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await buildStep.writeAsString(buildStep.allowedOutputs.single, generated);
|
return buildStep.writeAsString(buildStep.allowedOutputs.single, code);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension on Version {
|
static final Version _minimalDartLanguageVersion = Version(2, 12, 0);
|
||||||
String get majorMinor => '$major.$minor';
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import '../analysis/results/file_results.dart';
|
||||||
import '../analysis/results/results.dart';
|
import '../analysis/results/results.dart';
|
||||||
import '../services/find_stream_update_rules.dart';
|
import '../services/find_stream_update_rules.dart';
|
||||||
import '../utils/string_escaper.dart';
|
import '../utils/string_escaper.dart';
|
||||||
|
import 'modules.dart';
|
||||||
import 'queries/query_writer.dart';
|
import 'queries/query_writer.dart';
|
||||||
import 'tables/table_writer.dart';
|
import 'tables/table_writer.dart';
|
||||||
import 'tables/view_writer.dart';
|
import 'tables/view_writer.dart';
|
||||||
|
@ -28,18 +29,22 @@ class DatabaseWriter {
|
||||||
return 'DatabaseAtV${scope.generationOptions.forSchema}';
|
return 'DatabaseAtV${scope.generationOptions.forSchema}';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '_\$${db.id.name}';
|
final prefix = scope.generationOptions.isModular ? '' : r'_';
|
||||||
|
|
||||||
|
return '$prefix\$${db.id.name}';
|
||||||
}
|
}
|
||||||
|
|
||||||
void write() {
|
void write() {
|
||||||
final elements = input.resolvedAccessor.availableElements;
|
final elements = input.resolvedAccessor.availableElements;
|
||||||
|
|
||||||
// Write data classes, companions and info classes
|
// Write data classes, companions and info classes
|
||||||
for (final reference in elements) {
|
if (!scope.generationOptions.isModular) {
|
||||||
if (reference is DriftTable) {
|
for (final reference in elements) {
|
||||||
TableWriter(reference, scope.child()).writeInto();
|
if (reference is DriftTable) {
|
||||||
} else if (reference is DriftView) {
|
TableWriter(reference, scope.child()).writeInto();
|
||||||
ViewWriter(reference, scope.child(), this).write();
|
} else if (reference is DriftView) {
|
||||||
|
ViewWriter(reference, scope.child(), this).write();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +79,7 @@ class DatabaseWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity is DriftTable) {
|
if (entity is DriftTable) {
|
||||||
final tableClassName = entity.entityInfoName;
|
final tableClassName = dbScope.dartCode(dbScope.entityInfoType(entity));
|
||||||
|
|
||||||
writeMemoizedGetter(
|
writeMemoizedGetter(
|
||||||
buffer: dbScope.leaf().buffer,
|
buffer: dbScope.leaf().buffer,
|
||||||
|
@ -126,6 +131,27 @@ class DatabaseWriter {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also write implicit DAOs for modular imports
|
||||||
|
if (scope.generationOptions.isModular) {
|
||||||
|
for (final import in input.resolvedAccessor.knownImports) {
|
||||||
|
if (import.hasModularDriftAccessor) {
|
||||||
|
final type = dbScope.modularAccessor(import.ownUri);
|
||||||
|
final getter = ReCase(type.toString()).camelCase;
|
||||||
|
|
||||||
|
dbScope.leaf()
|
||||||
|
..writeDart(type)
|
||||||
|
..write(' get $getter => ')
|
||||||
|
..writeUriRef(
|
||||||
|
ModularAccessorWriter.modularSupport, 'ReadDatabaseContainer')
|
||||||
|
..writeln('(this).accessor<')
|
||||||
|
..writeDart(type)
|
||||||
|
..write('>(${asDartLiteral(import.ownUri.toString())}, ')
|
||||||
|
..writeDart(type)
|
||||||
|
..writeln('.new);');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write implementation for query methods
|
// Write implementation for query methods
|
||||||
for (final query in input.availableRegularQueries) {
|
for (final query in input.availableRegularQueries) {
|
||||||
QueryWriter(dbScope.child()).write(query);
|
QueryWriter(dbScope.child()).write(query);
|
||||||
|
@ -134,11 +160,14 @@ class DatabaseWriter {
|
||||||
// Write List of tables
|
// Write List of tables
|
||||||
final schemaScope = dbScope.leaf();
|
final schemaScope = dbScope.leaf();
|
||||||
|
|
||||||
|
final tableInfoType =
|
||||||
|
'${dbScope.drift('TableInfo')}<${dbScope.drift('Table')}, Object?>';
|
||||||
|
final schemaEntity = dbScope.drift('DatabaseSchemaEntity');
|
||||||
|
|
||||||
schemaScope
|
schemaScope
|
||||||
..write(
|
..write('@override\nIterable<$tableInfoType> get allTables => ')
|
||||||
'@override\nIterable<TableInfo<Table, dynamic>> get allTables => ')
|
..write('allSchemaEntities.whereType<$tableInfoType>();\n')
|
||||||
..write('allSchemaEntities.whereType<TableInfo<Table, Object?>>();\n')
|
..write('@override\nList<$schemaEntity> get allSchemaEntities ')
|
||||||
..write('@override\nList<DatabaseSchemaEntity> get allSchemaEntities ')
|
|
||||||
..write('=> [');
|
..write('=> [');
|
||||||
|
|
||||||
schemaScope
|
schemaScope
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import '../utils/string_escaper.dart';
|
||||||
|
import 'writer.dart';
|
||||||
|
|
||||||
abstract class ImportManager {
|
abstract class ImportManager {
|
||||||
String? prefixFor(Uri definitionUri, String elementName);
|
String? prefixFor(Uri definitionUri, String elementName);
|
||||||
}
|
}
|
||||||
|
@ -8,3 +11,31 @@ class ImportManagerForPartFiles extends ImportManager {
|
||||||
return null; // todo: Find import alias from existing imports?
|
return null; // todo: Find import alias from existing imports?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LibraryInputManager extends ImportManager {
|
||||||
|
static final _dartCore = Uri.parse('dart:core');
|
||||||
|
|
||||||
|
final Map<Uri, String> _importAliases = {};
|
||||||
|
TextEmitter? emitter;
|
||||||
|
|
||||||
|
LibraryInputManager();
|
||||||
|
|
||||||
|
void linkToWriter(Writer writer) {
|
||||||
|
emitter = writer.leaf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? prefixFor(Uri definitionUri, String elementName) {
|
||||||
|
if (definitionUri == _dartCore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _importAliases.putIfAbsent(definitionUri, () {
|
||||||
|
final alias = 'i${_importAliases.length}';
|
||||||
|
|
||||||
|
emitter?.writeln(
|
||||||
|
'import ${asDartLiteral(definitionUri.toString())} as $alias;');
|
||||||
|
return alias;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import '../analysis/driver/state.dart';
|
||||||
|
import '../analysis/results/results.dart';
|
||||||
|
import '../utils/string_escaper.dart';
|
||||||
|
import 'queries/query_writer.dart';
|
||||||
|
import 'writer.dart';
|
||||||
|
|
||||||
|
/// Write a modular database accessor for a drift file.
|
||||||
|
///
|
||||||
|
/// In drift's (opt-in) modular build set, a `.drift.dart` file is generated for
|
||||||
|
/// each `.drift` file. This file defines elements defined in that drift file
|
||||||
|
/// (like tables, data classes, companions, fields for indexes and triggers).
|
||||||
|
///
|
||||||
|
/// If queries are defined in that drift file, a "modular accessor" is generated
|
||||||
|
/// as well. This accessor contains generated methods for all queries. The main
|
||||||
|
/// database will make these accessor available as getters.
|
||||||
|
class ModularAccessorWriter {
|
||||||
|
final Scope scope;
|
||||||
|
final FileState file;
|
||||||
|
|
||||||
|
ModularAccessorWriter(this.scope, this.file);
|
||||||
|
|
||||||
|
void write() {
|
||||||
|
if (!file.hasModularDriftAccessor) return;
|
||||||
|
|
||||||
|
final className = scope.modularAccessor(file.ownUri);
|
||||||
|
final generatedDatabase = scope.drift('GeneratedDatabase');
|
||||||
|
|
||||||
|
scope.leaf()
|
||||||
|
..write('class $className extends ')
|
||||||
|
..write(_modular('ModularAccessor'))
|
||||||
|
..writeln('{ $className($generatedDatabase db): super(db);');
|
||||||
|
|
||||||
|
final referencedElements = <DriftElement>{};
|
||||||
|
|
||||||
|
final queries = file.fileAnalysis?.resolvedQueries ?? const {};
|
||||||
|
for (final query in queries.entries) {
|
||||||
|
final queryElement = file.analysis[query.key]?.result;
|
||||||
|
if (queryElement != null) {
|
||||||
|
referencedElements.addAll(queryElement.references);
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryWriter(scope.child()).write(query.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final restOfClass = scope.leaf();
|
||||||
|
|
||||||
|
for (final reference in referencedElements) {
|
||||||
|
// This element is referenced in a query, and the query writer expects it
|
||||||
|
// to be available as a getter. So, let's generate that getter:
|
||||||
|
|
||||||
|
if (reference is DriftElementWithResultSet) {
|
||||||
|
final infoType = restOfClass.entityInfoType(reference);
|
||||||
|
|
||||||
|
restOfClass
|
||||||
|
..writeDart(infoType)
|
||||||
|
..write(' get ${reference.dbGetterName} => this.resultSet<')
|
||||||
|
..writeDart(infoType)
|
||||||
|
..write('>(${asDartLiteral(reference.schemaName)});');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restOfClass.writeln('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
String _modular(String element) {
|
||||||
|
return scope.refUri(modularSupport, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Uri modularSupport =
|
||||||
|
Uri.parse('package:drift/internal/modular.dart');
|
||||||
|
}
|
|
@ -710,10 +710,10 @@ String? _defaultForDartPlaceholder(
|
||||||
// the surrounding precedence in SQL.
|
// the surrounding precedence in SQL.
|
||||||
final sql = SqlWriter(scope.options)
|
final sql = SqlWriter(scope.options)
|
||||||
.writeNodeIntoStringLiteral(Parentheses(kind.defaultValue!));
|
.writeNodeIntoStringLiteral(Parentheses(kind.defaultValue!));
|
||||||
return 'const CustomExpression($sql)';
|
return 'const ${scope.drift('CustomExpression')}($sql)';
|
||||||
} else if (kind is SimpleDartPlaceholderType &&
|
} else if (kind is SimpleDartPlaceholderType &&
|
||||||
kind.kind == SimpleDartPlaceholderKind.orderBy) {
|
kind.kind == SimpleDartPlaceholderKind.orderBy) {
|
||||||
return 'const OrderBy.nothing()';
|
return 'const ${scope.drift('OrderBy')}.nothing()';
|
||||||
} else {
|
} else {
|
||||||
assert(!placeholder.hasDefaultOrImplicitFallback);
|
assert(!placeholder.hasDefaultOrImplicitFallback);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -17,7 +17,7 @@ class DataClassWriter {
|
||||||
|
|
||||||
DataClassWriter(this.table, this.scope) : _emitter = scope.leaf();
|
DataClassWriter(this.table, this.scope) : _emitter = scope.leaf();
|
||||||
|
|
||||||
String get serializerType => 'ValueSerializer?';
|
String get serializerType => _emitter.drift('ValueSerializer?');
|
||||||
|
|
||||||
String _columnType(DriftColumn column) {
|
String _columnType(DriftColumn column) {
|
||||||
return _emitter.dartCode(_emitter.dartType(column));
|
return _emitter.dartCode(_emitter.dartType(column));
|
||||||
|
@ -44,14 +44,15 @@ class DataClassWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
void write() {
|
void write() {
|
||||||
final parentClass = table.customParentClass ?? 'DataClass';
|
final parentClass = table.customParentClass ?? _emitter.drift('DataClass');
|
||||||
_buffer.write('class ${table.nameOfRowClass} extends $parentClass ');
|
_buffer.write('class ${table.nameOfRowClass} extends $parentClass ');
|
||||||
|
|
||||||
if (isInsertable) {
|
if (isInsertable) {
|
||||||
// The data class is only an insertable if we can actually insert rows
|
// The data class is only an insertable if we can actually insert rows
|
||||||
// into the target entity.
|
// into the target entity.
|
||||||
final type = _emitter.dartCode(_emitter.writer.rowType(table));
|
final type = _emitter.dartCode(_emitter.writer.rowType(table));
|
||||||
_buffer.writeln('implements Insertable<$type> {');
|
|
||||||
|
_buffer.writeln('implements ${_emitter.drift('Insertable')}<$type> {');
|
||||||
} else {
|
} else {
|
||||||
_buffer.writeln('{');
|
_buffer.writeln('{');
|
||||||
}
|
}
|
||||||
|
@ -121,7 +122,8 @@ class DataClassWriter {
|
||||||
..write('factory $dataClassName.fromJson('
|
..write('factory $dataClassName.fromJson('
|
||||||
'Map<String, dynamic> json, {$serializerType serializer}'
|
'Map<String, dynamic> json, {$serializerType serializer}'
|
||||||
') {\n')
|
') {\n')
|
||||||
..write('serializer ??= driftRuntimeOptions.defaultSerializer;\n')
|
..write('serializer ??= ${_emitter.drift('driftRuntimeOptions')}'
|
||||||
|
'.defaultSerializer;\n')
|
||||||
..write('return $dataClassName(');
|
..write('return $dataClassName(');
|
||||||
|
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
|
@ -161,7 +163,8 @@ class DataClassWriter {
|
||||||
void _writeToJson() {
|
void _writeToJson() {
|
||||||
_buffer.write('@override Map<String, dynamic> toJson('
|
_buffer.write('@override Map<String, dynamic> toJson('
|
||||||
'{$serializerType serializer}) {\n'
|
'{$serializerType serializer}) {\n'
|
||||||
'serializer ??= driftRuntimeOptions.defaultSerializer;\n'
|
'serializer ??= ${_emitter.drift('driftRuntimeOptions')}'
|
||||||
|
'.defaultSerializer;\n'
|
||||||
'return <String, dynamic>{\n');
|
'return <String, dynamic>{\n');
|
||||||
|
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
|
@ -187,6 +190,7 @@ class DataClassWriter {
|
||||||
void _writeCopyWith() {
|
void _writeCopyWith() {
|
||||||
final dataClassName = _emitter.dartCode(_emitter.writer.rowClass(table));
|
final dataClassName = _emitter.dartCode(_emitter.writer.rowClass(table));
|
||||||
final wrapNullableInValue = scope.options.generateValuesInCopyWith;
|
final wrapNullableInValue = scope.options.generateValuesInCopyWith;
|
||||||
|
final valueType = _emitter.drift('Value');
|
||||||
|
|
||||||
_buffer.write('$dataClassName copyWith({');
|
_buffer.write('$dataClassName copyWith({');
|
||||||
for (var i = 0; i < columns.length; i++) {
|
for (var i = 0; i < columns.length; i++) {
|
||||||
|
@ -197,8 +201,8 @@ class DataClassWriter {
|
||||||
final typeName = _columnType(column);
|
final typeName = _columnType(column);
|
||||||
if (wrapNullableInValue && isNullable) {
|
if (wrapNullableInValue && isNullable) {
|
||||||
_buffer
|
_buffer
|
||||||
..write('Value<$typeName> ${column.nameInDart} ')
|
..write('$valueType<$typeName> ${column.nameInDart} ')
|
||||||
..write('= const Value.absent()');
|
..write('= const $valueType.absent()');
|
||||||
} else if (!isNullable) {
|
} else if (!isNullable) {
|
||||||
// We always use nullable parameters in copyWith, since all parameters
|
// We always use nullable parameters in copyWith, since all parameters
|
||||||
// are optional. The !isNullable check is there to avoid a duplicate
|
// are optional. The !isNullable check is there to avoid a duplicate
|
||||||
|
@ -233,10 +237,13 @@ class DataClassWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _writeToColumnsOverride() {
|
void _writeToColumnsOverride() {
|
||||||
|
final expression = _emitter.drift('Expression');
|
||||||
|
final variable = _emitter.drift('Variable');
|
||||||
|
|
||||||
_buffer
|
_buffer
|
||||||
..write('@override\nMap<String, Expression> toColumns'
|
..write('@override\nMap<String, $expression> toColumns'
|
||||||
'(bool nullToAbsent) {\n')
|
'(bool nullToAbsent) {\n')
|
||||||
..write('final map = <String, Expression> {};');
|
..write('final map = <String, $expression> {};');
|
||||||
|
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
// Generated column - cannot be used for inserts or updates
|
// Generated column - cannot be used for inserts or updates
|
||||||
|
@ -255,7 +262,7 @@ class DataClassWriter {
|
||||||
|
|
||||||
final typeName = column.variableTypeCode(nullable: false);
|
final typeName = column.variableTypeCode(nullable: false);
|
||||||
final mapSetter = 'map[${asDartLiteral(column.nameInSql)}] = '
|
final mapSetter = 'map[${asDartLiteral(column.nameInSql)}] = '
|
||||||
'Variable<$typeName>';
|
'$variable<$typeName>';
|
||||||
|
|
||||||
if (column.typeConverter != null) {
|
if (column.typeConverter != null) {
|
||||||
// apply type converter before writing the variable
|
// apply type converter before writing the variable
|
||||||
|
@ -309,12 +316,14 @@ class DataClassWriter {
|
||||||
if (needsNullCheck) {
|
if (needsNullCheck) {
|
||||||
_buffer
|
_buffer
|
||||||
..write(dartName)
|
..write(dartName)
|
||||||
..write(' == null && nullToAbsent ? const Value.absent() : ');
|
..write(' == null && nullToAbsent ? '
|
||||||
|
'const ${_emitter.drift('Value')}.absent() : ');
|
||||||
// We'll write the non-null case afterwards
|
// We'll write the non-null case afterwards
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer
|
_buffer
|
||||||
..write('Value (')
|
..write(_emitter.drift('Value'))
|
||||||
|
..write('(')
|
||||||
..write(dartName)
|
..write(dartName)
|
||||||
..write('),');
|
..write('),');
|
||||||
}
|
}
|
||||||
|
@ -369,7 +378,7 @@ class RowMappingWriter {
|
||||||
final columnName = column.nameInSql;
|
final columnName = column.nameInSql;
|
||||||
final rawData = "data['\${effectivePrefix}$columnName']";
|
final rawData = "data['\${effectivePrefix}$columnName']";
|
||||||
|
|
||||||
final sqlType = column.sqlType.toString();
|
final sqlType = writer.drift(column.sqlType.toString());
|
||||||
var loadType = '$databaseGetter.typeMapping.read($sqlType, $rawData)';
|
var loadType = '$databaseGetter.typeMapping.read($sqlType, $rawData)';
|
||||||
|
|
||||||
if (!column.nullable) {
|
if (!column.nullable) {
|
||||||
|
|
|
@ -23,7 +23,8 @@ abstract class TableOrViewWriter {
|
||||||
|
|
||||||
for (final constraint in column.constraints) {
|
for (final constraint in column.constraints) {
|
||||||
if (constraint is LimitingTextLength) {
|
if (constraint is LimitingTextLength) {
|
||||||
final buffer = StringBuffer('GeneratedColumn.checkTextLength(');
|
final buffer =
|
||||||
|
StringBuffer(emitter.drift('GeneratedColumn.checkTextLength('));
|
||||||
|
|
||||||
if (constraint.minLength != null) {
|
if (constraint.minLength != null) {
|
||||||
buffer.write('minTextLength: ${constraint.minLength},');
|
buffer.write('minTextLength: ${constraint.minLength},');
|
||||||
|
@ -45,7 +46,7 @@ abstract class TableOrViewWriter {
|
||||||
final dartCode = emitter.dartCode(constraint.dartExpression);
|
final dartCode = emitter.dartCode(constraint.dartExpression);
|
||||||
|
|
||||||
additionalParams['generatedAs'] =
|
additionalParams['generatedAs'] =
|
||||||
'GeneratedAs($dartCode, ${constraint.stored})';
|
'${emitter.drift('GeneratedAs')}($dartCode, ${constraint.stored})';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (constraint is PrimaryKeyColumn && constraint.isAutoIncrement) {
|
if (constraint is PrimaryKeyColumn && constraint.isAutoIncrement) {
|
||||||
|
@ -53,7 +54,7 @@ abstract class TableOrViewWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
additionalParams['type'] = column.sqlType.toString();
|
additionalParams['type'] = emitter.drift(column.sqlType.toString());
|
||||||
|
|
||||||
if (tableOrView is DriftTable) {
|
if (tableOrView is DriftTable) {
|
||||||
additionalParams['requiredDuringInsert'] = (tableOrView as DriftTable)
|
additionalParams['requiredDuringInsert'] = (tableOrView as DriftTable)
|
||||||
|
@ -102,7 +103,7 @@ abstract class TableOrViewWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
final innerType = column.innerColumnType();
|
final innerType = column.innerColumnType();
|
||||||
var type = 'GeneratedColumn<$innerType>';
|
var type = '${emitter.drift('GeneratedColumn')}<$innerType>';
|
||||||
expressionBuffer
|
expressionBuffer
|
||||||
..write(type)
|
..write(type)
|
||||||
..write(
|
..write(
|
||||||
|
@ -133,7 +134,8 @@ abstract class TableOrViewWriter {
|
||||||
final converterCode = emitter.dartCode(emitter.writer
|
final converterCode = emitter.dartCode(emitter.writer
|
||||||
.readConverter(converter, forNullable: column.nullable));
|
.readConverter(converter, forNullable: column.nullable));
|
||||||
|
|
||||||
type = 'GeneratedColumnWithTypeConverter<$mappedType, $innerType>';
|
type = '${emitter.drift('GeneratedColumnWithTypeConverter')}'
|
||||||
|
'<$mappedType, $innerType>';
|
||||||
expressionBuffer
|
expressionBuffer
|
||||||
..write('.withConverter<')
|
..write('.withConverter<')
|
||||||
..write(mappedType)
|
..write(mappedType)
|
||||||
|
@ -155,7 +157,7 @@ abstract class TableOrViewWriter {
|
||||||
if (!scope.generationOptions.writeDataClasses) {
|
if (!scope.generationOptions.writeDataClasses) {
|
||||||
buffer.writeln('''
|
buffer.writeln('''
|
||||||
@override
|
@override
|
||||||
Never map(Map<String, dynamic> data, {$String? tablePrefix}) {
|
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
throw UnsupportedError('TableInfo.map in schema verification code');
|
||||||
}
|
}
|
||||||
''');
|
''');
|
||||||
|
@ -229,7 +231,8 @@ abstract class TableOrViewWriter {
|
||||||
void writeGetColumnsOverride() {
|
void writeGetColumnsOverride() {
|
||||||
final columnsWithGetters =
|
final columnsWithGetters =
|
||||||
tableOrView.columns.map((c) => c.nameInDart).join(', ');
|
tableOrView.columns.map((c) => c.nameInDart).join(', ');
|
||||||
buffer.write('@override\nList<GeneratedColumn> get \$columns => '
|
buffer.write(
|
||||||
|
'@override\nList<${emitter.drift('GeneratedColumn')}> get \$columns => '
|
||||||
'[$columnsWithGetters];\n');
|
'[$columnsWithGetters];\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,30 +279,37 @@ class TableWriter extends TableOrViewWriter {
|
||||||
|
|
||||||
if (!scope.generationOptions.writeDataClasses) {
|
if (!scope.generationOptions.writeDataClasses) {
|
||||||
// Write a small table header without data class
|
// Write a small table header without data class
|
||||||
|
buffer
|
||||||
|
..write('class ${table.entityInfoName} extends ')
|
||||||
|
..write(emitter.drift('Table'))
|
||||||
|
..write(' with ')
|
||||||
|
..write(emitter.drift('TableInfo'));
|
||||||
buffer.write('class ${table.entityInfoName} extends Table with '
|
buffer.write('class ${table.entityInfoName} extends Table with '
|
||||||
'TableInfo');
|
'TableInfo');
|
||||||
if (table.isVirtual) {
|
if (table.isVirtual) {
|
||||||
buffer.write(', VirtualTableInfo');
|
buffer.write(', ${emitter.drift('VirtualTableInfo')}');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Regular generation, write full table class
|
// Regular generation, write full table class
|
||||||
final dataClass = emitter.dartCode(emitter.writer.rowClass(table));
|
final dataClass = emitter.dartCode(emitter.writer.rowClass(table));
|
||||||
final tableDslName = table.definingDartClass ?? 'Table';
|
final tableDslName = table.definingDartClass ?? emitter.drift('Table');
|
||||||
|
|
||||||
// class UsersTable extends Users implements TableInfo<Users, User> {
|
// class UsersTable extends Users implements TableInfo<Users, User> {
|
||||||
final typeArgs = '<${table.entityInfoName}, $dataClass>';
|
final typeArgs = '<${table.entityInfoName}, $dataClass>';
|
||||||
|
|
||||||
buffer.write('class ${table.entityInfoName} extends $tableDslName with '
|
buffer.write('class ${table.entityInfoName} extends $tableDslName with '
|
||||||
'TableInfo$typeArgs ');
|
'${emitter.drift('TableInfo')}$typeArgs ');
|
||||||
|
|
||||||
if (table.isVirtual) {
|
if (table.isVirtual) {
|
||||||
buffer.write(', VirtualTableInfo$typeArgs ');
|
buffer.write(', ${emitter.drift('VirtualTableInfo')}$typeArgs ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
..writeln('{')
|
..writeln('{')
|
||||||
// write a GeneratedDatabase reference that is set in the constructor
|
// write a GeneratedDatabase reference that is set in the constructor
|
||||||
..writeln('@override final GeneratedDatabase attachedDatabase;')
|
..writeln(
|
||||||
|
'@override final ${emitter.drift('GeneratedDatabase')} attachedDatabase;')
|
||||||
..writeln('final String? _alias;')
|
..writeln('final String? _alias;')
|
||||||
..writeln(
|
..writeln(
|
||||||
'${table.entityInfoName}(this.attachedDatabase, [this._alias]);');
|
'${table.entityInfoName}(this.attachedDatabase, [this._alias]);');
|
||||||
|
@ -366,9 +376,11 @@ class TableWriter extends TableOrViewWriter {
|
||||||
|
|
||||||
void _writeColumnVerificationMeta(DriftColumn column) {
|
void _writeColumnVerificationMeta(DriftColumn column) {
|
||||||
if (!_skipVerification) {
|
if (!_skipVerification) {
|
||||||
|
final meta = emitter.drift('VerificationMeta');
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
..write('final VerificationMeta ${_fieldNameForColumnMeta(column)} = ')
|
..write('static const $meta ${_fieldNameForColumnMeta(column)} = ')
|
||||||
..write("const VerificationMeta('${column.nameInDart}');\n");
|
..writeln("const $meta('${column.nameInDart}');");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,11 +388,13 @@ class TableWriter extends TableOrViewWriter {
|
||||||
if (_skipVerification) return;
|
if (_skipVerification) return;
|
||||||
|
|
||||||
final innerType = emitter.dartCode(emitter.writer.rowType(table));
|
final innerType = emitter.dartCode(emitter.writer.rowType(table));
|
||||||
buffer
|
emitter
|
||||||
..write('@override\nVerificationContext validateIntegrity'
|
..writeln('@override')
|
||||||
'(Insertable<$innerType> instance, '
|
..writeDriftRef('VerificationContext')
|
||||||
'{bool isInserting = false}) {\n')
|
..write(' validateIntegrity(')
|
||||||
..write('final context = VerificationContext();\n')
|
..writeDriftRef('Insertable')
|
||||||
|
..writeln('<$innerType> instance, {bool isInserting = false}) {')
|
||||||
|
..write('final context = ${emitter.drift('VerificationContext')}();\n')
|
||||||
..write('final data = instance.toColumns(true);\n');
|
..write('final data = instance.toColumns(true);\n');
|
||||||
|
|
||||||
const locals = {'instance', 'isInserting', 'context', 'data'};
|
const locals = {'instance', 'isInserting', 'context', 'data'};
|
||||||
|
@ -392,8 +406,8 @@ class TableWriter extends TableOrViewWriter {
|
||||||
if (column.typeConverter != null) {
|
if (column.typeConverter != null) {
|
||||||
// dont't verify custom columns, we assume that the user knows what
|
// dont't verify custom columns, we assume that the user knows what
|
||||||
// they're doing
|
// they're doing
|
||||||
buffer.write(
|
buffer.write('context.handle($metaName, '
|
||||||
'context.handle($metaName, const VerificationResult.success());');
|
'const ${emitter.drift('VerificationResult')}.success());');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,11 +435,12 @@ class TableWriter extends TableOrViewWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _writePrimaryKeyOverride() {
|
void _writePrimaryKeyOverride() {
|
||||||
buffer.write('@override\nSet<GeneratedColumn> get \$primaryKey => ');
|
buffer.write(
|
||||||
|
'@override\nSet<${emitter.drift('GeneratedColumn')}> get \$primaryKey => ');
|
||||||
final primaryKey = table.fullPrimaryKey;
|
final primaryKey = table.fullPrimaryKey;
|
||||||
|
|
||||||
if (primaryKey.isEmpty) {
|
if (primaryKey.isEmpty) {
|
||||||
buffer.write('const <GeneratedColumn>{};');
|
buffer.write('const {};');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +467,8 @@ class TableWriter extends TableOrViewWriter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.write('@override\nList<Set<GeneratedColumn>> get uniqueKeys => [');
|
buffer.write('@override\nList<Set<${emitter.drift('GeneratedColumn')}>> '
|
||||||
|
'get uniqueKeys => [');
|
||||||
|
|
||||||
for (final uniqueKey in uniqueKeys) {
|
for (final uniqueKey in uniqueKeys) {
|
||||||
buffer.write('{');
|
buffer.write('{');
|
||||||
|
|
|
@ -13,6 +13,8 @@ class UpdateCompanionWriter {
|
||||||
|
|
||||||
StringBuffer get _buffer => _emitter.buffer;
|
StringBuffer get _buffer => _emitter.buffer;
|
||||||
|
|
||||||
|
String get _value => _emitter.drift('Value');
|
||||||
|
|
||||||
late final List<DriftColumn> columns = [
|
late final List<DriftColumn> columns = [
|
||||||
for (final column in table.columns)
|
for (final column in table.columns)
|
||||||
if (!column.isGenerated) column,
|
if (!column.isGenerated) column,
|
||||||
|
@ -20,15 +22,15 @@ class UpdateCompanionWriter {
|
||||||
|
|
||||||
UpdateCompanionWriter(this.table, this.scope) : _emitter = scope.leaf();
|
UpdateCompanionWriter(this.table, this.scope) : _emitter = scope.leaf();
|
||||||
|
|
||||||
String get _companionClass =>
|
String get _companionClass => _emitter.companionType(table).toString();
|
||||||
_emitter.dartCode(_emitter.companionType(table));
|
String get _companionType => _emitter.dartCode(_emitter.companionType(table));
|
||||||
|
|
||||||
void write() {
|
void write() {
|
||||||
final rowClass = _emitter.dartCode(_emitter.rowType(table));
|
final rowClass = _emitter.dartCode(_emitter.rowType(table));
|
||||||
|
|
||||||
_buffer.write('class $_companionClass '
|
_buffer.write('class $_companionClass '
|
||||||
'extends '
|
'extends '
|
||||||
'UpdateCompanion<$rowClass> {\n');
|
'${_emitter.drift('UpdateCompanion')}<$rowClass> {\n');
|
||||||
_writeFields();
|
_writeFields();
|
||||||
|
|
||||||
_writeConstructor();
|
_writeConstructor();
|
||||||
|
@ -50,7 +52,7 @@ class UpdateCompanionWriter {
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
final modifier = scope.options.fieldModifier;
|
final modifier = scope.options.fieldModifier;
|
||||||
final type = _emitter.dartCode(scope.writer.dartType(column));
|
final type = _emitter.dartCode(scope.writer.dartType(column));
|
||||||
_buffer.write('$modifier Value<$type> ${column.nameInDart};\n');
|
_buffer.write('$modifier $_value<$type> ${column.nameInDart};\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ class UpdateCompanionWriter {
|
||||||
_buffer.write('$_companionClass({');
|
_buffer.write('$_companionClass({');
|
||||||
|
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
_buffer.write('this.${column.nameInDart} = const Value.absent(),');
|
_buffer.write('this.${column.nameInDart} = const $_value.absent(),');
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer.write('});\n');
|
_buffer.write('});\n');
|
||||||
|
@ -93,7 +95,7 @@ class UpdateCompanionWriter {
|
||||||
|
|
||||||
_buffer.write('required $typeName $param,');
|
_buffer.write('required $typeName $param,');
|
||||||
} else {
|
} else {
|
||||||
_buffer.write('this.$param = const Value.absent(),');
|
_buffer.write('this.$param = const $_value.absent(),');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_buffer.write('})');
|
_buffer.write('})');
|
||||||
|
@ -108,7 +110,7 @@ class UpdateCompanionWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
final param = required.nameInDart;
|
final param = required.nameInDart;
|
||||||
_buffer.write('$param = Value($param)');
|
_buffer.write('$param = $_value($param)');
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer.write(';\n');
|
_buffer.write(';\n');
|
||||||
|
@ -124,17 +126,19 @@ class UpdateCompanionWriter {
|
||||||
|
|
||||||
final rowType = _emitter.dartCode(_emitter.rowType(table));
|
final rowType = _emitter.dartCode(_emitter.rowType(table));
|
||||||
_buffer
|
_buffer
|
||||||
..write('static Insertable<$rowType> $constructorName')
|
..write(
|
||||||
|
'static ${_emitter.drift('Insertable')}<$rowType> $constructorName')
|
||||||
..write('({');
|
..write('({');
|
||||||
|
|
||||||
|
final expression = _emitter.drift('Expression');
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
final typeName = column.innerColumnType();
|
final typeName = column.innerColumnType();
|
||||||
_buffer.write('Expression<$typeName>? ${column.nameInDart}, \n');
|
_buffer.write('$expression<$typeName>? ${column.nameInDart}, \n');
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer
|
_buffer
|
||||||
..write('}) {\n')
|
..write('}) {\n')
|
||||||
..write('return RawValuesInsertable({');
|
..write('return ${_emitter.drift('RawValuesInsertable')}({');
|
||||||
|
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
_buffer
|
_buffer
|
||||||
|
@ -148,7 +152,7 @@ class UpdateCompanionWriter {
|
||||||
|
|
||||||
void _writeCopyWith() {
|
void _writeCopyWith() {
|
||||||
_buffer
|
_buffer
|
||||||
..write(_companionClass)
|
..write(_companionType)
|
||||||
..write(' copyWith({');
|
..write(' copyWith({');
|
||||||
var first = true;
|
var first = true;
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
|
@ -158,12 +162,12 @@ class UpdateCompanionWriter {
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
final typeName = _emitter.dartCode(_emitter.dartType(column));
|
final typeName = _emitter.dartCode(_emitter.dartType(column));
|
||||||
_buffer.write('Value<$typeName>? ${column.nameInDart}');
|
_buffer.write('$_value<$typeName>? ${column.nameInDart}');
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer
|
_buffer
|
||||||
..writeln('}) {')
|
..writeln('}) {')
|
||||||
..write('return $_companionClass(');
|
..write('return $_companionType(');
|
||||||
for (final column in columns) {
|
for (final column in columns) {
|
||||||
final name = column.nameInDart;
|
final name = column.nameInDart;
|
||||||
_buffer.write('$name: $name ?? this.$name,');
|
_buffer.write('$name: $name ?? this.$name,');
|
||||||
|
@ -172,11 +176,11 @@ class UpdateCompanionWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _writeToColumnsOverride() {
|
void _writeToColumnsOverride() {
|
||||||
// Map<String, Variable> entityToSql(covariant UpdateCompanion<D> instance)
|
final expression = _emitter.drift('Expression');
|
||||||
_buffer
|
_buffer
|
||||||
..write('@override\nMap<String, Expression> toColumns'
|
..write('@override\nMap<String, $expression> toColumns'
|
||||||
'(bool nullToAbsent) {\n')
|
'(bool nullToAbsent) {\n')
|
||||||
..write('final map = <String, Expression> {};');
|
..write('final map = <String, $expression> {};');
|
||||||
|
|
||||||
const locals = {'map', 'nullToAbsent', 'converter'};
|
const locals = {'map', 'nullToAbsent', 'converter'};
|
||||||
|
|
||||||
|
@ -186,7 +190,7 @@ class UpdateCompanionWriter {
|
||||||
_buffer.write('if ($getterName.present) {');
|
_buffer.write('if ($getterName.present) {');
|
||||||
final typeName = column.variableTypeCode(nullable: false);
|
final typeName = column.variableTypeCode(nullable: false);
|
||||||
final mapSetter = 'map[${asDartLiteral(column.nameInSql)}] = '
|
final mapSetter = 'map[${asDartLiteral(column.nameInSql)}] = '
|
||||||
'Variable<$typeName>';
|
'${_emitter.drift('Variable')}<$typeName>';
|
||||||
|
|
||||||
final converter = column.typeConverter;
|
final converter = column.typeConverter;
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
|
@ -237,7 +241,7 @@ class UpdateCompanionWriter {
|
||||||
'$insertableClass(this._object);\n\n'
|
'$insertableClass(this._object);\n\n'
|
||||||
'@override\n'
|
'@override\n'
|
||||||
'Map<String, Expression> toColumns(bool nullToAbsent) {\n'
|
'Map<String, Expression> toColumns(bool nullToAbsent) {\n'
|
||||||
'return $_companionClass(\n');
|
'return $_companionType(\n');
|
||||||
|
|
||||||
final columns = info.positionalColumns.followedBy(info.namedColumns.values);
|
final columns = info.positionalColumns.followedBy(info.namedColumns.values);
|
||||||
for (final columnName in columns) {
|
for (final columnName in columns) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'table_writer.dart';
|
||||||
class ViewWriter extends TableOrViewWriter {
|
class ViewWriter extends TableOrViewWriter {
|
||||||
final DriftView view;
|
final DriftView view;
|
||||||
final Scope scope;
|
final Scope scope;
|
||||||
final DatabaseWriter databaseWriter;
|
final DatabaseWriter? databaseWriter;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
late TextEmitter emitter;
|
late TextEmitter emitter;
|
||||||
|
@ -41,10 +41,10 @@ class ViewWriter extends TableOrViewWriter {
|
||||||
}
|
}
|
||||||
buffer.writeln(' implements HasResultSet {');
|
buffer.writeln(' implements HasResultSet {');
|
||||||
|
|
||||||
|
final dbClassName = databaseWriter?.dbClassName ?? 'GeneratedDatabase';
|
||||||
buffer
|
buffer
|
||||||
..writeln('final String? _alias;')
|
..writeln('final String? _alias;')
|
||||||
..writeln(
|
..writeln('@override final $dbClassName attachedDatabase;')
|
||||||
'@override final ${databaseWriter.dbClassName} attachedDatabase;')
|
|
||||||
..writeln('${view.entityInfoName}(this.attachedDatabase, '
|
..writeln('${view.entityInfoName}(this.attachedDatabase, '
|
||||||
'[this._alias]);');
|
'[this._alias]);');
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import 'package:recase/recase.dart';
|
||||||
import 'package:sqlparser/sqlparser.dart' as sql;
|
import 'package:sqlparser/sqlparser.dart' as sql;
|
||||||
|
import 'package:path/path.dart' show url;
|
||||||
|
|
||||||
import '../analysis/results/results.dart';
|
import '../analysis/results/results.dart';
|
||||||
import '../analysis/options.dart';
|
import '../analysis/options.dart';
|
||||||
|
@ -55,14 +57,53 @@ class Writer extends _NodeOrWriter {
|
||||||
abstract class _NodeOrWriter {
|
abstract class _NodeOrWriter {
|
||||||
Writer get writer;
|
Writer get writer;
|
||||||
|
|
||||||
|
AnnotatedDartCode _generatedElement(DriftElement element, String dartName) {
|
||||||
|
if (writer.generationOptions.isModular) {
|
||||||
|
return AnnotatedDartCode([
|
||||||
|
DartTopLevelSymbol(dartName, element.id.modularImportUri),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return AnnotatedDartCode([dartName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotatedDartCode modularAccessor(Uri driftFile) {
|
||||||
|
final id = DriftElementId(driftFile, '(file)');
|
||||||
|
|
||||||
|
return AnnotatedDartCode([
|
||||||
|
DartTopLevelSymbol(
|
||||||
|
ReCase(url.basename(driftFile.path)).pascalCase, id.modularImportUri),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
AnnotatedDartCode companionType(DriftTable table) {
|
AnnotatedDartCode companionType(DriftTable table) {
|
||||||
final baseName = writer.options.useDataClassNameForCompanions
|
final baseName = writer.options.useDataClassNameForCompanions
|
||||||
? table.nameOfRowClass
|
? table.nameOfRowClass
|
||||||
: table.baseDartName;
|
: table.baseDartName;
|
||||||
|
|
||||||
return AnnotatedDartCode([
|
return _generatedElement(table, '${baseName}Companion');
|
||||||
DartTopLevelSymbol('${baseName}Companion', table.id.libraryUri),
|
}
|
||||||
]);
|
|
||||||
|
AnnotatedDartCode entityInfoType(DriftElementWithResultSet element) {
|
||||||
|
return _generatedElement(element, element.entityInfoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotatedDartCode rowType(DriftElementWithResultSet element) {
|
||||||
|
final existing = element.existingRowClass;
|
||||||
|
if (existing != null) {
|
||||||
|
return existing.targetType;
|
||||||
|
} else {
|
||||||
|
return _generatedElement(element, element.nameOfRowClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotatedDartCode rowClass(DriftElementWithResultSet element) {
|
||||||
|
final existing = element.existingRowClass;
|
||||||
|
if (existing != null) {
|
||||||
|
return existing.targetClass;
|
||||||
|
} else {
|
||||||
|
return _generatedElement(element, element.nameOfRowClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a Dart expression evaluating to the [converter].
|
/// Returns a Dart expression evaluating to the [converter].
|
||||||
|
@ -139,24 +180,6 @@ abstract class _NodeOrWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnotatedDartCode rowType(DriftElementWithResultSet element) {
|
|
||||||
final existing = element.existingRowClass;
|
|
||||||
if (existing != null) {
|
|
||||||
return existing.targetType;
|
|
||||||
} else {
|
|
||||||
return AnnotatedDartCode([element.nameOfRowClass]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnnotatedDartCode rowClass(DriftElementWithResultSet element) {
|
|
||||||
final existing = element.existingRowClass;
|
|
||||||
if (existing != null) {
|
|
||||||
return existing.targetClass;
|
|
||||||
} else {
|
|
||||||
return AnnotatedDartCode([element.nameOfRowClass]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String refUri(Uri definition, String element) {
|
String refUri(Uri definition, String element) {
|
||||||
final prefix =
|
final prefix =
|
||||||
writer.generationOptions.imports.prefixFor(definition, element);
|
writer.generationOptions.imports.prefixFor(definition, element);
|
||||||
|
@ -168,6 +191,12 @@ abstract class _NodeOrWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// References a top-level symbol exposed by the core `package:drift/drift.dart`
|
||||||
|
/// library.
|
||||||
|
String drift(String element) {
|
||||||
|
return refUri(AnnotatedDartCode.drift, element);
|
||||||
|
}
|
||||||
|
|
||||||
String dartCode(AnnotatedDartCode code) {
|
String dartCode(AnnotatedDartCode code) {
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
|
@ -291,6 +320,10 @@ class GenerationOptions {
|
||||||
/// Whether companions should be generated.
|
/// Whether companions should be generated.
|
||||||
final bool writeCompanions;
|
final bool writeCompanions;
|
||||||
|
|
||||||
|
/// Whether multiple files are generated, instead of just generating one file
|
||||||
|
/// for each database.
|
||||||
|
final bool isModular;
|
||||||
|
|
||||||
final ImportManager imports;
|
final ImportManager imports;
|
||||||
|
|
||||||
const GenerationOptions({
|
const GenerationOptions({
|
||||||
|
@ -298,6 +331,7 @@ class GenerationOptions {
|
||||||
this.forSchema,
|
this.forSchema,
|
||||||
this.writeDataClasses = true,
|
this.writeDataClasses = true,
|
||||||
this.writeCompanions = true,
|
this.writeCompanions = true,
|
||||||
|
this.isModular = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Whether, instead of generating the full database code, we're only
|
/// Whether, instead of generating the full database code, we're only
|
||||||
|
|
|
@ -6,5 +6,6 @@ This collection of examples demonstrates how to use some advanced drift features
|
||||||
- `encryption`: A very simple Flutter app running an encrypted drift database.
|
- `encryption`: A very simple Flutter app running an encrypted drift database.
|
||||||
- `flutter_web_worker_example`: Asynchronously run a drift database through a web worker with Fluter.
|
- `flutter_web_worker_example`: Asynchronously run a drift database through a web worker with Fluter.
|
||||||
- `migrations_example`: Example showing to how to generate test utilities to verify schema migration.
|
- `migrations_example`: Example showing to how to generate test utilities to verify schema migration.
|
||||||
|
- `modular`: Example using drift's upcoming modular generation mode.
|
||||||
- `web_worker_-_example`: Asynchronously run a drift database through a web worker, without Flutter.
|
- `web_worker_-_example`: Asynchronously run a drift database through a web worker, without Flutter.
|
||||||
- `with_built_value`: Configure `build_runner` so that drift-generated classes can be used by `build_runner`.
|
- `with_built_value`: Configure `build_runner` so that drift-generated classes can be used by `build_runner`.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Files and directories created by pub.
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
|
@ -0,0 +1 @@
|
||||||
|
include: package:lints/recommended.yaml
|
|
@ -0,0 +1,8 @@
|
||||||
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:modular/database.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
final database = Database(NativeDatabase.memory(logStatements: true));
|
||||||
|
|
||||||
|
database.usersDrift.findUsers().watch().listen(print);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
targets:
|
||||||
|
$default:
|
||||||
|
builders:
|
||||||
|
drift_dev:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
drift_dev:analyzer:
|
||||||
|
enabled: true
|
||||||
|
drift_dev:modular:
|
||||||
|
enabled: true
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
import 'database.drift.dart';
|
||||||
|
|
||||||
|
@DriftDatabase(include: {'src/users.drift'})
|
||||||
|
class Database extends $Database {
|
||||||
|
Database(super.e);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get schemaVersion => 1;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
import 'package:drift/drift.dart' as i0;
|
||||||
|
import 'package:modular/src/users.drift.dart' as i1;
|
||||||
|
import 'package:drift/internal/modular.dart' as i2;
|
||||||
|
|
||||||
|
abstract class $Database extends i0.GeneratedDatabase {
|
||||||
|
$Database(i0.QueryExecutor e) : super(e);
|
||||||
|
late final i1.Users users = i1.Users(this);
|
||||||
|
i1.UsersDrift get usersDrift =>
|
||||||
|
i2.ReadDatabaseContainer(this).accessor<i1.UsersDrift>(
|
||||||
|
'package:modular/src/users.drift', i1.UsersDrift.new);
|
||||||
|
@override
|
||||||
|
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||||
|
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||||
|
@override
|
||||||
|
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [users];
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
biography TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
findUsers($predicate = TRUE): SELECT * FROM users WHERE $predicate;
|
|
@ -0,0 +1,243 @@
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
import 'package:drift/drift.dart' as i0;
|
||||||
|
import 'package:modular/src/users.drift.dart' as i1;
|
||||||
|
import 'package:drift/internal/modular.dart' as i2;
|
||||||
|
|
||||||
|
class User extends i0.DataClass implements i0.Insertable<i1.User> {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
final String? biography;
|
||||||
|
const User({required this.id, required this.name, this.biography});
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
map['id'] = i0.Variable<int>(id);
|
||||||
|
map['name'] = i0.Variable<String>(name);
|
||||||
|
if (!nullToAbsent || biography != null) {
|
||||||
|
map['biography'] = i0.Variable<String>(biography);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.UsersCompanion toCompanion(bool nullToAbsent) {
|
||||||
|
return i1.UsersCompanion(
|
||||||
|
id: i0.Value(id),
|
||||||
|
name: i0.Value(name),
|
||||||
|
biography: biography == null && nullToAbsent
|
||||||
|
? const i0.Value.absent()
|
||||||
|
: i0.Value(biography),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory User.fromJson(Map<String, dynamic> json,
|
||||||
|
{i0.ValueSerializer? serializer}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return User(
|
||||||
|
id: serializer.fromJson<int>(json['id']),
|
||||||
|
name: serializer.fromJson<String>(json['name']),
|
||||||
|
biography: serializer.fromJson<String?>(json['biography']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'id': serializer.toJson<int>(id),
|
||||||
|
'name': serializer.toJson<String>(name),
|
||||||
|
'biography': serializer.toJson<String?>(biography),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.User copyWith(
|
||||||
|
{int? id,
|
||||||
|
String? name,
|
||||||
|
i0.Value<String?> biography = const i0.Value.absent()}) =>
|
||||||
|
i1.User(
|
||||||
|
id: id ?? this.id,
|
||||||
|
name: name ?? this.name,
|
||||||
|
biography: biography.present ? biography.value : this.biography,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('User(')
|
||||||
|
..write('id: $id, ')
|
||||||
|
..write('name: $name, ')
|
||||||
|
..write('biography: $biography')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(id, name, biography);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is i1.User &&
|
||||||
|
other.id == this.id &&
|
||||||
|
other.name == this.name &&
|
||||||
|
other.biography == this.biography);
|
||||||
|
}
|
||||||
|
|
||||||
|
class UsersCompanion extends i0.UpdateCompanion<i1.User> {
|
||||||
|
final i0.Value<int> id;
|
||||||
|
final i0.Value<String> name;
|
||||||
|
final i0.Value<String?> biography;
|
||||||
|
const UsersCompanion({
|
||||||
|
this.id = const i0.Value.absent(),
|
||||||
|
this.name = const i0.Value.absent(),
|
||||||
|
this.biography = const i0.Value.absent(),
|
||||||
|
});
|
||||||
|
UsersCompanion.insert({
|
||||||
|
this.id = const i0.Value.absent(),
|
||||||
|
required String name,
|
||||||
|
this.biography = const i0.Value.absent(),
|
||||||
|
}) : name = i0.Value(name);
|
||||||
|
static i0.Insertable<i1.User> custom({
|
||||||
|
i0.Expression<int>? id,
|
||||||
|
i0.Expression<String>? name,
|
||||||
|
i0.Expression<String>? biography,
|
||||||
|
}) {
|
||||||
|
return i0.RawValuesInsertable({
|
||||||
|
if (id != null) 'id': id,
|
||||||
|
if (name != null) 'name': name,
|
||||||
|
if (biography != null) 'biography': biography,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.UsersCompanion copyWith(
|
||||||
|
{i0.Value<int>? id,
|
||||||
|
i0.Value<String>? name,
|
||||||
|
i0.Value<String?>? biography}) {
|
||||||
|
return i1.UsersCompanion(
|
||||||
|
id: id ?? this.id,
|
||||||
|
name: name ?? this.name,
|
||||||
|
biography: biography ?? this.biography,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
if (id.present) {
|
||||||
|
map['id'] = i0.Variable<int>(id.value);
|
||||||
|
}
|
||||||
|
if (name.present) {
|
||||||
|
map['name'] = i0.Variable<String>(name.value);
|
||||||
|
}
|
||||||
|
if (biography.present) {
|
||||||
|
map['biography'] = i0.Variable<String>(biography.value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('i1.UsersCompanion(')
|
||||||
|
..write('id: $id, ')
|
||||||
|
..write('name: $name, ')
|
||||||
|
..write('biography: $biography')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Users extends i0.Table with i0.TableInfo<Users, i1.User> {
|
||||||
|
@override
|
||||||
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
|
final String? _alias;
|
||||||
|
Users(this.attachedDatabase, [this._alias]);
|
||||||
|
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||||
|
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
|
||||||
|
'id', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.int,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
$customConstraints: 'NOT NULL PRIMARY KEY');
|
||||||
|
static const i0.VerificationMeta _nameMeta =
|
||||||
|
const i0.VerificationMeta('name');
|
||||||
|
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
|
||||||
|
'name', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
$customConstraints: 'NOT NULL');
|
||||||
|
static const i0.VerificationMeta _biographyMeta =
|
||||||
|
const i0.VerificationMeta('biography');
|
||||||
|
late final i0.GeneratedColumn<String> biography = i0.GeneratedColumn<String>(
|
||||||
|
'biography', aliasedName, true,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
$customConstraints: '');
|
||||||
|
@override
|
||||||
|
List<i0.GeneratedColumn> get $columns => [id, name, biography];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? 'users';
|
||||||
|
@override
|
||||||
|
String get actualTableName => 'users';
|
||||||
|
@override
|
||||||
|
i0.VerificationContext validateIntegrity(i0.Insertable<i1.User> instance,
|
||||||
|
{bool isInserting = false}) {
|
||||||
|
final context = i0.VerificationContext();
|
||||||
|
final data = instance.toColumns(true);
|
||||||
|
if (data.containsKey('id')) {
|
||||||
|
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||||
|
}
|
||||||
|
if (data.containsKey('name')) {
|
||||||
|
context.handle(
|
||||||
|
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_nameMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('biography')) {
|
||||||
|
context.handle(_biographyMeta,
|
||||||
|
biography.isAcceptableOrUnknown(data['biography']!, _biographyMeta));
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||||
|
@override
|
||||||
|
i1.User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
|
return i1.User(
|
||||||
|
id: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||||
|
name: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||||
|
biography: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}biography']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Users createAlias(String alias) {
|
||||||
|
return Users(attachedDatabase, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get customConstraints => const [];
|
||||||
|
@override
|
||||||
|
bool get dontWriteConstraints => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UsersDrift extends i2.ModularAccessor {
|
||||||
|
UsersDrift(i0.GeneratedDatabase db) : super(db);
|
||||||
|
i0.Selectable<i1.User> findUsers({FindUsers$predicate? predicate}) {
|
||||||
|
var $arrayStartIndex = 1;
|
||||||
|
final generatedpredicate = $write(
|
||||||
|
predicate?.call(this.users) ?? const i0.CustomExpression('(TRUE)'),
|
||||||
|
startIndex: $arrayStartIndex);
|
||||||
|
$arrayStartIndex += generatedpredicate.amountOfVariables;
|
||||||
|
return customSelect('SELECT * FROM users WHERE ${generatedpredicate.sql}',
|
||||||
|
variables: [
|
||||||
|
...generatedpredicate.introducedVariables
|
||||||
|
],
|
||||||
|
readsFrom: {
|
||||||
|
users,
|
||||||
|
...generatedpredicate.watchedTables,
|
||||||
|
}).asyncMap(users.mapFromRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.Users get users => this.resultSet<i1.Users>('users');
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef FindUsers$predicate = i0.Expression<bool> Function(Users users);
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: modular
|
||||||
|
version: 1.0.0
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0 <3.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
drift:
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
build_runner: ^2.3.2
|
||||||
|
drift_dev:
|
||||||
|
lints: ^2.0.0
|
||||||
|
test: ^1.16.0
|
Loading…
Reference in New Issue