From 1da0664c08faea9bbbb522d5ca9e75880d98b812 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Fri, 29 Mar 2024 02:20:58 -0400 Subject: [PATCH 01/74] Initial Manager Commit --- drift/lib/drift.dart | 1 + drift/lib/src/runtime/manager/composer.dart | 119 ++++++++++ drift/lib/src/runtime/manager/filter.dart | 197 ++++++++++++++++ drift/lib/src/runtime/manager/join.dart | 65 ++++++ drift/lib/src/runtime/manager/manager.dart | 219 ++++++++++++++++++ drift/lib/src/runtime/manager/ordering.dart | 63 +++++ drift_dev/lib/src/writer/database_writer.dart | 4 + drift_dev/lib/src/writer/manager.dart | 154 ++++++++++++ 8 files changed, 822 insertions(+) create mode 100644 drift/lib/src/runtime/manager/composer.dart create mode 100644 drift/lib/src/runtime/manager/filter.dart create mode 100644 drift/lib/src/runtime/manager/join.dart create mode 100644 drift/lib/src/runtime/manager/manager.dart create mode 100644 drift/lib/src/runtime/manager/ordering.dart create mode 100644 drift_dev/lib/src/writer/manager.dart diff --git a/drift/lib/drift.dart b/drift/lib/drift.dart index a844bb07..6bb2c6ed 100644 --- a/drift/lib/drift.dart +++ b/drift/lib/drift.dart @@ -20,6 +20,7 @@ export 'src/runtime/query_builder/query_builder.dart' hide CaseWhenExpressionWithBase, BaseCaseWhenExpression; export 'src/runtime/types/converters.dart'; export 'src/runtime/types/mapping.dart' hide BaseSqlType, UserDefinedSqlType; +export 'src/runtime/manager/manager.dart'; export 'src/utils/lazy_database.dart'; /// A [ListEquality] instance used by generated drift code for the `==` and diff --git a/drift/lib/src/runtime/manager/composer.dart b/drift/lib/src/runtime/manager/composer.dart new file mode 100644 index 00000000..201af3d8 --- /dev/null +++ b/drift/lib/src/runtime/manager/composer.dart @@ -0,0 +1,119 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +part of 'manager.dart'; + +/// A class that holds the state for a query composer +@internal +class ComposerState + implements HasJoinBuilders { + /// The database that the query will be executed on + final DB db; + + /// The table that the query will be executed on + final T table; + + @override + final Set joinBuilders; + @override + void addJoinBuilder(JoinBuilder builder) { + joinBuilders.add(builder); + } + + /// Get a random alias for a table + String _getRandomAlias(TableInfo table) { + var aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}'; + while (joinBuilders.aliasedNames.contains(aliasName)) { + aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}'; + continue; + } + return aliasName; + } + + /// Create a new query composer state + ComposerState._(this.db, this.table, Set? joinBuilders) + : joinBuilders = joinBuilders ?? {}; +} + +@internal +class AliasedComposerBuilder { + ComposerState state; + CT aliasedTable; + AliasedComposerBuilder( + this.state, + this.aliasedTable, + ); +} + +/// Base class for all query composers +@internal +sealed class Composer { + /// The state of the query composer + final ComposerState state; + + Composer.withAliasedTable(AliasedComposerBuilder data) + : state = ComposerState._( + data.state.db, data.aliasedTable, data.state.joinBuilders); + Composer.empty(DB db, CT table) : state = ComposerState._(db, table, {}); + + /// Helper method for creaing an aliased join + /// and adding it to the state and Composable object + + B referenced, + B extends HasJoinBuilders>({ + required GeneratedColumn Function(CT) getCurrentColumn, + required RT referencedTable, + required GeneratedColumn Function(RT) getReferencedColumn, + required B Function(QC) builder, + required QC Function(AliasedComposerBuilder data) + getReferencedQueryComposer, + }) { + // Create an alias of the referenced table + // We will never use `referencedTable` again, only the alias + final aliasedReferencedTable = state.db.alias(referencedTable as TableInfo, + state._getRandomAlias(referencedTable)) as RT; + + // We are a function to get the column of the current table, + // This is so that if the `table` in `state` is an alias, + // The user can't supply a column that does not have an alias + // E.G. db.table.id instead of state.table.id + final currentColumn = getCurrentColumn(state.table); + + // We are using a function to get the column of the referenced table + // This is so that the user cant by mistake use a column of a table that is not aliased + final referencedColumn = getReferencedColumn(aliasedReferencedTable); + + // Create a join builder for the referenced table + final joinBuilder = JoinBuilder( + currentTable: state.table, + referencedTable: aliasedReferencedTable, + currentColumn: currentColumn, + referencedColumn: referencedColumn, + ); + + // Add the join builder to the state + state.addJoinBuilder(joinBuilder); + + // Get the query composer for the referenced table, passing in the aliased + // table and all the join builders + final referencedQueryComposer = getReferencedQueryComposer( + AliasedComposerBuilder(this.state, aliasedReferencedTable)); + + // Run the user provided builder with the referencedQueryComposer + // This may return a filter or ordering, but we only enforce that it's + // a HasJoinBuilders + final result = builder(referencedQueryComposer); + + // At this point it is possible that the result has created more joins + // that state doesnt have, it is also possible that the result is missing + // the `joinBuilder` we create above. + // We will combine both sets and set it to `state.joinBuilders` and `result.joinBuilders` + + // Add the joins that may have been created in the filterBuilder to state + for (var joinBuilder in result.joinBuilders.union(state.joinBuilders)) { + state.addJoinBuilder(joinBuilder); + result.addJoinBuilder(joinBuilder); + } + + return result; + } +} diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart new file mode 100644 index 00000000..2b5015cd --- /dev/null +++ b/drift/lib/src/runtime/manager/filter.dart @@ -0,0 +1,197 @@ +part of 'manager.dart'; + +/// Defines a class that can be used to compose filters for a column +class ColumnFilters { + /// This class is a wrapper on top of the generated column class + /// + /// It's used to expose filter functions for a column type + /// + /// Use an extention to add more filters to any column type + /// + /// ```dart + /// extension on FilterComposer{ + /// FitlerBuilder after2000() => isAfter(DateTime(2000)); + ///} + /// ``` + ColumnFilters(this.column); + + /// Column that this [ColumnFilters] wraps + GeneratedColumn column; + + // ignore: public_member_api_docs + ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); + // ignore: public_member_api_docs + ComposableFilter isNotNull() => ComposableFilter.simple(column.isNotNull()); + // ignore: public_member_api_docs + ComposableFilter equals(T value) => + ComposableFilter.simple(column.equals(value)); +} + +/// Built in filters for int/double columns +extension NumFilters on ColumnFilters { + // ignore: public_member_api_docs + ComposableFilter isBiggerThan(T value) => + ComposableFilter.simple(column.isBiggerThanValue(value)); +// ignore: public_member_api_docs + ComposableFilter isNotBiggerThan(T value) => isBiggerThan(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isSmallerThan(T value) => + ComposableFilter.simple(column.isSmallerThanValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotSmallerThan(T value) => + isSmallerThan(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isBiggerOrEqualTo(T value) => + ComposableFilter.simple(column.isBiggerOrEqualValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotBiggerOrEqualTo(T value) => + isBiggerOrEqualTo(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isSmallerOrEqualTo(T value) => + ComposableFilter.simple(column.isSmallerOrEqualValue(value)); +// ignore: public_member_api_docs + ComposableFilter isNotSmallerOrEqualTo(T value) => + isSmallerOrEqualTo(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isBetween(T lower, T higher) => + ComposableFilter.simple(column.isBetweenValues(lower, higher)); +// ignore: public_member_api_docs + ComposableFilter isNotBetween(T lower, T higher) => + isBetween(lower, higher)._reversed(); +} + +/// Built in filters for BigInt columns +extension BigIntFilters on ColumnFilters { + // ignore: public_member_api_docs + ComposableFilter isBiggerThan(T value) => + ComposableFilter.simple(column.isBiggerThanValue(value)); +// ignore: public_member_api_docs + ComposableFilter isNotBiggerThan(T value) => isBiggerThan(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isSmallerThan(T value) => + ComposableFilter.simple(column.isSmallerThanValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotSmallerThan(T value) => + isSmallerThan(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isBiggerOrEqualTo(T value) => + ComposableFilter.simple(column.isBiggerOrEqualValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotBiggerOrEqualTo(T value) => + isBiggerOrEqualTo(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isSmallerOrEqualTo(T value) => + ComposableFilter.simple(column.isSmallerOrEqualValue(value)); +// ignore: public_member_api_docs + ComposableFilter isNotSmallerOrEqualTo(T value) => + isSmallerOrEqualTo(value)._reversed(); +// ignore: public_member_api_docs + ComposableFilter isBetween(T lower, T higher) => + ComposableFilter.simple(column.isBetweenValues(lower, higher)); +// ignore: public_member_api_docs + ComposableFilter isNotBetween(T lower, T higher) => + isBetween(lower, higher)._reversed(); +} + +/// Built in filters for String columns +extension DateFilters on ColumnFilters { +// ignore: public_member_api_docs + ComposableFilter isAfter(T value) => + ComposableFilter.simple(column.isBiggerThanValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotAfter(T value) => isAfter(value)._reversed(); + + // ignore: public_member_api_docs + ComposableFilter isBefore(T value) => + ComposableFilter.simple(column.isSmallerThanValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotBefore(T value) => isBefore(value)._reversed(); + + // ignore: public_member_api_docs + ComposableFilter isAfterOrOn(T value) => + ComposableFilter.simple(column.isBiggerOrEqualValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotAfterOrOn(T value) => isAfterOrOn(value)._reversed(); + + // ignore: public_member_api_docs + ComposableFilter isBeforeOrOn(T value) => + ComposableFilter.simple(column.isSmallerOrEqualValue(value)); + // ignore: public_member_api_docs + ComposableFilter isNotBeforeOrOn(T value) => isBeforeOrOn(value)._reversed(); + + // ignore: public_member_api_docs + ComposableFilter isBetween(T lower, T higher) => + ComposableFilter.simple(column.isBetweenValues(lower, higher)); + // ignore: public_member_api_docs + ComposableFilter isNotBetween(T lower, T higher) => + isBetween(lower, higher)._reversed(); +} + +/// Defines a class that can be used to compose filters for a column +class ComposableFilter implements HasJoinBuilders { + @override + final Set joinBuilders; + @override + void addJoinBuilder(JoinBuilder builder) { + joinBuilders.add(builder); + } + + /// The expression that will be applied to the query + final Expression expression; + + /// This class is wrapper on expression class + /// + /// It contains the expression, along with any joins that are required + /// to execute the expression + /// + /// It also contains a list of aliases that were used in joins + /// This will be used to ensure that the same alias isn't used twice + ComposableFilter.withJoin( + this.expression, + this.joinBuilders, + ); + + // ignore: public_member_api_docs + ComposableFilter.simple(this.expression) : joinBuilders = {}; + + /// Combine two filters with an AND + ComposableFilter operator &(ComposableFilter other) { + return ComposableFilter.withJoin( + expression & other.expression, + joinBuilders.union(other.joinBuilders), + ); + } + + /// Combine two filters with an OR + ComposableFilter operator |(ComposableFilter other) { + return ComposableFilter.withJoin( + expression | other.expression, + joinBuilders.union(other.joinBuilders), + ); + } + + /// Returns a copy of this filter with the expression reversed + ComposableFilter _reversed() { + return ComposableFilter.withJoin( + expression.not(), + joinBuilders, + ); + } + + /// Returns a copy of this filter with the given joins and usedAliases + ComposableFilter copyWithJoins(Set joinBuilders) { + return ComposableFilter.withJoin( + expression, joinBuilders.union(this.joinBuilders)); + } +} + +/// The class that orchestrates the composition of filtering +class FilterComposer + extends Composer { + /// Create a new filter composer from existing query state + // FilterComposer(super.state); + + /// Create a filter composer with an empty state + FilterComposer.empty(super.db, super.table) : super.empty(); + FilterComposer.withAliasedTable(super.data) : super.withAliasedTable(); +} diff --git a/drift/lib/src/runtime/manager/join.dart b/drift/lib/src/runtime/manager/join.dart new file mode 100644 index 00000000..7868ba86 --- /dev/null +++ b/drift/lib/src/runtime/manager/join.dart @@ -0,0 +1,65 @@ +part of 'manager.dart'; + +/// A class that contains the information needed to create a join +@internal +class JoinBuilder { + /// The table that the join is being applied to + final Table currentTable; + + /// The referenced table that will be joined + final Table referencedTable; + + /// The column of the current database which will be use to create the join + final GeneratedColumn currentColumn; + + /// The column of the referenced database which will be use to create the join + + final GeneratedColumn referencedColumn; + + /// Class that describes how a ordering that is being + /// applied to a referenced table + /// should be joined to the current table + JoinBuilder( + {required this.currentTable, + required this.referencedTable, + required this.currentColumn, + required this.referencedColumn}); + + /// The name of the alias that this join will use + String get aliasedName { + return referencedColumn.tableName; + } + + @override + bool operator ==(covariant JoinBuilder other) { + if (identical(this, other)) return true; + + return other.aliasedName == aliasedName; + } + + @override + int get hashCode { + return aliasedName.hashCode; + } + + /// Build a join from this join builder + Join buildJoin() { + return leftOuterJoin( + referencedTable, currentColumn.equalsExp(referencedColumn)); + } +} + +/// An interface for classes that hold join builders +@internal +abstract interface class HasJoinBuilders { + /// The join builders that are associated with this class + Set get joinBuilders; + + /// Add a join builder to this class + void addJoinBuilder(JoinBuilder builder); +} + +/// Helper for getting all the aliased names of a set of join builders +extension on Set { + List get aliasedNames => map((e) => (e.aliasedName)).toList(); +} diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart new file mode 100644 index 00000000..a6c53a81 --- /dev/null +++ b/drift/lib/src/runtime/manager/manager.dart @@ -0,0 +1,219 @@ +import 'dart:math'; +import 'package:drift/drift.dart'; +import 'package:meta/meta.dart'; + +part 'composer.dart'; +part 'filter.dart'; +part 'join.dart'; +part 'ordering.dart'; + +/// Defines a class that holds the state for a table manager +class TableManagerState< + DB extends GeneratedDatabase, + T extends TableInfo, + DT extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer> { + /// The database that the query will be executed on + final DB db; + + /// The table that the query will be executed on + final T table; + + /// The expression that will be applied to the query + final Expression? filter; + + /// The orderings that will be applied to the query + final Set orderingBuilders; + + /// The joins that will be applied to the query + final Set joinBuilders; + + /// Whether the query should return distinct results + final bool? distinct; + + /// If set, the maximum number of rows that will be returned + final int? limit; + + /// If set, the number of rows that will be skipped + final int? offset; + + /// The composer for the filters + final FS filteringComposer; + + /// The composer for the orderings + final OS orderingComposer; + + /// Defines a class which holds the state for a table manager + /// It contains the database instance, the table instance, and any filters/orderings that will be applied to the query + /// This is held in a seperate class so that the state can be passed down from the root manager to the lower level managers + TableManagerState({ + required this.db, + required this.table, + required this.filteringComposer, + required this.orderingComposer, + this.filter, + this.distinct, + this.limit, + this.offset, + Set? orderingTerms, + Set? joinBuilders, + }) : orderingBuilders = orderingTerms ?? {}, + joinBuilders = joinBuilders ?? {}; + + /// Copy this state with the given values + TableManagerState copyWith({ + bool? distinct, + int? limit, + int? offset, + Expression? filter, + Set? orderingTerms, + Set? joinBuilders, + }) { + return TableManagerState( + db: db, + table: table, + filteringComposer: filteringComposer, + orderingComposer: orderingComposer, + filter: filter ?? this.filter, + joinBuilders: joinBuilders ?? this.joinBuilders, + orderingTerms: orderingTerms ?? this.orderingBuilders, + distinct: distinct ?? this.distinct, + limit: limit ?? this.limit, + offset: offset ?? this.offset, + ); + } + + /// Helper for getting the table that's casted as a TableInfo + /// This is needed due to dart's limitations with generics + TableInfo get _tableAsTableInfo => table as TableInfo; + + /// Builds a joined select statement. + JoinedSelectStatement _buildJoinedSelectStatement() { + // Build the joins + final joins = joinBuilders.map((e) => e.buildJoin()).toList(); + + // Create the joined statement + final statement = + db.select(_tableAsTableInfo, distinct: distinct ?? false).join(joins); + + // Apply the expression to the statement + if (filter != null) { + statement.where(filter!); + } + + // Add orderings + statement.orderBy(orderingBuilders.map((e) => e.buildTerm()).toList()); + + // Set the limit and offset + if (limit != null) { + statement.limit(limit!, offset: offset); + } + return statement; + } + + /// Builds a simple select statement + SimpleSelectStatement _buildSimpleSelectStatement() { + // Create the joined statement + final statement = db.select(_tableAsTableInfo, distinct: distinct ?? false); + + // Apply the expression to the statement + if (filter != null) { + statement.where((_) => filter!); + } + + // Add orderings + statement + .orderBy(orderingBuilders.map((e) => (_) => e.buildTerm()).toList()); + + // Set the limit and offset + if (limit != null) { + statement.limit(limit!, offset: offset); + } + return statement; + } + + /// Build a select statement based on the manager state + Selectable
buildSelectStatement() { + if (joinBuilders.isEmpty) { + return _buildSimpleSelectStatement(); + } else { + return _buildJoinedSelectStatement() + .map((p0) => p0.readTable(_tableAsTableInfo)); + } + } + + /// Build a delete statement based on the manager state + // DeleteStatement buildDeleteStatement() { + // // Being that drift doesnt support joins on deletes, if there are any joins we will use 2 queries + // final DeleteStatement deleteStatement; + // // Check if there are any joins + // if (_joins.isEmpty) { + // // If there are no joins, we can just use a single delete statement + // deleteStatement = db.delete(_tableAsTableInfo); + // if (filter?.expression != null) { + // deleteStatement.where((_) => filter!.expression); + // } + // } else { + // // If there are joins, we need to use a subquery to get the rowIds + // final selectOnlyRowIdStatement = + // _buildJoinedStatement(onlyWithRowId: true); + // deleteStatement = db.delete(_tableAsTableInfo) + // ..where( + // (_) => _tableAsTableInfo.rowId.isInQuery(selectOnlyRowIdStatement)); + // } + // return deleteStatement; + // } +} + +/// Defines the base class for a table manager +@internal +abstract class BaseTableManager< + DB extends GeneratedDatabase, + T extends TableInfo, + DT extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer> { + /// The state for this manager + final TableManagerState state; + + /// Create a new [BaseTableManager] instance + BaseTableManager(this.state); +} + +/// Defines the top level manager for a table +abstract class RootTableManager< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer> + extends BaseTableManager { + /// Top level manager for a table. + /// This class contains the top level manager functions for the table. + RootTableManager(super.state); + Future insert(Insertable item) => + state.db.into(state._tableAsTableInfo).insert(item); + Future insertAllBatch(List> items) => state.db + .batch((batch) => batch.insertAll(state._tableAsTableInfo, items)); + Future> getAll() => state.buildSelectStatement().get(); + Stream> watchAll() => state.buildSelectStatement().watch(); + // Future deleteAll() => state.buildDeleteStatement().go(); +} + +/// Defines a manager for a table that can be used to build queries +abstract class ProcessedTableManager< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer> + extends BaseTableManager { + /// A table manager which uses it's internal state to build queries + ProcessedTableManager(super.state); + Future getSingle() => state.buildSelectStatement().getSingle(); + Stream watchSingle() => state.buildSelectStatement().watchSingle(); + Future> get() => state.buildSelectStatement().get(); + Stream> watch() => state.buildSelectStatement().watch(); + // Future delete() => state.buildDeleteStatement().go(); +} diff --git a/drift/lib/src/runtime/manager/ordering.dart b/drift/lib/src/runtime/manager/ordering.dart new file mode 100644 index 00000000..ad14e4f2 --- /dev/null +++ b/drift/lib/src/runtime/manager/ordering.dart @@ -0,0 +1,63 @@ +part of 'manager.dart'; + +class OrderingBuilder { + /// The mode of the ordering + final OrderingMode mode; + + /// The column that the ordering is applied to + final GeneratedColumn column; + + OrderingBuilder(this.mode, this.column); + + @override + bool operator ==(covariant OrderingBuilder other) { + if (identical(this, other)) return true; + + return other.mode == mode && other.column == column; + } + + @override + int get hashCode => mode.hashCode ^ column.hashCode; + + /// Build a join from this join builder + OrderingTerm buildTerm() { + return OrderingTerm(mode: mode, expression: column); + } +} + +/// Defines a class that can be used to compose orderings for a column +class ComposableOrdering implements HasJoinBuilders { + final Set orderingBuilders; + @override + final Set joinBuilders; + @override + void addJoinBuilder(JoinBuilder builder) { + joinBuilders.add(builder); + } + + /// Create a new ordering for a column + ComposableOrdering.simple(this.orderingBuilders) : joinBuilders = {}; + ComposableOrdering.withJoin(this.orderingBuilders, this.joinBuilders); + + ComposableOrdering operator |(ComposableOrdering other) { + return ComposableOrdering.withJoin( + orderingBuilders.union(other.orderingBuilders), + joinBuilders.union(other.joinBuilders)); + } + + /// Build a drift [OrderingTerm] from this ordering + List buildTerms() => orderingBuilders + .map((e) => OrderingTerm(mode: e.mode, expression: e.column)) + .toList(); +} + +/// The class that orchestrates the composition of orderings +class OrderingComposer + extends Composer { + /// Create a new ordering composer from existing query state + // OrderingComposer.fromComposer(super.state); + + /// Create an ordering composer with an empty state + OrderingComposer.empty(super.db, super.table) : super.empty(); + OrderingComposer.withAliasedTable(super.data) : super.withAliasedTable(); +} diff --git a/drift_dev/lib/src/writer/database_writer.dart b/drift_dev/lib/src/writer/database_writer.dart index ef550a47..ed4fdbf1 100644 --- a/drift_dev/lib/src/writer/database_writer.dart +++ b/drift_dev/lib/src/writer/database_writer.dart @@ -1,6 +1,7 @@ import 'package:drift/drift.dart' as drift; // ignore: implementation_imports import 'package:drift/src/runtime/executor/stream_queries.dart'; +import 'package:drift_dev/src/writer/manager.dart'; import 'package:drift_dev/src/writer/utils/memoized_getter.dart'; import 'package:recase/recase.dart'; @@ -38,11 +39,14 @@ class DatabaseWriter { void write() { final elements = input.resolvedAccessor.availableElements; + final shortcutWriter = ManagerWriter(scope.child(), dbClassName); + // Write data classes, companions and info classes if (!scope.generationOptions.isModular) { for (final reference in elements) { if (reference is DriftTable) { TableWriter(reference, scope.child()).writeInto(); + shortcutWriter.addTable(reference); } else if (reference is DriftView) { ViewWriter(reference, scope.child(), this).write(); } diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart new file mode 100644 index 00000000..141aef2c --- /dev/null +++ b/drift_dev/lib/src/writer/manager.dart @@ -0,0 +1,154 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:drift/drift.dart'; +import 'package:drift_dev/src/analysis/results/results.dart'; +import 'package:drift_dev/src/writer/writer.dart'; +import 'package:recase/recase.dart'; + +extension on DriftColumn { + bool get isForeignKey { + return constraints.whereType().isNotEmpty; + } +} + +class ManagerWriter { + final Scope scope; + final String dbClassName; + + ManagerWriter(this.scope, this.dbClassName); + + void addTable(DriftTable table) { + final leaf = scope.leaf(); + + final filters = StringBuffer(); + final orderings = StringBuffer(); + + for (var col in table.columns) { + final getterName = + ReCase(col.nameInDart + (col.isForeignKey ? " id" : " ")).camelCase; + + filters.writeln( + "ColumnFilters get $getterName => ColumnFilters(state.table.${col.nameInDart});"); + orderings.writeln( + "ComposableOrdering get ${getterName}Asc => ComposableOrdering.simple({OrderingBuilder( OrderingMode.asc, state.table.${col.nameInDart})});"); + orderings.writeln( + "ComposableOrdering get ${getterName}Desc => ComposableOrdering.simple({OrderingBuilder( OrderingMode.desc, state.table.${col.nameInDart})});"); + + if (col.isForeignKey) { + final referencedCol = col.constraints + .whereType() + .firstOrNull + ?.otherColumn; + if (referencedCol != null) { + if (referencedCol.owner is DriftTable) { + final referencedTableGetter = referencedCol.owner.dbGetterName; + final referencedEntityInfoName = referencedCol.owner.entityInfoName; + if (referencedTableGetter != null) { + filters.write(''' + +ComposableFilter ${col.nameInDart}OrderBy( + ComposableFilter Function(${referencedEntityInfoName}FilterComposer) f) { + return referenced( + referencedTable: state.db.${referencedTableGetter}, + getCurrentColumn: (p0) => p0.${col.nameInDart}, + getReferencedColumn: (p0) => p0.${referencedCol.nameInDart}, + getReferencedQueryComposer: (data) => + ${referencedEntityInfoName}FilterComposer.withAliasedTable(data), + builder: f); + } + + '''); + + orderings.write(''' + +ComposableOrdering ${col.nameInDart}OrderBy( + ComposableOrdering Function(${referencedEntityInfoName}OrderingComposer) f) { + return referenced( + referencedTable: state.db.${referencedTableGetter}, + getCurrentColumn: (p0) => p0.${col.nameInDart}, + getReferencedColumn: (p0) => p0.${referencedCol.nameInDart}, + getReferencedQueryComposer: (data) => + ${referencedEntityInfoName}OrderingComposer.withAliasedTable(data), + builder: f); + } + + '''); + } + } + } + } + } + + leaf.write(""" + +class ${table.entityInfoName}FilterComposer extends FilterComposer<$dbClassName, ${table.entityInfoName}> { + ${table.entityInfoName}FilterComposer.empty(super.db, super.table) : super.empty(); + ${table.entityInfoName}FilterComposer.withAliasedTable(super.data) : super.withAliasedTable(); + + $filters +} + +class ${table.entityInfoName}OrderingComposer extends OrderingComposer<$dbClassName, ${table.entityInfoName}> { + ${table.entityInfoName}OrderingComposer.empty(super.db, super.table) : super.empty(); + ${table.entityInfoName}OrderingComposer.withAliasedTable(super.data) : super.withAliasedTable(); + + $orderings +} + +class ${table.entityInfoName}ProcessedTableManager extends ProcessedTableManager<$dbClassName, + ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { + ${table.entityInfoName}ProcessedTableManager(super.data); +} + +class ${table.entityInfoName}ProcessedTableManagerWithFiltering extends ProcessedTableManager<$dbClassName, + ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { + ${table.entityInfoName}ProcessedTableManagerWithFiltering(super.data); + + ${table.entityInfoName}ProcessedTableManager filter( + ComposableFilter Function(${table.entityInfoName}FilterComposer f) f) { + final filter = f(state.filteringComposer); + return ${table.entityInfoName}ProcessedTableManager(state.copyWith( + filter: filter.expression, + joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + } +} + +class ${table.entityInfoName}ProcessedTableManagerWithOrdering extends ProcessedTableManager<$dbClassName, + ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { + ${table.entityInfoName}ProcessedTableManagerWithOrdering(super.data); + + ${table.entityInfoName}ProcessedTableManager orderBy( + ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) order) { + final ordering = order(state.orderingComposer); + return ${table.entityInfoName}ProcessedTableManager(state.copyWith( + orderingTerms: state.orderingBuilders.union(ordering.orderingBuilders), + joinBuilders: state.joinBuilders.union(ordering.joinBuilders))); + } +} + +class ${table.entityInfoName}TableManager extends ${table.entityInfoName}ProcessedTableManager { + ${table.entityInfoName}TableManager($dbClassName db, ${table.entityInfoName} table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: ${table.entityInfoName}FilterComposer.empty(db, table), + orderingComposer: ${table.entityInfoName}OrderingComposer.empty(db, table))); + + ${table.entityInfoName}ProcessedTableManagerWithOrdering filter( + ComposableFilter Function(${table.entityInfoName}FilterComposer f) f) { + final filter = f(state.filteringComposer); + return ${table.entityInfoName}ProcessedTableManagerWithOrdering(state.copyWith( + filter: filter.expression, + joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + } + + ${table.entityInfoName}ProcessedTableManagerWithFiltering orderBy( + ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) order) { + final ordering = order(state.orderingComposer); + return ${table.entityInfoName}ProcessedTableManagerWithFiltering(state.copyWith( + orderingTerms: state.orderingBuilders.union(ordering.orderingBuilders), + joinBuilders: state.joinBuilders.union(ordering.joinBuilders))); + } +} +"""); + } +} From 46953c3a73523f5a97ea35c1285fb45426dc3f26 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Fri, 29 Mar 2024 10:44:59 -0400 Subject: [PATCH 02/74] Refactor ordering - Add better Docs --- drift/lib/src/runtime/manager/composer.dart | 28 ++++-- drift/lib/src/runtime/manager/filter.dart | 99 +++++++++------------ drift/lib/src/runtime/manager/join.dart | 5 -- drift/lib/src/runtime/manager/manager.dart | 28 +++--- drift/lib/src/runtime/manager/ordering.dart | 49 ++++++++-- drift_dev/lib/src/writer/manager.dart | 4 +- 6 files changed, 126 insertions(+), 87 deletions(-) diff --git a/drift/lib/src/runtime/manager/composer.dart b/drift/lib/src/runtime/manager/composer.dart index 201af3d8..55a8f7b3 100644 --- a/drift/lib/src/runtime/manager/composer.dart +++ b/drift/lib/src/runtime/manager/composer.dart @@ -21,7 +21,7 @@ class ComposerState /// Get a random alias for a table String _getRandomAlias(TableInfo table) { var aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}'; - while (joinBuilders.aliasedNames.contains(aliasName)) { + while (joinBuilders.map((e) => (e.aliasedName)).contains(aliasName)) { aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}'; continue; } @@ -44,10 +44,26 @@ class AliasedComposerBuilder f.id.equals(1) & f.name.equals('Bob')); +/// ``` +/// `f` in this example is a [Composer] object, and `f.id.equals(1) & f.name.equals('Bob')` is a [ComposableFilter] object. +/// +/// The [Composer] class is responsible for creating joins between tables, and passing them down to the composable classes. +/// This is done to ensure that duplicate joins are never created. +/// +/// The [ComposerState] that is held in this class only holds temporary state, as the final state will be held in the composable classes. +/// E.G. In the example above, the resulting [ComposableFilter] object is returned, and the [FilterComposer] is discarded. +/// @internal sealed class Composer { - /// The state of the query composer + /// The state of the composer final ComposerState state; Composer.withAliasedTable(AliasedComposerBuilder data) @@ -55,9 +71,7 @@ sealed class Composer { data.state.db, data.aliasedTable, data.state.joinBuilders); Composer.empty(DB db, CT table) : state = ComposerState._(db, table, {}); - /// Helper method for creaing an aliased join - /// and adding it to the state and Composable object - + /// Method for create a join between two tables B referenced, B extends HasJoinBuilders>({ required GeneratedColumn Function(CT) getCurrentColumn, @@ -107,8 +121,6 @@ sealed class Composer { // that state doesnt have, it is also possible that the result is missing // the `joinBuilder` we create above. // We will combine both sets and set it to `state.joinBuilders` and `result.joinBuilders` - - // Add the joins that may have been created in the filterBuilder to state for (var joinBuilder in result.joinBuilders.union(state.joinBuilders)) { state.addJoinBuilder(joinBuilder); result.addJoinBuilder(joinBuilder); diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 2b5015cd..91b2f89c 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -1,6 +1,6 @@ part of 'manager.dart'; -/// Defines a class that can be used to compose filters for a column +/// Defines a class which is used to wrap a column to only expose filter functions class ColumnFilters { /// This class is a wrapper on top of the generated column class /// @@ -18,111 +18,99 @@ class ColumnFilters { /// Column that this [ColumnFilters] wraps GeneratedColumn column; - // ignore: public_member_api_docs + /// Create a filter that checks if the column is null. ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); - // ignore: public_member_api_docs + + /// Create a filter that checks if the column is not null. ComposableFilter isNotNull() => ComposableFilter.simple(column.isNotNull()); - // ignore: public_member_api_docs + + /// Create a filter that checks if the column equals a value. ComposableFilter equals(T value) => ComposableFilter.simple(column.equals(value)); + + /// Create a filter that checks if the column equals a value. + ComposableFilter call(T value) => + ComposableFilter.simple(column.equals(value)); } /// Built in filters for int/double columns extension NumFilters on ColumnFilters { - // ignore: public_member_api_docs + /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); -// ignore: public_member_api_docs - ComposableFilter isNotBiggerThan(T value) => isBiggerThan(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is small than a value ComposableFilter isSmallerThan(T value) => ComposableFilter.simple(column.isSmallerThanValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotSmallerThan(T value) => - isSmallerThan(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is bigger or equal to a value ComposableFilter isBiggerOrEqualTo(T value) => ComposableFilter.simple(column.isBiggerOrEqualValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotBiggerOrEqualTo(T value) => - isBiggerOrEqualTo(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is small or equal to a value ComposableFilter isSmallerOrEqualTo(T value) => ComposableFilter.simple(column.isSmallerOrEqualValue(value)); -// ignore: public_member_api_docs - ComposableFilter isNotSmallerOrEqualTo(T value) => - isSmallerOrEqualTo(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is between two values ComposableFilter isBetween(T lower, T higher) => ComposableFilter.simple(column.isBetweenValues(lower, higher)); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is not between two values ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._reversed(); } /// Built in filters for BigInt columns extension BigIntFilters on ColumnFilters { - // ignore: public_member_api_docs + /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); -// ignore: public_member_api_docs - ComposableFilter isNotBiggerThan(T value) => isBiggerThan(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is small than a value ComposableFilter isSmallerThan(T value) => ComposableFilter.simple(column.isSmallerThanValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotSmallerThan(T value) => - isSmallerThan(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is bigger or equal to a value ComposableFilter isBiggerOrEqualTo(T value) => ComposableFilter.simple(column.isBiggerOrEqualValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotBiggerOrEqualTo(T value) => - isBiggerOrEqualTo(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is small or equal to a value ComposableFilter isSmallerOrEqualTo(T value) => ComposableFilter.simple(column.isSmallerOrEqualValue(value)); -// ignore: public_member_api_docs - ComposableFilter isNotSmallerOrEqualTo(T value) => - isSmallerOrEqualTo(value)._reversed(); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is between two values ComposableFilter isBetween(T lower, T higher) => ComposableFilter.simple(column.isBetweenValues(lower, higher)); -// ignore: public_member_api_docs + + /// Create a filter to check if the column is not between two values ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._reversed(); } /// Built in filters for String columns extension DateFilters on ColumnFilters { -// ignore: public_member_api_docs + /// Create a filter to check if the column is after a [DateTime] ComposableFilter isAfter(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotAfter(T value) => isAfter(value)._reversed(); - // ignore: public_member_api_docs + /// Create a filter to check if the column is before a [DateTime] ComposableFilter isBefore(T value) => ComposableFilter.simple(column.isSmallerThanValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotBefore(T value) => isBefore(value)._reversed(); - // ignore: public_member_api_docs + /// Create a filter to check if the column is on or after a [DateTime] ComposableFilter isAfterOrOn(T value) => ComposableFilter.simple(column.isBiggerOrEqualValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotAfterOrOn(T value) => isAfterOrOn(value)._reversed(); - // ignore: public_member_api_docs + /// Create a filter to check if the column is before or on a [DateTime] ComposableFilter isBeforeOrOn(T value) => ComposableFilter.simple(column.isSmallerOrEqualValue(value)); - // ignore: public_member_api_docs - ComposableFilter isNotBeforeOrOn(T value) => isBeforeOrOn(value)._reversed(); - // ignore: public_member_api_docs + /// Create a filter to check if the column is between 2 [DateTime]s + ComposableFilter isBetween(T lower, T higher) => ComposableFilter.simple(column.isBetweenValues(lower, higher)); - // ignore: public_member_api_docs + + /// Create a filter to check if the column is not between 2 [DateTime]s ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._reversed(); } @@ -186,12 +174,11 @@ class ComposableFilter implements HasJoinBuilders { } /// The class that orchestrates the composition of filtering -class FilterComposer +class FilterComposer extends Composer { - /// Create a new filter composer from existing query state - // FilterComposer(super.state); - /// Create a filter composer with an empty state FilterComposer.empty(super.db, super.table) : super.empty(); + + /// Create a filter composer using another composers state FilterComposer.withAliasedTable(super.data) : super.withAliasedTable(); } diff --git a/drift/lib/src/runtime/manager/join.dart b/drift/lib/src/runtime/manager/join.dart index 7868ba86..968030be 100644 --- a/drift/lib/src/runtime/manager/join.dart +++ b/drift/lib/src/runtime/manager/join.dart @@ -58,8 +58,3 @@ abstract interface class HasJoinBuilders { /// Add a join builder to this class void addJoinBuilder(JoinBuilder builder); } - -/// Helper for getting all the aliased names of a set of join builders -extension on Set { - List get aliasedNames => map((e) => (e.aliasedName)).toList(); -} diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index a6c53a81..9d14ae67 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -7,10 +7,10 @@ part 'filter.dart'; part 'join.dart'; part 'ordering.dart'; -/// Defines a class that holds the state for a table manager +/// Defines a class that holds the state for a [BaseTableManager] class TableManagerState< DB extends GeneratedDatabase, - T extends TableInfo, + T extends Table, DT extends DataClass, FS extends FilterComposer, OS extends OrderingComposer> { @@ -23,10 +23,12 @@ class TableManagerState< /// The expression that will be applied to the query final Expression? filter; - /// The orderings that will be applied to the query + /// A set of [OrderingBuilder] which will be used to apply + /// [OrderingTerm]s to the statement when it's eventually built final Set orderingBuilders; - /// The joins that will be applied to the query + /// A set of [JoinBuilder] which will be used to create [Join]s + /// that will be applied to the build statement final Set joinBuilders; /// Whether the query should return distinct results @@ -38,15 +40,19 @@ class TableManagerState< /// If set, the number of rows that will be skipped final int? offset; - /// The composer for the filters + /// The [FilterComposer] for this [TableManagerState] + /// This class will be used to create filtering [Expression]s + /// which will be applied to the statement when its eventually created final FS filteringComposer; - /// The composer for the orderings + /// The [OrderingComposer] for this [TableManagerState] + /// This class will be used to create [OrderingTerm]s + /// which will be applied to the statement when its eventually created final OS orderingComposer; /// Defines a class which holds the state for a table manager /// It contains the database instance, the table instance, and any filters/orderings that will be applied to the query - /// This is held in a seperate class so that the state can be passed down from the root manager to the lower level managers + /// This is held in a seperate class than the [BaseTableManager] so that the state can be passed down from the root manager to the lower level managers TableManagerState({ required this.db, required this.table, @@ -88,7 +94,8 @@ class TableManagerState< /// This is needed due to dart's limitations with generics TableInfo get _tableAsTableInfo => table as TableInfo; - /// Builds a joined select statement. + /// Builds a joined select statement, should be used when joins are present + /// Will order, filter, and limit the statement using the state JoinedSelectStatement _buildJoinedSelectStatement() { // Build the joins final joins = joinBuilders.map((e) => e.buildJoin()).toList(); @@ -112,9 +119,10 @@ class TableManagerState< return statement; } - /// Builds a simple select statement + /// Builds a simple select statement, this should be used when there are no joins + /// Will order, filter, and limit the statement using the state SimpleSelectStatement _buildSimpleSelectStatement() { - // Create the joined statement + // Create the statement final statement = db.select(_tableAsTableInfo, distinct: distinct ?? false); // Apply the expression to the statement diff --git a/drift/lib/src/runtime/manager/ordering.dart b/drift/lib/src/runtime/manager/ordering.dart index ad14e4f2..84f52ccd 100644 --- a/drift/lib/src/runtime/manager/ordering.dart +++ b/drift/lib/src/runtime/manager/ordering.dart @@ -1,5 +1,35 @@ part of 'manager.dart'; +/// Defines a class which is used to wrap a column to only expose ordering functions +class ColumnOrderings { + /// This class is a wrapper on top of the generated column class + /// + /// It's used to expose ordering functions for a column + /// + /// ```dart + /// extension on FilterComposer{ + /// FitlerBuilder after2000() => isAfter(DateTime(2000)); + ///} + /// ``` + ColumnOrderings(this.column); + + /// Column that this [ColumnOrderings] wraps + GeneratedColumn column; + + /// Sort this column in ascending order + /// + /// 10 -> 1 | Z -> A | Dec 31 -> Jan 1 + ComposableOrdering asc() => + ComposableOrdering.simple({OrderingBuilder(OrderingMode.asc, column)}); + + /// Sort this column in descending order + /// + /// 1 -> 10 | A -> Z | Jan 1 -> Dec 31 + ComposableOrdering desc() => + ComposableOrdering.simple({OrderingBuilder(OrderingMode.desc, column)}); +} + +/// Defines a class which will hold the information needed to create an ordering class OrderingBuilder { /// The mode of the ordering final OrderingMode mode; @@ -7,6 +37,7 @@ class OrderingBuilder { /// The column that the ordering is applied to final GeneratedColumn column; + /// Create a new ordering builder, will be used by the [TableManagerState] to create [OrderingTerm]s OrderingBuilder(this.mode, this.column); @override @@ -26,7 +57,11 @@ class OrderingBuilder { } /// Defines a class that can be used to compose orderings for a column +/// +/// Multiple orderings can be composed together using the `&` operator. +/// The orderings will be executed from left to right. class ComposableOrdering implements HasJoinBuilders { + /// The orderings that are being composed final Set orderingBuilders; @override final Set joinBuilders; @@ -35,11 +70,14 @@ class ComposableOrdering implements HasJoinBuilders { joinBuilders.add(builder); } - /// Create a new ordering for a column + /// Create a new [ComposableOrdering] for a column without any joins ComposableOrdering.simple(this.orderingBuilders) : joinBuilders = {}; + + /// Create a new [ComposableOrdering] for a column with joins ComposableOrdering.withJoin(this.orderingBuilders, this.joinBuilders); - ComposableOrdering operator |(ComposableOrdering other) { + /// Combine two orderings with THEN + ComposableOrdering operator &(ComposableOrdering other) { return ComposableOrdering.withJoin( orderingBuilders.union(other.orderingBuilders), joinBuilders.union(other.joinBuilders)); @@ -52,12 +90,13 @@ class ComposableOrdering implements HasJoinBuilders { } /// The class that orchestrates the composition of orderings +/// +/// class OrderingComposer extends Composer { - /// Create a new ordering composer from existing query state - // OrderingComposer.fromComposer(super.state); - /// Create an ordering composer with an empty state OrderingComposer.empty(super.db, super.table) : super.empty(); + + /// Create an ordering composer using another composers state OrderingComposer.withAliasedTable(super.data) : super.withAliasedTable(); } diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 141aef2c..b76a91c8 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -29,9 +29,7 @@ class ManagerWriter { filters.writeln( "ColumnFilters get $getterName => ColumnFilters(state.table.${col.nameInDart});"); orderings.writeln( - "ComposableOrdering get ${getterName}Asc => ComposableOrdering.simple({OrderingBuilder( OrderingMode.asc, state.table.${col.nameInDart})});"); - orderings.writeln( - "ComposableOrdering get ${getterName}Desc => ComposableOrdering.simple({OrderingBuilder( OrderingMode.desc, state.table.${col.nameInDart})});"); + "ColumnOrderings get $getterName => ColumnOrderings(state.table.${col.nameInDart});"); if (col.isForeignKey) { final referencedCol = col.constraints From 7ed6ac57350e67498ef88881013aa94720d44168 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Fri, 29 Mar 2024 11:08:28 -0400 Subject: [PATCH 03/74] Hide some exports - Add manager to Database --- drift/lib/drift.dart | 9 ++- drift_dev/lib/src/writer/database_writer.dart | 8 ++- drift_dev/lib/src/writer/manager.dart | 64 ++++++++++++++----- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/drift/lib/drift.dart b/drift/lib/drift.dart index 6bb2c6ed..a9f63b7b 100644 --- a/drift/lib/drift.dart +++ b/drift/lib/drift.dart @@ -20,7 +20,14 @@ export 'src/runtime/query_builder/query_builder.dart' hide CaseWhenExpressionWithBase, BaseCaseWhenExpression; export 'src/runtime/types/converters.dart'; export 'src/runtime/types/mapping.dart' hide BaseSqlType, UserDefinedSqlType; -export 'src/runtime/manager/manager.dart'; +export 'src/runtime/manager/manager.dart' + hide + JoinBuilder, + ComposerState, + HasJoinBuilders, + Composer, + AliasedComposerBuilder, + BaseTableManager; export 'src/utils/lazy_database.dart'; /// A [ListEquality] instance used by generated drift code for the `==` and diff --git a/drift_dev/lib/src/writer/database_writer.dart b/drift_dev/lib/src/writer/database_writer.dart index ed4fdbf1..e93738e5 100644 --- a/drift_dev/lib/src/writer/database_writer.dart +++ b/drift_dev/lib/src/writer/database_writer.dart @@ -39,20 +39,22 @@ class DatabaseWriter { void write() { final elements = input.resolvedAccessor.availableElements; - final shortcutWriter = ManagerWriter(scope.child(), dbClassName); + final managerWriter = ManagerWriter(scope.child(), dbClassName); // Write data classes, companions and info classes if (!scope.generationOptions.isModular) { for (final reference in elements) { if (reference is DriftTable) { TableWriter(reference, scope.child()).writeInto(); - shortcutWriter.addTable(reference); + managerWriter.addTable(reference); } else if (reference is DriftView) { ViewWriter(reference, scope.child(), this).write(); } } } + managerWriter.write(); + // Write the database class final dbScope = scope.child(); @@ -151,6 +153,8 @@ class DatabaseWriter { } } + firstLeaf.writeln(managerWriter.managerGetter); + // Write implementation for query methods for (final query in input.availableRegularQueries) { QueryWriter(dbScope.child()).write(query); diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index b76a91c8..379e5cc3 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -1,5 +1,4 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first -import 'package:drift/drift.dart'; import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/writer.dart'; import 'package:recase/recase.dart'; @@ -11,20 +10,21 @@ extension on DriftColumn { } class ManagerWriter { - final Scope scope; - final String dbClassName; + final Scope _scope; + final String _dbClassName; + final List _addedTables; - ManagerWriter(this.scope, this.dbClassName); + ManagerWriter(this._scope, this._dbClassName) : _addedTables = []; - void addTable(DriftTable table) { - final leaf = scope.leaf(); + void _writeTableManagers(DriftTable table) { + final leaf = _scope.leaf(); final filters = StringBuffer(); final orderings = StringBuffer(); for (var col in table.columns) { final getterName = - ReCase(col.nameInDart + (col.isForeignKey ? " id" : " ")).camelCase; + (col.nameInDart + (col.isForeignKey ? " id" : " ")).camelCase; filters.writeln( "ColumnFilters get $getterName => ColumnFilters(state.table.${col.nameInDart});"); @@ -46,7 +46,7 @@ class ManagerWriter { ComposableFilter ${col.nameInDart}OrderBy( ComposableFilter Function(${referencedEntityInfoName}FilterComposer) f) { return referenced( - referencedTable: state.db.${referencedTableGetter}, + referencedTable: state.db.$referencedTableGetter, getCurrentColumn: (p0) => p0.${col.nameInDart}, getReferencedColumn: (p0) => p0.${referencedCol.nameInDart}, getReferencedQueryComposer: (data) => @@ -61,7 +61,7 @@ ComposableFilter ${col.nameInDart}OrderBy( ComposableOrdering ${col.nameInDart}OrderBy( ComposableOrdering Function(${referencedEntityInfoName}OrderingComposer) f) { return referenced( - referencedTable: state.db.${referencedTableGetter}, + referencedTable: state.db.$referencedTableGetter, getCurrentColumn: (p0) => p0.${col.nameInDart}, getReferencedColumn: (p0) => p0.${referencedCol.nameInDart}, getReferencedQueryComposer: (data) => @@ -78,26 +78,26 @@ ComposableOrdering ${col.nameInDart}OrderBy( leaf.write(""" -class ${table.entityInfoName}FilterComposer extends FilterComposer<$dbClassName, ${table.entityInfoName}> { +class ${table.entityInfoName}FilterComposer extends FilterComposer<$_dbClassName, ${table.entityInfoName}> { ${table.entityInfoName}FilterComposer.empty(super.db, super.table) : super.empty(); ${table.entityInfoName}FilterComposer.withAliasedTable(super.data) : super.withAliasedTable(); $filters } -class ${table.entityInfoName}OrderingComposer extends OrderingComposer<$dbClassName, ${table.entityInfoName}> { +class ${table.entityInfoName}OrderingComposer extends OrderingComposer<$_dbClassName, ${table.entityInfoName}> { ${table.entityInfoName}OrderingComposer.empty(super.db, super.table) : super.empty(); ${table.entityInfoName}OrderingComposer.withAliasedTable(super.data) : super.withAliasedTable(); $orderings } -class ${table.entityInfoName}ProcessedTableManager extends ProcessedTableManager<$dbClassName, +class ${table.entityInfoName}ProcessedTableManager extends ProcessedTableManager<$_dbClassName, ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { ${table.entityInfoName}ProcessedTableManager(super.data); } -class ${table.entityInfoName}ProcessedTableManagerWithFiltering extends ProcessedTableManager<$dbClassName, +class ${table.entityInfoName}ProcessedTableManagerWithFiltering extends ProcessedTableManager<$_dbClassName, ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { ${table.entityInfoName}ProcessedTableManagerWithFiltering(super.data); @@ -110,7 +110,7 @@ class ${table.entityInfoName}ProcessedTableManagerWithFiltering extends Processe } } -class ${table.entityInfoName}ProcessedTableManagerWithOrdering extends ProcessedTableManager<$dbClassName, +class ${table.entityInfoName}ProcessedTableManagerWithOrdering extends ProcessedTableManager<$_dbClassName, ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { ${table.entityInfoName}ProcessedTableManagerWithOrdering(super.data); @@ -123,8 +123,9 @@ class ${table.entityInfoName}ProcessedTableManagerWithOrdering extends Processed } } -class ${table.entityInfoName}TableManager extends ${table.entityInfoName}ProcessedTableManager { - ${table.entityInfoName}TableManager($dbClassName db, ${table.entityInfoName} table) +class ${table.entityInfoName}TableManager extends RootTableManager<$_dbClassName, + ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { + ${table.entityInfoName}TableManager($_dbClassName db, ${table.entityInfoName} table) : super(TableManagerState( db: db, table: table, @@ -147,6 +148,37 @@ class ${table.entityInfoName}TableManager extends ${table.entityInfoName}Process joinBuilders: state.joinBuilders.union(ordering.joinBuilders))); } } +"""); + } + + String get managerGetter { + return '''${_dbClassName}Manager managers => ${_dbClassName}Manager(this);'''; + } + + void addTable(DriftTable table) { + _addedTables.add(table); + } + + void write() { + for (var table in _addedTables) { + _writeTableManagers(table); + } + final leaf = _scope.leaf(); + final tableManagerGetters = StringBuffer(); + + for (var table in _addedTables) { + tableManagerGetters.writeln( + "${table.entityInfoName}TableManager get ${table.dbGetterName} => ${table.entityInfoName}TableManager(_db, _db.${table.dbGetterName});"); + } + + leaf.write(""" +class ${_dbClassName}Manager { + final $_dbClassName _db; + + ${_dbClassName}Manager(this._db); + + $tableManagerGetters +} """); } } From 83b04e7073ff0dfddb4a1e41c497abf43b2a936b Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Fri, 29 Mar 2024 11:44:56 -0400 Subject: [PATCH 04/74] order typo --- drift_dev/lib/src/writer/manager.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 379e5cc3..3c282df0 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -43,7 +43,7 @@ class ManagerWriter { if (referencedTableGetter != null) { filters.write(''' -ComposableFilter ${col.nameInDart}OrderBy( +ComposableFilter ${col.nameInDart}Filter( ComposableFilter Function(${referencedEntityInfoName}FilterComposer) f) { return referenced( referencedTable: state.db.$referencedTableGetter, @@ -152,7 +152,7 @@ class ${table.entityInfoName}TableManager extends RootTableManager<$_dbClassName } String get managerGetter { - return '''${_dbClassName}Manager managers => ${_dbClassName}Manager(this);'''; + return '''${_dbClassName}Manager get managers => ${_dbClassName}Manager(this);'''; } void addTable(DriftTable table) { From c22935cd7ad55681603c7a1a16cbda908fce06b8 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Fri, 29 Mar 2024 11:55:18 -0400 Subject: [PATCH 05/74] add filters for custom objects --- drift/lib/src/runtime/manager/filter.dart | 30 ++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 91b2f89c..602e8d6c 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -1,7 +1,7 @@ part of 'manager.dart'; /// Defines a class which is used to wrap a column to only expose filter functions -class ColumnFilters { +class ColumnFilters> { /// This class is a wrapper on top of the generated column class /// /// It's used to expose filter functions for a column type @@ -16,7 +16,7 @@ class ColumnFilters { ColumnFilters(this.column); /// Column that this [ColumnFilters] wraps - GeneratedColumn column; + C column; /// Create a filter that checks if the column is null. ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); @@ -28,13 +28,13 @@ class ColumnFilters { ComposableFilter equals(T value) => ComposableFilter.simple(column.equals(value)); - /// Create a filter that checks if the column equals a value. + /// Shortcut for [equals] ComposableFilter call(T value) => ComposableFilter.simple(column.equals(value)); } /// Built in filters for int/double columns -extension NumFilters on ColumnFilters { +extension NumFilters on ColumnFilters> { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); @@ -61,7 +61,8 @@ extension NumFilters on ColumnFilters { } /// Built in filters for BigInt columns -extension BigIntFilters on ColumnFilters { +extension BigIntFilters + on ColumnFilters> { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); @@ -88,7 +89,8 @@ extension BigIntFilters on ColumnFilters { } /// Built in filters for String columns -extension DateFilters on ColumnFilters { +extension DateFilters + on ColumnFilters> { /// Create a filter to check if the column is after a [DateTime] ComposableFilter isAfter(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); @@ -115,6 +117,22 @@ extension DateFilters on ColumnFilters { isBetween(lower, higher)._reversed(); } +extension CustomFilters> + on ColumnFilters { + /// Create a filter that checks if the column equals the columns custom type + ComposableFilter equals(CUSTOM value) => + ComposableFilter.simple(column.equalsValue(value)); + + /// Create a filter that checks if the column equals the value of the columns custom type + ComposableFilter equalsValue(T value) => + ComposableFilter.simple(column.equals(value)); + + /// Shortcut for [equals] + ComposableFilter call(CUSTOM value) => + ComposableFilter.simple(column.equalsValue(value)); +} + /// Defines a class that can be used to compose filters for a column class ComposableFilter implements HasJoinBuilders { @override From 04c07a980a77ef8e22ca523dc42d33b823e00c7e Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Fri, 29 Mar 2024 17:31:26 -0400 Subject: [PATCH 06/74] fix names --- drift/lib/src/runtime/manager/filter.dart | 51 ++++++++++++++++------- drift_dev/lib/src/writer/manager.dart | 39 +++++++++++------ 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 602e8d6c..d06dbde7 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -1,7 +1,7 @@ part of 'manager.dart'; /// Defines a class which is used to wrap a column to only expose filter functions -class ColumnFilters> { +class ColumnFilters { /// This class is a wrapper on top of the generated column class /// /// It's used to expose filter functions for a column type @@ -16,7 +16,7 @@ class ColumnFilters> { ColumnFilters(this.column); /// Column that this [ColumnFilters] wraps - C column; + GeneratedColumn column; /// Create a filter that checks if the column is null. ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); @@ -34,7 +34,7 @@ class ColumnFilters> { } /// Built in filters for int/double columns -extension NumFilters on ColumnFilters> { +extension NumFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); @@ -61,8 +61,7 @@ extension NumFilters on ColumnFilters> { } /// Built in filters for BigInt columns -extension BigIntFilters - on ColumnFilters> { +extension BigIntFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); @@ -89,8 +88,7 @@ extension BigIntFilters } /// Built in filters for String columns -extension DateFilters - on ColumnFilters> { +extension DateFilters on ColumnFilters { /// Create a filter to check if the column is after a [DateTime] ComposableFilter isAfter(T value) => ComposableFilter.simple(column.isBiggerThanValue(value)); @@ -117,22 +115,45 @@ extension DateFilters isBetween(lower, higher)._reversed(); } -extension CustomFilters> - on ColumnFilters { - /// Create a filter that checks if the column equals the columns custom type +/// Defines a class which is used to wrap a column with a type converter to only expose filter functions +class ColumnWithTypeConverterFilters { + /// Similar to [ColumnFilters] but for columns with type converters + ColumnWithTypeConverterFilters(this.column); + + /// Column that this [ColumnWithTypeConverterFilters] wraps + GeneratedColumnWithTypeConverter column; + + /// Create a filter that checks if the column is null. + ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); + + /// Create a filter that checks if the column is not null. + ComposableFilter isNotNull() => ComposableFilter.simple(column.isNotNull()); + + /// Create a filter that checks if the column equals a value. ComposableFilter equals(CUSTOM value) => ComposableFilter.simple(column.equalsValue(value)); - /// Create a filter that checks if the column equals the value of the columns custom type - ComposableFilter equalsValue(T value) => - ComposableFilter.simple(column.equals(value)); - /// Shortcut for [equals] ComposableFilter call(CUSTOM value) => ComposableFilter.simple(column.equalsValue(value)); } +// extension CustomFilters> +// on ColumnFilters { +// /// Create a filter that checks if the column equals the columns custom type +// ComposableFilter equals(CUSTOM value) => +// ComposableFilter.simple(column.equalsValue(value)); + +// /// Create a filter that checks if the column equals the value of the columns custom type +// ComposableFilter equalsValue(T value) => +// ComposableFilter.simple(column.equals(value)); + +// /// Shortcut for [equals] +// ComposableFilter call(CUSTOM value) => +// ComposableFilter.simple(column.equalsValue(value)); +// } + /// Defines a class that can be used to compose filters for a column class ComposableFilter implements HasJoinBuilders { @override diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 3c282df0..eab3d260 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -26,8 +26,20 @@ class ManagerWriter { final getterName = (col.nameInDart + (col.isForeignKey ? " id" : " ")).camelCase; - filters.writeln( - "ColumnFilters get $getterName => ColumnFilters(state.table.${col.nameInDart});"); + // The type this column stores + final innerColumnType = leaf.dartCode(leaf.innerColumnType(col.sqlType)); + + if (col.typeConverter == null) { + filters.writeln( + "ColumnFilters<$innerColumnType> get $getterName => ColumnFilters(state.table.${col.nameInDart});"); + } else { + filters.writeln( + "ColumnFilters<$innerColumnType> get ${getterName}Value => ColumnFilters(state.table.${col.nameInDart});"); + final mappedType = leaf.dartCode(leaf.writer.dartType(col)); + filters.writeln( + "ColumnWithTypeConverterFilters<$mappedType,$innerColumnType> get $getterName => ColumnWithTypeConverterFilters(state.table.${col.nameInDart});"); + } + orderings.writeln( "ColumnOrderings get $getterName => ColumnOrderings(state.table.${col.nameInDart});"); @@ -40,15 +52,16 @@ class ManagerWriter { if (referencedCol.owner is DriftTable) { final referencedTableGetter = referencedCol.owner.dbGetterName; final referencedEntityInfoName = referencedCol.owner.entityInfoName; + if (referencedTableGetter != null) { filters.write(''' ComposableFilter ${col.nameInDart}Filter( - ComposableFilter Function(${referencedEntityInfoName}FilterComposer) f) { + ComposableFilter Function(${referencedEntityInfoName}FilterComposer f) f) { return referenced( referencedTable: state.db.$referencedTableGetter, - getCurrentColumn: (p0) => p0.${col.nameInDart}, - getReferencedColumn: (p0) => p0.${referencedCol.nameInDart}, + getCurrentColumn: (f) => f.${col.nameInDart}, + getReferencedColumn: (f) => f.${referencedCol.nameInDart}, getReferencedQueryComposer: (data) => ${referencedEntityInfoName}FilterComposer.withAliasedTable(data), builder: f); @@ -59,14 +72,14 @@ ComposableFilter ${col.nameInDart}Filter( orderings.write(''' ComposableOrdering ${col.nameInDart}OrderBy( - ComposableOrdering Function(${referencedEntityInfoName}OrderingComposer) f) { + ComposableOrdering Function(${referencedEntityInfoName}OrderingComposer o) o) { return referenced( referencedTable: state.db.$referencedTableGetter, - getCurrentColumn: (p0) => p0.${col.nameInDart}, - getReferencedColumn: (p0) => p0.${referencedCol.nameInDart}, + getCurrentColumn: (f) => f.${col.nameInDart}, + getReferencedColumn: (f) => f.${referencedCol.nameInDart}, getReferencedQueryComposer: (data) => ${referencedEntityInfoName}OrderingComposer.withAliasedTable(data), - builder: f); + builder: o); } '''); @@ -115,8 +128,8 @@ class ${table.entityInfoName}ProcessedTableManagerWithOrdering extends Processed ${table.entityInfoName}ProcessedTableManagerWithOrdering(super.data); ${table.entityInfoName}ProcessedTableManager orderBy( - ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) order) { - final ordering = order(state.orderingComposer); + ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) o) { + final ordering = o(state.orderingComposer); return ${table.entityInfoName}ProcessedTableManager(state.copyWith( orderingTerms: state.orderingBuilders.union(ordering.orderingBuilders), joinBuilders: state.joinBuilders.union(ordering.joinBuilders))); @@ -141,8 +154,8 @@ class ${table.entityInfoName}TableManager extends RootTableManager<$_dbClassName } ${table.entityInfoName}ProcessedTableManagerWithFiltering orderBy( - ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) order) { - final ordering = order(state.orderingComposer); + ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) o) { + final ordering = o(state.orderingComposer); return ${table.entityInfoName}ProcessedTableManagerWithFiltering(state.copyWith( orderingTerms: state.orderingBuilders.union(ordering.orderingBuilders), joinBuilders: state.joinBuilders.union(ordering.joinBuilders))); From 5875e184582b5ed83e3b078da6cb94373113018e Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 31 Mar 2024 00:19:44 -0400 Subject: [PATCH 07/74] remove join from composer state --- drift/lib/src/runtime/manager/composer.dart | 100 +++-------- drift/lib/src/runtime/manager/filter.dart | 17 +- drift/lib/src/runtime/manager/join.dart | 7 +- drift/lib/src/runtime/manager/manager.dart | 142 +++++++++++----- drift/lib/src/runtime/manager/ordering.dart | 16 +- drift_dev/lib/src/writer/manager.dart | 173 +++++++++++--------- 6 files changed, 242 insertions(+), 213 deletions(-) diff --git a/drift/lib/src/runtime/manager/composer.dart b/drift/lib/src/runtime/manager/composer.dart index 55a8f7b3..18e81ba8 100644 --- a/drift/lib/src/runtime/manager/composer.dart +++ b/drift/lib/src/runtime/manager/composer.dart @@ -3,73 +3,40 @@ part of 'manager.dart'; /// A class that holds the state for a query composer @internal -class ComposerState - implements HasJoinBuilders { +class ComposerState { /// The database that the query will be executed on final DB db; /// The table that the query will be executed on final T table; - - @override - final Set joinBuilders; - @override - void addJoinBuilder(JoinBuilder builder) { - joinBuilders.add(builder); - } - - /// Get a random alias for a table - String _getRandomAlias(TableInfo table) { - var aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}'; - while (joinBuilders.map((e) => (e.aliasedName)).contains(aliasName)) { - aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}'; - continue; - } - return aliasName; - } - - /// Create a new query composer state - ComposerState._(this.db, this.table, Set? joinBuilders) - : joinBuilders = joinBuilders ?? {}; -} - -@internal -class AliasedComposerBuilder { - ComposerState state; - CT aliasedTable; - AliasedComposerBuilder( - this.state, - this.aliasedTable, + ComposerState._( + this.db, + this.table, ); } /// Base class for all composers /// -/// Any class that can be composed using the `&` or `|` operator is called a composable, -/// and must implement the [HasJoinBuilders] interface. [ComposableFilter] and [ComposableOrdering] are examples of composable classes. +/// Any class that can be composed using the `&` or `|` operator is called a composable. +/// [ComposableFilter] and [ComposableOrdering] are examples of composable classes. /// /// The [Composer] class is a top level manager for this operation. /// ```dart /// filter((f) => f.id.equals(1) & f.name.equals('Bob')); /// ``` -/// `f` in this example is a [Composer] object, and `f.id.equals(1) & f.name.equals('Bob')` is a [ComposableFilter] object. +/// `f` in this example is a [Composer] object, and `f.id.equals(1)` returns a [ComposableFilter] object. /// /// The [Composer] class is responsible for creating joins between tables, and passing them down to the composable classes. -/// This is done to ensure that duplicate joins are never created. /// /// The [ComposerState] that is held in this class only holds temporary state, as the final state will be held in the composable classes. -/// E.G. In the example above, the resulting [ComposableFilter] object is returned, and the [FilterComposer] is discarded. +/// E.G. In the example above, only the resulting [ComposableFilter] object is returned, and the [FilterComposer] is discarded. /// @internal sealed class Composer { /// The state of the composer final ComposerState state; - Composer.withAliasedTable(AliasedComposerBuilder data) - : state = ComposerState._( - data.state.db, data.aliasedTable, data.state.joinBuilders); - Composer.empty(DB db, CT table) : state = ComposerState._(db, table, {}); + Composer(DB db, CT table) : state = ComposerState._(db, table); /// Method for create a join between two tables B referenced, @@ -78,53 +45,38 @@ sealed class Composer { required RT referencedTable, required GeneratedColumn Function(RT) getReferencedColumn, required B Function(QC) builder, - required QC Function(AliasedComposerBuilder data) - getReferencedQueryComposer, + required QC Function(DB db, RT table) getReferencedComposer, }) { - // Create an alias of the referenced table - // We will never use `referencedTable` again, only the alias - final aliasedReferencedTable = state.db.alias(referencedTable as TableInfo, - state._getRandomAlias(referencedTable)) as RT; - - // We are a function to get the column of the current table, - // This is so that if the `table` in `state` is an alias, - // The user can't supply a column that does not have an alias - // E.G. db.table.id instead of state.table.id + // The name of the alias will be created using the following logic: + // "currentTableName__currentColumnName__referencedColumnName__referencedTableName" + // This is to ensure that the alias is unique final currentColumn = getCurrentColumn(state.table); - - // We are using a function to get the column of the referenced table - // This is so that the user cant by mistake use a column of a table that is not aliased - final referencedColumn = getReferencedColumn(aliasedReferencedTable); + final tempReferencedColumn = getReferencedColumn(referencedTable); + final aliasName = + '${currentColumn.tableName}__${currentColumn.name}__${tempReferencedColumn.tableName}__${tempReferencedColumn.name}'; + final aliasedReferencedTable = + state.db.alias(referencedTable as TableInfo, aliasName); + final aliasedReferencedColumn = + getReferencedColumn(aliasedReferencedTable as RT); // Create a join builder for the referenced table final joinBuilder = JoinBuilder( currentTable: state.table, - referencedTable: aliasedReferencedTable, currentColumn: currentColumn, - referencedColumn: referencedColumn, + referencedTable: aliasedReferencedTable, + referencedColumn: aliasedReferencedColumn, ); - // Add the join builder to the state - state.addJoinBuilder(joinBuilder); - // Get the query composer for the referenced table, passing in the aliased // table and all the join builders - final referencedQueryComposer = getReferencedQueryComposer( - AliasedComposerBuilder(this.state, aliasedReferencedTable)); + final referencedComposer = + getReferencedComposer(state.db, aliasedReferencedTable); // Run the user provided builder with the referencedQueryComposer // This may return a filter or ordering, but we only enforce that it's // a HasJoinBuilders - final result = builder(referencedQueryComposer); - - // At this point it is possible that the result has created more joins - // that state doesnt have, it is also possible that the result is missing - // the `joinBuilder` we create above. - // We will combine both sets and set it to `state.joinBuilders` and `result.joinBuilders` - for (var joinBuilder in result.joinBuilders.union(state.joinBuilders)) { - state.addJoinBuilder(joinBuilder); - result.addJoinBuilder(joinBuilder); - } + final result = builder(referencedComposer); + result.addJoinBuilders({joinBuilder}); return result; } diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index d06dbde7..6a294352 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -117,7 +117,7 @@ extension DateFilters on ColumnFilters { /// Defines a class which is used to wrap a column with a type converter to only expose filter functions class ColumnWithTypeConverterFilters { - /// Similar to [ColumnFilters] but for columns with type converters + /// Similar to [ColumnFilters] but for columns with type converters\ ColumnWithTypeConverterFilters(this.column); /// Column that this [ColumnWithTypeConverterFilters] wraps @@ -159,8 +159,8 @@ class ComposableFilter implements HasJoinBuilders { @override final Set joinBuilders; @override - void addJoinBuilder(JoinBuilder builder) { - joinBuilders.add(builder); + void addJoinBuilders(Set builders) { + joinBuilders.addAll(builders); } /// The expression that will be applied to the query @@ -204,20 +204,11 @@ class ComposableFilter implements HasJoinBuilders { joinBuilders, ); } - - /// Returns a copy of this filter with the given joins and usedAliases - ComposableFilter copyWithJoins(Set joinBuilders) { - return ComposableFilter.withJoin( - expression, joinBuilders.union(this.joinBuilders)); - } } /// The class that orchestrates the composition of filtering class FilterComposer extends Composer { /// Create a filter composer with an empty state - FilterComposer.empty(super.db, super.table) : super.empty(); - - /// Create a filter composer using another composers state - FilterComposer.withAliasedTable(super.data) : super.withAliasedTable(); + FilterComposer(super.db, super.table); } diff --git a/drift/lib/src/runtime/manager/join.dart b/drift/lib/src/runtime/manager/join.dart index 968030be..3d415ef8 100644 --- a/drift/lib/src/runtime/manager/join.dart +++ b/drift/lib/src/runtime/manager/join.dart @@ -34,12 +34,13 @@ class JoinBuilder { bool operator ==(covariant JoinBuilder other) { if (identical(this, other)) return true; - return other.aliasedName == aliasedName; + return other.currentColumn == currentColumn && + other.referencedColumn == referencedColumn; } @override int get hashCode { - return aliasedName.hashCode; + return currentColumn.hashCode ^ referencedColumn.hashCode; } /// Build a join from this join builder @@ -56,5 +57,5 @@ abstract interface class HasJoinBuilders { Set get joinBuilders; /// Add a join builder to this class - void addJoinBuilder(JoinBuilder builder); + void addJoinBuilders(Set builders); } diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 9d14ae67..bc28dc8d 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -1,4 +1,3 @@ -import 'dart:math'; import 'package:drift/drift.dart'; import 'package:meta/meta.dart'; @@ -53,7 +52,7 @@ class TableManagerState< /// Defines a class which holds the state for a table manager /// It contains the database instance, the table instance, and any filters/orderings that will be applied to the query /// This is held in a seperate class than the [BaseTableManager] so that the state can be passed down from the root manager to the lower level managers - TableManagerState({ + const TableManagerState({ required this.db, required this.table, required this.filteringComposer, @@ -62,10 +61,9 @@ class TableManagerState< this.distinct, this.limit, this.offset, - Set? orderingTerms, - Set? joinBuilders, - }) : orderingBuilders = orderingTerms ?? {}, - joinBuilders = joinBuilders ?? {}; + this.orderingBuilders = const {}, + this.joinBuilders = const {}, + }); /// Copy this state with the given values TableManagerState copyWith({ @@ -73,7 +71,7 @@ class TableManagerState< int? limit, int? offset, Expression? filter, - Set? orderingTerms, + Set? orderingBuilders, Set? joinBuilders, }) { return TableManagerState( @@ -83,7 +81,7 @@ class TableManagerState< orderingComposer: orderingComposer, filter: filter ?? this.filter, joinBuilders: joinBuilders ?? this.joinBuilders, - orderingTerms: orderingTerms ?? this.orderingBuilders, + orderingBuilders: orderingBuilders ?? this.orderingBuilders, distinct: distinct ?? this.distinct, limit: limit ?? this.limit, offset: offset ?? this.offset, @@ -186,42 +184,108 @@ abstract class BaseTableManager< final TableManagerState state; /// Create a new [BaseTableManager] instance - BaseTableManager(this.state); + const BaseTableManager(this.state); } -/// Defines the top level manager for a table -abstract class RootTableManager< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer> - extends BaseTableManager { - /// Top level manager for a table. - /// This class contains the top level manager functions for the table. - RootTableManager(super.state); - Future insert(Insertable item) => - state.db.into(state._tableAsTableInfo).insert(item); - Future insertAllBatch(List> items) => state.db - .batch((batch) => batch.insertAll(state._tableAsTableInfo, items)); - Future> getAll() => state.buildSelectStatement().get(); - Stream> watchAll() => state.buildSelectStatement().watch(); - // Future deleteAll() => state.buildDeleteStatement().go(); -} - -/// Defines a manager for a table that can be used to build queries -abstract class ProcessedTableManager< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer> - extends BaseTableManager { - /// A table manager which uses it's internal state to build queries - ProcessedTableManager(super.state); +/// Mixin that adds processed functions to a table manager +/// Is used to act on a table manager that has filtering/ordering applied +mixin ProcessedTableManagerMixin< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer> on BaseTableManager { Future getSingle() => state.buildSelectStatement().getSingle(); Stream watchSingle() => state.buildSelectStatement().watchSingle(); Future> get() => state.buildSelectStatement().get(); Stream> watch() => state.buildSelectStatement().watch(); // Future delete() => state.buildDeleteStatement().go(); } + +/// A table manager that only has functions to return items based on the state build by parent managers +class ProcessedTableManager< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer> + extends BaseTableManager + with ProcessedTableManagerMixin { + /// A table manager that only has functions to return items based on the state build by parent managers + ProcessedTableManager(super.state); +} + +/// A table manager that has methods to filter the query +class TableManagerWithFiltering< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager> + extends BaseTableManager + with ProcessedTableManagerMixin { + final C Function(TableManagerState) getChildManager; + const TableManagerWithFiltering(super.state, {required this.getChildManager}); + C filter(ComposableFilter Function(FilterComposer f) f) { + final filter = f(state.filteringComposer); + return getChildManager(state.copyWith( + filter: filter.expression, + joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + } +} + +/// A table manager that has methods to order the query +class TableManagerWithOrdering< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager> + extends BaseTableManager + with ProcessedTableManagerMixin { + final C Function(TableManagerState) getChildManager; + const TableManagerWithOrdering(super.state, {required this.getChildManager}); + C orderBy(ComposableOrdering Function(OrderingComposer o) o) { + final orderings = o(state.orderingComposer); + return getChildManager(state.copyWith( + orderingBuilders: orderings.orderingBuilders, + joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); + } +} + +/// A table manager with top level function for creating, reading, updating, and deleting items +class RootTableManager< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager, + CF extends TableManagerWithFiltering, + CO extends TableManagerWithOrdering> + extends BaseTableManager + with ProcessedTableManagerMixin { + final CF Function(TableManagerState) + getChildManagerWithFiltering; + final CO Function(TableManagerState) + getChildManagerWithOrdering; + + const RootTableManager(super.state, + {required this.getChildManagerWithFiltering, + required this.getChildManagerWithOrdering}); + CF filter(ComposableFilter Function(FilterComposer f) f) { + final filter = f(state.filteringComposer); + return getChildManagerWithFiltering(state.copyWith( + filter: filter.expression, + joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + } + + CO orderBy(ComposableOrdering Function(OrderingComposer o) o) { + final orderings = o(state.orderingComposer); + return getChildManagerWithOrdering(state.copyWith( + orderingBuilders: orderings.orderingBuilders, + joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); + } +} diff --git a/drift/lib/src/runtime/manager/ordering.dart b/drift/lib/src/runtime/manager/ordering.dart index 84f52ccd..7bd0893e 100644 --- a/drift/lib/src/runtime/manager/ordering.dart +++ b/drift/lib/src/runtime/manager/ordering.dart @@ -66,20 +66,19 @@ class ComposableOrdering implements HasJoinBuilders { @override final Set joinBuilders; @override - void addJoinBuilder(JoinBuilder builder) { - joinBuilders.add(builder); + void addJoinBuilders(Set builders) { + joinBuilders.addAll(builders); } /// Create a new [ComposableOrdering] for a column without any joins ComposableOrdering.simple(this.orderingBuilders) : joinBuilders = {}; /// Create a new [ComposableOrdering] for a column with joins - ComposableOrdering.withJoin(this.orderingBuilders, this.joinBuilders); + ComposableOrdering._(this.orderingBuilders, this.joinBuilders); /// Combine two orderings with THEN ComposableOrdering operator &(ComposableOrdering other) { - return ComposableOrdering.withJoin( - orderingBuilders.union(other.orderingBuilders), + return ComposableOrdering._(orderingBuilders.union(other.orderingBuilders), joinBuilders.union(other.joinBuilders)); } @@ -90,13 +89,8 @@ class ComposableOrdering implements HasJoinBuilders { } /// The class that orchestrates the composition of orderings -/// -/// class OrderingComposer extends Composer { /// Create an ordering composer with an empty state - OrderingComposer.empty(super.db, super.table) : super.empty(); - - /// Create an ordering composer using another composers state - OrderingComposer.withAliasedTable(super.data) : super.withAliasedTable(); + OrderingComposer(super.db, super.table); } diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index eab3d260..c11911f4 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -9,6 +9,23 @@ extension on DriftColumn { } } +class Names { + final String filterComposer; + final String orderingComposer; + final String processedTableManager; + final String tableManagerWithFiltering; + final String tableManagerWithOrdering; + final String rootTableManager; + + Names(String name) + : filterComposer = '${name}FilterComposer', + orderingComposer = '${name}OrderingComposer', + processedTableManager = '${name}ProcessedTableManager', + tableManagerWithFiltering = '${name}TableManagerWithFiltering', + tableManagerWithOrdering = '${name}ProcessedTableManagerWithOrdering', + rootTableManager = '${name}TableManager'; +} + class ManagerWriter { final Scope _scope; final String _dbClassName; @@ -28,16 +45,13 @@ class ManagerWriter { // The type this column stores final innerColumnType = leaf.dartCode(leaf.innerColumnType(col.sqlType)); + filters.writeln( + "ColumnFilters<$innerColumnType> get $getterName => ColumnFilters(state.table.${col.nameInDart});"); - if (col.typeConverter == null) { - filters.writeln( - "ColumnFilters<$innerColumnType> get $getterName => ColumnFilters(state.table.${col.nameInDart});"); - } else { - filters.writeln( - "ColumnFilters<$innerColumnType> get ${getterName}Value => ColumnFilters(state.table.${col.nameInDart});"); + if (col.typeConverter != null) { final mappedType = leaf.dartCode(leaf.writer.dartType(col)); filters.writeln( - "ColumnWithTypeConverterFilters<$mappedType,$innerColumnType> get $getterName => ColumnWithTypeConverterFilters(state.table.${col.nameInDart});"); + "ColumnWithTypeConverterFilters<$mappedType,$innerColumnType> get ${getterName}Ref => ColumnWithTypeConverterFilters(state.table.${col.nameInDart});"); } orderings.writeln( @@ -51,37 +65,32 @@ class ManagerWriter { if (referencedCol != null) { if (referencedCol.owner is DriftTable) { final referencedTableGetter = referencedCol.owner.dbGetterName; - final referencedEntityInfoName = referencedCol.owner.entityInfoName; + final referencedTableName = + Names(referencedCol.owner.entityInfoName); if (referencedTableGetter != null) { filters.write(''' - ComposableFilter ${col.nameInDart}Filter( - ComposableFilter Function(${referencedEntityInfoName}FilterComposer f) f) { + ComposableFilter Function(${referencedTableName.filterComposer} f) f) { return referenced( referencedTable: state.db.$referencedTableGetter, getCurrentColumn: (f) => f.${col.nameInDart}, getReferencedColumn: (f) => f.${referencedCol.nameInDart}, - getReferencedQueryComposer: (data) => - ${referencedEntityInfoName}FilterComposer.withAliasedTable(data), + getReferencedComposer: (db, table) => ${referencedTableName.filterComposer}(db, table), builder: f); } - '''); orderings.write(''' - ComposableOrdering ${col.nameInDart}OrderBy( - ComposableOrdering Function(${referencedEntityInfoName}OrderingComposer o) o) { + ComposableOrdering Function(${referencedTableName.orderingComposer} o) o) { return referenced( referencedTable: state.db.$referencedTableGetter, getCurrentColumn: (f) => f.${col.nameInDart}, getReferencedColumn: (f) => f.${referencedCol.nameInDart}, - getReferencedQueryComposer: (data) => - ${referencedEntityInfoName}OrderingComposer.withAliasedTable(data), + getReferencedComposer: (db, table) => ${referencedTableName.orderingComposer}(db, table), builder: o); } - '''); } } @@ -89,78 +98,96 @@ ComposableOrdering ${col.nameInDart}OrderBy( } } + final names = Names(table.entityInfoName); + leaf.write(""" -class ${table.entityInfoName}FilterComposer extends FilterComposer<$_dbClassName, ${table.entityInfoName}> { - ${table.entityInfoName}FilterComposer.empty(super.db, super.table) : super.empty(); - ${table.entityInfoName}FilterComposer.withAliasedTable(super.data) : super.withAliasedTable(); + + + + + + +class ${names.filterComposer} + extends FilterComposer<$_dbClassName, ${table.entityInfoName}> { + ${names.filterComposer}(super.db, super.table); $filters + } -class ${table.entityInfoName}OrderingComposer extends OrderingComposer<$_dbClassName, ${table.entityInfoName}> { - ${table.entityInfoName}OrderingComposer.empty(super.db, super.table) : super.empty(); - ${table.entityInfoName}OrderingComposer.withAliasedTable(super.data) : super.withAliasedTable(); +class ${names.orderingComposer} + extends OrderingComposer<$_dbClassName, ${table.entityInfoName}> { + ${names.orderingComposer}(super.db, super.table); $orderings + + } -class ${table.entityInfoName}ProcessedTableManager extends ProcessedTableManager<$_dbClassName, - ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { - ${table.entityInfoName}ProcessedTableManager(super.data); +class ${names.processedTableManager} extends ProcessedTableManager< + $_dbClassName, + ${table.entityInfoName}, + ${table.nameOfRowClass}, + ${names.filterComposer}, + ${names.orderingComposer}> { + ${names.processedTableManager}(super.data); } -class ${table.entityInfoName}ProcessedTableManagerWithFiltering extends ProcessedTableManager<$_dbClassName, - ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { - ${table.entityInfoName}ProcessedTableManagerWithFiltering(super.data); - - ${table.entityInfoName}ProcessedTableManager filter( - ComposableFilter Function(${table.entityInfoName}FilterComposer f) f) { - final filter = f(state.filteringComposer); - return ${table.entityInfoName}ProcessedTableManager(state.copyWith( - filter: filter.expression, - joinBuilders: state.joinBuilders.union(filter.joinBuilders))); - } +class ${names.tableManagerWithFiltering} + extends TableManagerWithFiltering< + $_dbClassName, + ${table.entityInfoName}, + ${table.nameOfRowClass}, + ${names.filterComposer}, + ${names.orderingComposer}, + ${names.processedTableManager}> { + ${names.tableManagerWithFiltering}(super.state, + {required super.getChildManager}); } -class ${table.entityInfoName}ProcessedTableManagerWithOrdering extends ProcessedTableManager<$_dbClassName, - ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { - ${table.entityInfoName}ProcessedTableManagerWithOrdering(super.data); - - ${table.entityInfoName}ProcessedTableManager orderBy( - ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) o) { - final ordering = o(state.orderingComposer); - return ${table.entityInfoName}ProcessedTableManager(state.copyWith( - orderingTerms: state.orderingBuilders.union(ordering.orderingBuilders), - joinBuilders: state.joinBuilders.union(ordering.joinBuilders))); - } +class ${names.tableManagerWithOrdering} + extends TableManagerWithOrdering< + $_dbClassName, + ${table.entityInfoName}, + ${table.nameOfRowClass}, + ${names.filterComposer}, + ${names.orderingComposer}, + ${names.processedTableManager}> { + ${names.tableManagerWithOrdering}(super.state, + {required super.getChildManager}); } -class ${table.entityInfoName}TableManager extends RootTableManager<$_dbClassName, - ${table.entityInfoName}, ${table.nameOfRowClass}, ${table.entityInfoName}FilterComposer, ${table.entityInfoName}OrderingComposer> { - ${table.entityInfoName}TableManager($_dbClassName db, ${table.entityInfoName} table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: ${table.entityInfoName}FilterComposer.empty(db, table), - orderingComposer: ${table.entityInfoName}OrderingComposer.empty(db, table))); - - ${table.entityInfoName}ProcessedTableManagerWithOrdering filter( - ComposableFilter Function(${table.entityInfoName}FilterComposer f) f) { - final filter = f(state.filteringComposer); - return ${table.entityInfoName}ProcessedTableManagerWithOrdering(state.copyWith( - filter: filter.expression, - joinBuilders: state.joinBuilders.union(filter.joinBuilders))); - } - - ${table.entityInfoName}ProcessedTableManagerWithFiltering orderBy( - ComposableOrdering Function(${table.entityInfoName}OrderingComposer o) o) { - final ordering = o(state.orderingComposer); - return ${table.entityInfoName}ProcessedTableManagerWithFiltering(state.copyWith( - orderingTerms: state.orderingBuilders.union(ordering.orderingBuilders), - joinBuilders: state.joinBuilders.union(ordering.joinBuilders))); - } +class ${names.rootTableManager} extends RootTableManager< + $_dbClassName, + ${table.entityInfoName}, + ${table.nameOfRowClass}, + ${names.filterComposer}, + ${names.orderingComposer}, + ${names.processedTableManager}, + ${names.tableManagerWithFiltering}, + ${names.tableManagerWithOrdering}> { + ${names.rootTableManager}($_dbClassName db, ${table.entityInfoName} table) + : super( + TableManagerState( + db: db, + table: table, + filteringComposer: + ${names.filterComposer}(db, table), + orderingComposer: + ${names.orderingComposer}(db, table)), + getChildManagerWithFiltering: (f) => + ${names.tableManagerWithFiltering}( + f, + getChildManager: (f) => + ${names.processedTableManager}(f), + ), + getChildManagerWithOrdering: (f) => + ${names.tableManagerWithOrdering}(f, + getChildManager: (f) => + ${names.processedTableManager}(f))); } + """); } From fbd39a0d0a52b22bd346fb764856f6fcb232e09e Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 31 Mar 2024 01:26:15 -0400 Subject: [PATCH 08/74] Add backref support --- drift/lib/src/runtime/manager/manager.dart | 8 ++--- drift_dev/lib/src/writer/manager.dart | 38 ++++++++++++++++++++-- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index bc28dc8d..c755f6aa 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -227,7 +227,7 @@ class TableManagerWithFiltering< with ProcessedTableManagerMixin { final C Function(TableManagerState) getChildManager; const TableManagerWithFiltering(super.state, {required this.getChildManager}); - C filter(ComposableFilter Function(FilterComposer f) f) { + C filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); return getChildManager(state.copyWith( filter: filter.expression, @@ -247,7 +247,7 @@ class TableManagerWithOrdering< with ProcessedTableManagerMixin { final C Function(TableManagerState) getChildManager; const TableManagerWithOrdering(super.state, {required this.getChildManager}); - C orderBy(ComposableOrdering Function(OrderingComposer o) o) { + C orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); return getChildManager(state.copyWith( orderingBuilders: orderings.orderingBuilders, @@ -275,14 +275,14 @@ class RootTableManager< const RootTableManager(super.state, {required this.getChildManagerWithFiltering, required this.getChildManagerWithOrdering}); - CF filter(ComposableFilter Function(FilterComposer f) f) { + CF filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); return getChildManagerWithFiltering(state.copyWith( filter: filter.expression, joinBuilders: state.joinBuilders.union(filter.joinBuilders))); } - CO orderBy(ComposableOrdering Function(OrderingComposer o) o) { + CO orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); return getChildManagerWithOrdering(state.copyWith( orderingBuilders: orderings.orderingBuilders, diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index c11911f4..077e11ea 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -33,7 +33,7 @@ class ManagerWriter { ManagerWriter(this._scope, this._dbClassName) : _addedTables = []; - void _writeTableManagers(DriftTable table) { + void _writeTableManagers(DriftTable table, List otherTables) { final leaf = _scope.leaf(); final filters = StringBuffer(); @@ -98,6 +98,40 @@ ComposableOrdering ${col.nameInDart}OrderBy( } } + // Any other table who has a reference to this table should have a back ref created for it + for (var otherTable in otherTables) { + for (var otherColumn in otherTable.columns) { + if (otherColumn.isForeignKey) { + final foreignKey = otherColumn.constraints + .whereType() + .firstOrNull; + // Check if this is a foreign key + if (foreignKey == null) { + continue; + } + // Check if the foreign key references this table + final thisColumn = foreignKey.otherColumn; + final thisTable = thisColumn.owner; + + final otherColumnName = Names(otherTable.entityInfoName); + // Check if the foreign key references this table + if (thisTable is DriftTable && + table.schemaName == thisTable.schemaName) { + filters.write(''' +ComposableFilter ${("referenced ${otherTable.dbGetterName} ${otherColumn.nameInDart}").camelCase}( + ComposableFilter Function(${otherColumnName.filterComposer} f) f) { + return referenced( + getCurrentColumn: (f) => f.${thisColumn.nameInDart}, + referencedTable: state.db.${otherTable.dbGetterName}, + getReferencedColumn: (f) => f.${otherColumn.nameInDart}, + getReferencedComposer: (db, table) => ${otherColumnName.filterComposer}(db, table), + builder: f); + } + '''); + } + } + } + } final names = Names(table.entityInfoName); leaf.write(""" @@ -201,7 +235,7 @@ class ${names.rootTableManager} extends RootTableManager< void write() { for (var table in _addedTables) { - _writeTableManagers(table); + _writeTableManagers(table, _addedTables); } final leaf = _scope.leaf(); final tableManagerGetters = StringBuffer(); From 45eb1b992ab59f492c267fd378cdda0009eae040 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 31 Mar 2024 18:23:29 -0400 Subject: [PATCH 09/74] Stuff --- drift/lib/drift.dart | 1 - drift/lib/src/runtime/manager/filter.dart | 60 ++++++++++++++++------ drift/lib/src/runtime/manager/manager.dart | 52 ++++++++++++------- 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/drift/lib/drift.dart b/drift/lib/drift.dart index a9f63b7b..996857fa 100644 --- a/drift/lib/drift.dart +++ b/drift/lib/drift.dart @@ -26,7 +26,6 @@ export 'src/runtime/manager/manager.dart' ComposerState, HasJoinBuilders, Composer, - AliasedComposerBuilder, BaseTableManager; export 'src/utils/lazy_database.dart'; diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 6a294352..990f0ec6 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -16,7 +16,7 @@ class ColumnFilters { ColumnFilters(this.column); /// Column that this [ColumnFilters] wraps - GeneratedColumn column; + Expression column; /// Create a filter that checks if the column is null. ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); @@ -28,9 +28,21 @@ class ColumnFilters { ComposableFilter equals(T value) => ComposableFilter.simple(column.equals(value)); + /// Create a filter that checks if the column is in a list of values. + + ComposableFilter isIn(List values) => + ComposableFilter.simple(column.isIn(values)); + + /// Create a filter that checks if the column is not in a list of values. + ComposableFilter isNotIn(List values) => + ComposableFilter.simple(column.isNotIn(values)); + /// Shortcut for [equals] ComposableFilter call(T value) => ComposableFilter.simple(column.equals(value)); + + /// Nested column for filtering on the count of a column + ColumnFilters get count => ColumnFilters(column.count()); } /// Built in filters for int/double columns @@ -58,6 +70,21 @@ extension NumFilters on ColumnFilters { /// Create a filter to check if the column is not between two values ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._reversed(); + + /// Nested column for filtering on the average of a column + ColumnFilters get avg => ColumnFilters(column.avg()); + + /// Nested column for filtering on the max item of a column + ColumnFilters get max => ColumnFilters(column.max()); + + /// Nested column for filtering on the min item of a column + ColumnFilters get min => ColumnFilters(column.min()); + + /// Nested column for filtering on the sum of a column + ColumnFilters get sum => ColumnFilters(column.sum()); + + /// Nested column for filtering on the total of a column + ColumnFilters get total => ColumnFilters(column.total()); } /// Built in filters for BigInt columns @@ -85,6 +112,21 @@ extension BigIntFilters on ColumnFilters { /// Create a filter to check if the column is not between two values ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._reversed(); + + /// Nested column for filtering on the average of a column + ColumnFilters get avg => ColumnFilters(column.avg()); + + /// Nested column for filtering on the max item of a column + ColumnFilters get max => ColumnFilters(column.max()); + + /// Nested column for filtering on the min item of a column + ColumnFilters get min => ColumnFilters(column.min()); + + /// Nested column for filtering on the sum of a column + ColumnFilters get sum => ColumnFilters(column.sum()); + + /// Nested column for filtering on the total of a column + ColumnFilters get total => ColumnFilters(column.total()); } /// Built in filters for String columns @@ -138,22 +180,6 @@ class ColumnWithTypeConverterFilters { ComposableFilter.simple(column.equalsValue(value)); } -// extension CustomFilters> -// on ColumnFilters { -// /// Create a filter that checks if the column equals the columns custom type -// ComposableFilter equals(CUSTOM value) => -// ComposableFilter.simple(column.equalsValue(value)); - -// /// Create a filter that checks if the column equals the value of the columns custom type -// ComposableFilter equalsValue(T value) => -// ComposableFilter.simple(column.equals(value)); - -// /// Shortcut for [equals] -// ComposableFilter call(CUSTOM value) => -// ComposableFilter.simple(column.equalsValue(value)); -// } - /// Defines a class that can be used to compose filters for a column class ComposableFilter implements HasJoinBuilders { @override diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index c755f6aa..d69b681d 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -149,30 +149,29 @@ class TableManagerState< } } - /// Build a delete statement based on the manager state + // Build a delete statement based on the manager state // DeleteStatement buildDeleteStatement() { - // // Being that drift doesnt support joins on deletes, if there are any joins we will use 2 queries + // // If there are any joins we will have to use a subquery to get the rowIds // final DeleteStatement deleteStatement; - // // Check if there are any joins - // if (_joins.isEmpty) { - // // If there are no joins, we can just use a single delete statement + // if (joinBuilders.isEmpty) { // deleteStatement = db.delete(_tableAsTableInfo); - // if (filter?.expression != null) { - // deleteStatement.where((_) => filter!.expression); + // if (filter != null) { + // deleteStatement.where((_) => filter!); // } // } else { // // If there are joins, we need to use a subquery to get the rowIds - // final selectOnlyRowIdStatement = - // _buildJoinedStatement(onlyWithRowId: true); + // final selectOnlyRowIdStatement = buildSelectStatement(); // deleteStatement = db.delete(_tableAsTableInfo) - // ..where( - // (_) => _tableAsTableInfo.rowId.isInQuery(selectOnlyRowIdStatement)); + // ..where((_) => + // _tableAsTableInfo.rowId.isInQuery(_buildJoinedSelectStatement())); // } // return deleteStatement; // } } -/// Defines the base class for a table manager +/// Base class for all table managers +/// Most of this classes functionality is kept in a seperate [TableManagerState] class +/// This is so that the state can be passed down to lower level managers @internal abstract class BaseTableManager< DB extends GeneratedDatabase, @@ -187,18 +186,32 @@ abstract class BaseTableManager< const BaseTableManager(this.state); } -/// Mixin that adds processed functions to a table manager -/// Is used to act on a table manager that has filtering/ordering applied +/// Mixin for adding select functionality to a table manager mixin ProcessedTableManagerMixin< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer> on BaseTableManager { + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer> + on BaseTableManager + implements + MultiSelectable, + SingleSelectable, + SingleOrNullSelectable { + @override Future getSingle() => state.buildSelectStatement().getSingle(); + @override Stream watchSingle() => state.buildSelectStatement().watchSingle(); + @override Future> get() => state.buildSelectStatement().get(); + @override Stream> watch() => state.buildSelectStatement().watch(); + @override + Future getSingleOrNull() => + state.buildSelectStatement().getSingleOrNull(); + @override + Stream watchSingleOrNull() => + state.buildSelectStatement().watchSingleOrNull(); // Future delete() => state.buildDeleteStatement().go(); } @@ -225,6 +238,7 @@ class TableManagerWithFiltering< C extends ProcessedTableManager> extends BaseTableManager with ProcessedTableManagerMixin { + /// Callback for final C Function(TableManagerState) getChildManager; const TableManagerWithFiltering(super.state, {required this.getChildManager}); C filter(ComposableFilter Function(FS f) f) { From 77a5384b3ab575c23196fa64cece495eca9fa6ad Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 31 Mar 2024 23:43:43 -0400 Subject: [PATCH 10/74] fix naming --- drift/lib/src/runtime/manager/manager.dart | 38 ++++++++++++++-------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index d69b681d..779a25ca 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -239,11 +239,14 @@ class TableManagerWithFiltering< extends BaseTableManager with ProcessedTableManagerMixin { /// Callback for - final C Function(TableManagerState) getChildManager; - const TableManagerWithFiltering(super.state, {required this.getChildManager}); + final C Function(TableManagerState) _getChildManager; + const TableManagerWithFiltering(super.state, + {required C Function(TableManagerState) + getChildManager}) + : _getChildManager = getChildManager; C filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); - return getChildManager(state.copyWith( + return _getChildManager(state.copyWith( filter: filter.expression, joinBuilders: state.joinBuilders.union(filter.joinBuilders))); } @@ -259,11 +262,14 @@ class TableManagerWithOrdering< C extends ProcessedTableManager> extends BaseTableManager with ProcessedTableManagerMixin { - final C Function(TableManagerState) getChildManager; - const TableManagerWithOrdering(super.state, {required this.getChildManager}); + final C Function(TableManagerState) _getChildManager; + const TableManagerWithOrdering(super.state, + {required C Function(TableManagerState) + getChildManager}) + : _getChildManager = getChildManager; C orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); - return getChildManager(state.copyWith( + return _getChildManager(state.copyWith( orderingBuilders: orderings.orderingBuilders, joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); } @@ -282,23 +288,27 @@ class RootTableManager< extends BaseTableManager with ProcessedTableManagerMixin { final CF Function(TableManagerState) - getChildManagerWithFiltering; + _getChildManagerWithFiltering; final CO Function(TableManagerState) - getChildManagerWithOrdering; + _getChildManagerWithOrdering; const RootTableManager(super.state, - {required this.getChildManagerWithFiltering, - required this.getChildManagerWithOrdering}); - CF filter(ComposableFilter Function(FS f) f) { + {required CF Function(TableManagerState) + getChildManagerWithFiltering, + required CO Function(TableManagerState) + getChildManagerWithOrdering}) + : _getChildManagerWithFiltering = getChildManagerWithFiltering, + _getChildManagerWithOrdering = getChildManagerWithOrdering; + CO filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); - return getChildManagerWithFiltering(state.copyWith( + return _getChildManagerWithOrdering(state.copyWith( filter: filter.expression, joinBuilders: state.joinBuilders.union(filter.joinBuilders))); } - CO orderBy(ComposableOrdering Function(OS o) o) { + CF orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); - return getChildManagerWithOrdering(state.copyWith( + return _getChildManagerWithFiltering(state.copyWith( orderingBuilders: orderings.orderingBuilders, joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); } From 21d841de63e603d3702d3f0af42d6c2c702ddc51 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 01:13:43 -0400 Subject: [PATCH 11/74] Refacotr select --- drift/lib/src/runtime/manager/manager.dart | 178 +++++++++++++-------- 1 file changed, 112 insertions(+), 66 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 779a25ca..aaa0961b 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -6,6 +6,23 @@ part 'filter.dart'; part 'join.dart'; part 'ordering.dart'; +sealed class _StatementType { + const _StatementType(); +} + +class _SimpleResult + extends _StatementType { + final SimpleSelectStatement statement; + const _SimpleResult(this.statement); +} + +class _JoinedResult + extends _StatementType { + final JoinedSelectStatement statement; + + const _JoinedResult(this.statement); +} + /// Defines a class that holds the state for a [BaseTableManager] class TableManagerState< DB extends GeneratedDatabase, @@ -92,81 +109,110 @@ class TableManagerState< /// This is needed due to dart's limitations with generics TableInfo get _tableAsTableInfo => table as TableInfo; - /// Builds a joined select statement, should be used when joins are present - /// Will order, filter, and limit the statement using the state - JoinedSelectStatement _buildJoinedSelectStatement() { - // Build the joins + /// Builds a select statement with the given target columns, or all columns if none are provided + _StatementType _buildSelectStatement( + {Iterable? targetColumns, + required bool addJoins, + required bool applyFilters, + required bool applyOrdering, + required bool applyLimit}) { final joins = joinBuilders.map((e) => e.buildJoin()).toList(); - // Create the joined statement - final statement = - db.select(_tableAsTableInfo, distinct: distinct ?? false).join(joins); - - // Apply the expression to the statement - if (filter != null) { - statement.where(filter!); + // If there are no joins and we are returning all columns, we can use a simple select statement + if (targetColumns == null && !addJoins) { + final simpleStatement = + db.select(_tableAsTableInfo, distinct: distinct ?? false); + // Apply the expression to the statement + if (applyFilters && filter != null) { + simpleStatement.where((_) => filter!); + } + // Add orderings + if (applyOrdering) { + simpleStatement.orderBy( + orderingBuilders.map((e) => (_) => e.buildTerm()).toList()); + } + // Set the limit and offset + if (applyLimit && limit != null) { + simpleStatement.limit(limit!, offset: offset); + } + return _SimpleResult(simpleStatement); + } else { + JoinedSelectStatement joinedStatement; + // If we are only selecting specific columns, we can use a selectOnly statement + if (targetColumns != null) { + joinedStatement = + (db.selectOnly(_tableAsTableInfo, distinct: distinct ?? false) + ..addColumns(targetColumns)); + // Add the joins to the statement + if (addJoins) { + joinedStatement = + joinedStatement.join(joins) as JoinedSelectStatement; + } + } else { + joinedStatement = db + .select(_tableAsTableInfo, distinct: distinct ?? false) + .join(joins) as JoinedSelectStatement; + } + // Apply the expression to the statement + if (applyFilters && filter != null) { + joinedStatement.where(filter!); + } + // Add orderings + if (applyOrdering) { + joinedStatement + .orderBy(orderingBuilders.map((e) => e.buildTerm()).toList()); + } + // Set the limit and offset + if (applyLimit && limit != null) { + joinedStatement.limit(limit!, offset: offset); + } + return _JoinedResult(joinedStatement); } - - // Add orderings - statement.orderBy(orderingBuilders.map((e) => e.buildTerm()).toList()); - - // Set the limit and offset - if (limit != null) { - statement.limit(limit!, offset: offset); - } - return statement; - } - - /// Builds a simple select statement, this should be used when there are no joins - /// Will order, filter, and limit the statement using the state - SimpleSelectStatement _buildSimpleSelectStatement() { - // Create the statement - final statement = db.select(_tableAsTableInfo, distinct: distinct ?? false); - - // Apply the expression to the statement - if (filter != null) { - statement.where((_) => filter!); - } - - // Add orderings - statement - .orderBy(orderingBuilders.map((e) => (_) => e.buildTerm()).toList()); - - // Set the limit and offset - if (limit != null) { - statement.limit(limit!, offset: offset); - } - return statement; } /// Build a select statement based on the manager state - Selectable
buildSelectStatement() { - if (joinBuilders.isEmpty) { - return _buildSimpleSelectStatement(); - } else { - return _buildJoinedSelectStatement() - .map((p0) => p0.readTable(_tableAsTableInfo)); - } + Selectable
buildSelectStatement( + {Iterable? targetColumns, + bool addJoins = true, + bool applyFilters = true, + bool applyOrdering = true, + bool applyLimit = true}) { + final result = _buildSelectStatement( + targetColumns: targetColumns, + addJoins: addJoins, + applyFilters: applyFilters, + applyOrdering: applyOrdering, + applyLimit: applyLimit); + return switch (result) { + _SimpleResult() => result.statement, + _JoinedResult() => + result.statement.map((p0) => p0.readTable(_tableAsTableInfo)) + }; } // Build a delete statement based on the manager state - // DeleteStatement buildDeleteStatement() { - // // If there are any joins we will have to use a subquery to get the rowIds - // final DeleteStatement deleteStatement; - // if (joinBuilders.isEmpty) { - // deleteStatement = db.delete(_tableAsTableInfo); - // if (filter != null) { - // deleteStatement.where((_) => filter!); - // } - // } else { - // // If there are joins, we need to use a subquery to get the rowIds - // final selectOnlyRowIdStatement = buildSelectStatement(); - // deleteStatement = db.delete(_tableAsTableInfo) - // ..where((_) => - // _tableAsTableInfo.rowId.isInQuery(_buildJoinedSelectStatement())); - // } - // return deleteStatement; - // } + DeleteStatement buildDeleteStatement() { + // If there are any joins we will have to use a subquery to get the rowIds + final DeleteStatement deleteStatement; + if (joinBuilders.isEmpty) { + deleteStatement = db.delete(_tableAsTableInfo); + if (filter != null) { + deleteStatement.where((_) => filter!); + } + } else { + deleteStatement = db.delete(_tableAsTableInfo); + for (var col in _tableAsTableInfo.primaryKey) { + final subquery = _buildSelectStatement( + targetColumns: [col], + addJoins: true, + applyFilters: true, + applyOrdering: false, + applyLimit: false) as _JoinedResult; + deleteStatement.where((tbl) => col.isInQuery(subquery.statement)); + } + } + return deleteStatement; + } } /// Base class for all table managers From 76fea2ac280e31bd867182c3f9e221c66f879307 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 01:20:36 -0400 Subject: [PATCH 12/74] Add delete --- drift/lib/src/runtime/manager/manager.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index aaa0961b..78b61508 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -230,6 +230,7 @@ abstract class BaseTableManager< /// Create a new [BaseTableManager] instance const BaseTableManager(this.state); + Future delete() => state.buildDeleteStatement().go(); } /// Mixin for adding select functionality to a table manager @@ -258,7 +259,7 @@ mixin ProcessedTableManagerMixin< @override Stream watchSingleOrNull() => state.buildSelectStatement().watchSingleOrNull(); - // Future delete() => state.buildDeleteStatement().go(); + Future delete() => state.buildDeleteStatement().go(); } /// A table manager that only has functions to return items based on the state build by parent managers From c7f310e46f8a4414927fbab0fa378f4f41f3b079 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 01:42:21 -0400 Subject: [PATCH 13/74] Refactor Generation --- drift/lib/src/runtime/manager/manager.dart | 2 +- drift_dev/lib/src/writer/manager.dart | 118 +++++++++------------ 2 files changed, 52 insertions(+), 68 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 78b61508..8c8c8f2c 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -272,7 +272,7 @@ class ProcessedTableManager< extends BaseTableManager with ProcessedTableManagerMixin { /// A table manager that only has functions to return items based on the state build by parent managers - ProcessedTableManager(super.state); + const ProcessedTableManager(super.state); } /// A table manager that has methods to filter the query diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 077e11ea..324f0c3a 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -22,7 +22,7 @@ class Names { orderingComposer = '${name}OrderingComposer', processedTableManager = '${name}ProcessedTableManager', tableManagerWithFiltering = '${name}TableManagerWithFiltering', - tableManagerWithOrdering = '${name}ProcessedTableManagerWithOrdering', + tableManagerWithOrdering = '${name}TableManagerWithOrdering', rootTableManager = '${name}TableManager'; } @@ -33,6 +33,54 @@ class ManagerWriter { ManagerWriter(this._scope, this._dbClassName) : _addedTables = []; + void writeManagers(TextEmitter leaf, DriftTable table) { + final names = Names(table.entityInfoName); + + // Write the ProcessedTableManager + leaf + ..write('class ${names.processedTableManager} extends ') + ..writeDriftRef('ProcessedTableManager') + ..writeln( + '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer}> {') + ..writeln('const ${names.processedTableManager}(super.state);') + ..writeln('}'); + + // Write the TableManagerWithFiltering + leaf + ..write('class ${names.tableManagerWithFiltering} extends ') + ..writeDriftRef('TableManagerWithFiltering') + ..writeln( + '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer},${names.processedTableManager}> {') + ..writeln( + 'const ${names.tableManagerWithFiltering}(super.state,{required super.getChildManager});') + ..writeln('}'); + + // Write the TableManagerWithOrdering + leaf + ..write('class ${names.tableManagerWithOrdering} extends ') + ..writeDriftRef('TableManagerWithOrdering') + ..writeln( + '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer},${names.processedTableManager}> {') + ..writeln( + 'const ${names.tableManagerWithOrdering}(super.state,{required super.getChildManager});') + ..writeln('}'); + // Write the Root Table Manager + leaf + ..write('class ${names.rootTableManager} extends ') + ..writeDriftRef('RootTableManager') + ..writeln( + '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer},${names.processedTableManager},${names.tableManagerWithFiltering},${names.tableManagerWithOrdering}> {') + ..writeln( + '${names.rootTableManager}($_dbClassName db, ${table.entityInfoName} table)') + ..writeln(": super(") + ..writeDriftRef("TableManagerState") + ..write( + """(db: db, table: table, filteringComposer:${names.filterComposer}(db, table),orderingComposer:${names.orderingComposer}(db, table)), + getChildManagerWithFiltering: (f) => ${names.tableManagerWithFiltering}(f,getChildManager: (f) => ${names.processedTableManager}(f)), + getChildManagerWithOrdering: (f) => ${names.tableManagerWithOrdering}(f,getChildManager: (f) =>${names.processedTableManager}(f)));""") + ..writeln('}'); + } + void _writeTableManagers(DriftTable table, List otherTables) { final leaf = _scope.leaf(); @@ -157,72 +205,8 @@ class ${names.orderingComposer} $orderings -} - -class ${names.processedTableManager} extends ProcessedTableManager< - $_dbClassName, - ${table.entityInfoName}, - ${table.nameOfRowClass}, - ${names.filterComposer}, - ${names.orderingComposer}> { - ${names.processedTableManager}(super.data); -} - -class ${names.tableManagerWithFiltering} - extends TableManagerWithFiltering< - $_dbClassName, - ${table.entityInfoName}, - ${table.nameOfRowClass}, - ${names.filterComposer}, - ${names.orderingComposer}, - ${names.processedTableManager}> { - ${names.tableManagerWithFiltering}(super.state, - {required super.getChildManager}); -} - -class ${names.tableManagerWithOrdering} - extends TableManagerWithOrdering< - $_dbClassName, - ${table.entityInfoName}, - ${table.nameOfRowClass}, - ${names.filterComposer}, - ${names.orderingComposer}, - ${names.processedTableManager}> { - ${names.tableManagerWithOrdering}(super.state, - {required super.getChildManager}); -} - -class ${names.rootTableManager} extends RootTableManager< - $_dbClassName, - ${table.entityInfoName}, - ${table.nameOfRowClass}, - ${names.filterComposer}, - ${names.orderingComposer}, - ${names.processedTableManager}, - ${names.tableManagerWithFiltering}, - ${names.tableManagerWithOrdering}> { - ${names.rootTableManager}($_dbClassName db, ${table.entityInfoName} table) - : super( - TableManagerState( - db: db, - table: table, - filteringComposer: - ${names.filterComposer}(db, table), - orderingComposer: - ${names.orderingComposer}(db, table)), - getChildManagerWithFiltering: (f) => - ${names.tableManagerWithFiltering}( - f, - getChildManager: (f) => - ${names.processedTableManager}(f), - ), - getChildManagerWithOrdering: (f) => - ${names.tableManagerWithOrdering}(f, - getChildManager: (f) => - ${names.processedTableManager}(f))); -} - -"""); +}"""); + writeManagers(leaf, table); } String get managerGetter { From 2d470dc660a21f0cd4cc039ece6d0e1c64e7a4b2 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 10:18:51 -0400 Subject: [PATCH 14/74] Refoactor Builder --- drift_dev/lib/src/writer/manager.dart | 534 +++++++++++++++++--------- 1 file changed, 342 insertions(+), 192 deletions(-) diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 324f0c3a..d12ad18d 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -9,21 +9,341 @@ extension on DriftColumn { } } -class Names { +abstract class _Filter { + void writeFilter(TextEmitter leaf); +} + +class _RegularFilter extends _Filter { + final String fieldGetter; + final String type; + final String filterName; + _RegularFilter(this.fieldGetter, this.type, this.filterName); + + @override + void writeFilter(TextEmitter leaf) { + leaf + ..writeDriftRef("ColumnFilters") + ..write("<$type> get $filterName =>") + ..writeDriftRef("ColumnFilters") + ..write("(state.table.$fieldGetter);"); + } +} + +class _FilterWithConverter extends _Filter { + final String fieldGetter; + final String type; + final String filterName; + final String converterType; + _FilterWithConverter( + this.fieldGetter, this.type, this.filterName, this.converterType); + + @override + void writeFilter(TextEmitter leaf) { + leaf + ..writeDriftRef("ColumnWithTypeConverterFilters") + ..write("<$converterType,$type> get $filterName =>") + ..writeDriftRef("ColumnWithTypeConverterFilters") + ..writeln("(state.table.$fieldGetter);"); + leaf + ..writeDriftRef("ColumnFilters") + ..write("<$type> get ${filterName}Value =>") + ..writeDriftRef("ColumnFilters") + ..writeln("(state.table.$fieldGetter);"); + } +} + +class _ReferencedFilter extends _Filter { + final String fieldGetter; + final String filterName; + final _TableNames referencedTable; + final _ColumnNames referencedColumn; + _ReferencedFilter(this.fieldGetter, this.filterName, this.referencedTable, + this.referencedColumn); + + @override + void writeFilter(TextEmitter leaf) { + leaf + ..writeDriftRef("ComposableFilter") + ..write(" $filterName(") + ..writeDriftRef("ComposableFilter") + ..writeln(" Function( ${referencedTable.filterComposer} f) f) {") + ..writeln(''' +return referenced( + referencedTable: state.db.${referencedTable.tableGetterName}, + getCurrentColumn: (f) => f.$fieldGetter, + getReferencedColumn: (f) => f.${referencedColumn.fieldGetter}, + getReferencedComposer: (db, table) => ${referencedTable.filterComposer}(db, table), + builder: f); + }'''); + } +} + +abstract class _Ordering { + void writeOrdering(TextEmitter leaf); +} + +class _RegularOrdering extends _Ordering { + final String fieldGetter; + final String type; + final String orderingName; + _RegularOrdering(this.fieldGetter, this.type, this.orderingName); + + @override + void writeOrdering(TextEmitter leaf) { + leaf + ..writeDriftRef("ColumnOrderings") + ..write(" get $orderingName =>") + ..writeDriftRef("ColumnOrderings") + ..write("(state.table.$fieldGetter);"); + } +} + +class _ReferencedOrdering extends _Ordering { + final String fieldGetter; + final String orderingName; + final _TableNames referencedTable; + final _ColumnNames referencedColumn; + _ReferencedOrdering(this.fieldGetter, this.orderingName, this.referencedTable, + this.referencedColumn); + + @override + void writeOrdering(TextEmitter leaf) { + leaf + ..writeDriftRef("ComposableOrdering") + ..write(" $orderingName(") + ..writeDriftRef("ComposableOrdering") + ..writeln(" Function( ${referencedTable.orderingComposer} o) o) {") + ..writeln(''' +return referenced( + referencedTable: state.db.${referencedTable.tableGetterName}, + getCurrentColumn: (f) => f.$fieldGetter, + getReferencedColumn: (f) => f.${referencedColumn.fieldGetter}, + getReferencedComposer: (db, table) => ${referencedTable.orderingComposer}(db, table), + builder: o); + }'''); + } +} + +class _ColumnNames { + /// The getter for the field + /// + /// E.G `id` + final String fieldGetter; + final List<_Filter> filters; + final List<_Ordering> orderings; + _ColumnNames(this.fieldGetter, this.filters, this.orderings); +} + +class _TableNames { + /// The current table + final DriftTable table; + + /// The name of the filter composer class + /// + /// E.G `UserFilterComposer` final String filterComposer; + + /// The name of the filter composer class + /// + /// E.G `UserOrderingComposer` final String orderingComposer; + + /// The name of the processed table manager class + /// + /// E.G `UserProcessedTableManager` final String processedTableManager; + + /// The name of the table manager with filtering class + /// + /// E.G `UserTableManagerWithFiltering` final String tableManagerWithFiltering; + + /// The name of the table manager with ordering class + /// + /// E.G `UserTableManagerWithOrdering` final String tableManagerWithOrdering; + + /// The name of the root table manager class + /// + /// E.G `UserTableManager` final String rootTableManager; - Names(String name) - : filterComposer = '${name}FilterComposer', - orderingComposer = '${name}OrderingComposer', - processedTableManager = '${name}ProcessedTableManager', - tableManagerWithFiltering = '${name}TableManagerWithFiltering', - tableManagerWithOrdering = '${name}TableManagerWithOrdering', - rootTableManager = '${name}TableManager'; + /// Name of the table class that will be generated + /// + /// E.G `$CategoriesTable` + final String tableClassName; + + /// Name of the getter for the table + /// + /// E.G `categories` + final String tableGetterName; + + /// Name of the class that cooresponds to a table row + /// + /// E.G `Category` + final String rowClassName; + + /// Columns with their names, filters and orderings + final List<_ColumnNames> columns; + + final List<_ReferencedFilter> backRefFilters; + + _TableNames(this.table) + : filterComposer = '${table.entityInfoName}FilterComposer', + orderingComposer = '${table.entityInfoName}OrderingComposer', + processedTableManager = '${table.entityInfoName}ProcessedTableManager', + tableManagerWithFiltering = + '${table.entityInfoName}TableManagerWithFiltering', + tableManagerWithOrdering = + '${table.entityInfoName}TableManagerWithOrdering', + rootTableManager = '${table.entityInfoName}TableManager', + rowClassName = table.nameOfRowClass, + tableClassName = table.entityInfoName, + tableGetterName = table.dbGetterName, + backRefFilters = [], + columns = []; + + void writeManager(TextEmitter leaf, String dbClassName) { + // Write the FilterComposer + leaf + ..write('class $filterComposer extends ') + ..writeDriftRef('FilterComposer') + ..writeln('<$dbClassName,$tableClassName> {') + ..writeln('$filterComposer(super.db, super.table);'); + for (var c in columns) { + for (var f in c.filters) { + f.writeFilter(leaf); + } + } + for (var f in backRefFilters) { + f.writeFilter(leaf); + } + leaf.writeln('}'); + + // Write the OrderingComposer + leaf + ..write('class $orderingComposer extends ') + ..writeDriftRef('OrderingComposer') + ..writeln('<$dbClassName,$tableClassName> {') + ..writeln('$orderingComposer(super.db, super.table);'); + for (var c in columns) { + for (var o in c.orderings) { + o.writeOrdering(leaf); + } + } + leaf.writeln('}'); + + // Write the ProcessedTableManager + leaf + ..write('class $processedTableManager extends ') + ..writeDriftRef('ProcessedTableManager') + ..writeln( + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer> {') + ..writeln('const $processedTableManager(super.state);') + ..writeln('}'); + + // Write the TableManagerWithFiltering + leaf + ..write('class $tableManagerWithFiltering extends ') + ..writeDriftRef('TableManagerWithFiltering') + ..writeln( + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager> {') + ..writeln( + 'const $tableManagerWithFiltering(super.state,{required super.getChildManager});') + ..writeln('}'); + + // Write the TableManagerWithOrdering + leaf + ..write('class $tableManagerWithOrdering extends ') + ..writeDriftRef('TableManagerWithOrdering') + ..writeln( + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager> {') + ..writeln( + 'const $tableManagerWithOrdering(super.state,{required super.getChildManager});') + ..writeln('}'); + // Write the Root Table Manager + leaf + ..write('class $rootTableManager extends ') + ..writeDriftRef('RootTableManager') + ..writeln( + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$tableManagerWithFiltering,$tableManagerWithOrdering> {') + ..writeln('$rootTableManager($dbClassName db, $tableClassName table)') + ..writeln(": super(") + ..writeDriftRef("TableManagerState") + ..write( + """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table)), + getChildManagerWithFiltering: (f) => $tableManagerWithFiltering(f,getChildManager: (f) => $processedTableManager(f)), + getChildManagerWithOrdering: (f) => $tableManagerWithOrdering(f,getChildManager: (f) =>$processedTableManager(f)));""") + ..writeln('}'); + } + + void addFiltersAndOrderings(List tables, TextEmitter leaf) { + /// First add the filters and orderings for the columns + /// of the current table + for (var column in table.columns) { + final c = _ColumnNames(column.nameInDart, [], []); + final innerColumnType = + leaf.dartCode(leaf.innerColumnType(column.sqlType)); + if (column.typeConverter != null) { + final mappedType = leaf.dartCode(leaf.writer.dartType(column)); + c.filters.add(_FilterWithConverter( + c.fieldGetter, innerColumnType, c.fieldGetter, mappedType)); + } else { + c.filters + .add(_RegularFilter(c.fieldGetter, innerColumnType, c.fieldGetter)); + } + c.orderings + .add(_RegularOrdering(c.fieldGetter, innerColumnType, c.fieldGetter)); + + final referencedCol = column.constraints + .whereType() + .firstOrNull + ?.otherColumn; + final referencedTable = referencedCol?.owner; + if (referencedCol != null && referencedTable is DriftTable) { + final referencedTableNames = _TableNames(referencedTable); + final referencedColumnNames = + _ColumnNames(referencedCol.nameInDart, [], []); + c.filters.add(_ReferencedFilter(c.fieldGetter, "${c.fieldGetter}Ref", + referencedTableNames, referencedColumnNames)); + c.orderings.add(_ReferencedOrdering( + c.fieldGetter, + "${c.fieldGetter}OrderBy", + referencedTableNames, + referencedColumnNames)); + } + columns.add(c); + } + for (var otherTable in tables) { + final otherTableNames = _TableNames(otherTable); + + /// We are adding backrefs now, skip the current table + if (otherTableNames.tableClassName == tableClassName) { + continue; + } + for (var otherColumn in otherTable.columns) { + final referencedCol = otherColumn.constraints + .whereType() + .firstOrNull + ?.otherColumn; + final referencedTable = referencedCol?.owner; + if (referencedCol != null && referencedTable is DriftTable) { + final referencedTableNames = _TableNames(referencedTable); + final referencedColumnNames = + _ColumnNames(referencedCol.nameInDart, [], []); + + // If we are referencing the current table, add a back ref + if (referencedTableNames.tableClassName == tableClassName) { + backRefFilters.add(_ReferencedFilter( + referencedColumnNames.fieldGetter, + "${otherTableNames.tableClassName.camelCase}Refs", + otherTableNames, + referencedColumnNames)); + } + } + } + } + } } class ManagerWriter { @@ -33,207 +353,37 @@ class ManagerWriter { ManagerWriter(this._scope, this._dbClassName) : _addedTables = []; - void writeManagers(TextEmitter leaf, DriftTable table) { - final names = Names(table.entityInfoName); - - // Write the ProcessedTableManager - leaf - ..write('class ${names.processedTableManager} extends ') - ..writeDriftRef('ProcessedTableManager') - ..writeln( - '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer}> {') - ..writeln('const ${names.processedTableManager}(super.state);') - ..writeln('}'); - - // Write the TableManagerWithFiltering - leaf - ..write('class ${names.tableManagerWithFiltering} extends ') - ..writeDriftRef('TableManagerWithFiltering') - ..writeln( - '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer},${names.processedTableManager}> {') - ..writeln( - 'const ${names.tableManagerWithFiltering}(super.state,{required super.getChildManager});') - ..writeln('}'); - - // Write the TableManagerWithOrdering - leaf - ..write('class ${names.tableManagerWithOrdering} extends ') - ..writeDriftRef('TableManagerWithOrdering') - ..writeln( - '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer},${names.processedTableManager}> {') - ..writeln( - 'const ${names.tableManagerWithOrdering}(super.state,{required super.getChildManager});') - ..writeln('}'); - // Write the Root Table Manager - leaf - ..write('class ${names.rootTableManager} extends ') - ..writeDriftRef('RootTableManager') - ..writeln( - '<$_dbClassName,${table.entityInfoName},${table.nameOfRowClass},${names.filterComposer},${names.orderingComposer},${names.processedTableManager},${names.tableManagerWithFiltering},${names.tableManagerWithOrdering}> {') - ..writeln( - '${names.rootTableManager}($_dbClassName db, ${table.entityInfoName} table)') - ..writeln(": super(") - ..writeDriftRef("TableManagerState") - ..write( - """(db: db, table: table, filteringComposer:${names.filterComposer}(db, table),orderingComposer:${names.orderingComposer}(db, table)), - getChildManagerWithFiltering: (f) => ${names.tableManagerWithFiltering}(f,getChildManager: (f) => ${names.processedTableManager}(f)), - getChildManagerWithOrdering: (f) => ${names.tableManagerWithOrdering}(f,getChildManager: (f) =>${names.processedTableManager}(f)));""") - ..writeln('}'); - } - - void _writeTableManagers(DriftTable table, List otherTables) { - final leaf = _scope.leaf(); - - final filters = StringBuffer(); - final orderings = StringBuffer(); - - for (var col in table.columns) { - final getterName = - (col.nameInDart + (col.isForeignKey ? " id" : " ")).camelCase; - - // The type this column stores - final innerColumnType = leaf.dartCode(leaf.innerColumnType(col.sqlType)); - filters.writeln( - "ColumnFilters<$innerColumnType> get $getterName => ColumnFilters(state.table.${col.nameInDart});"); - - if (col.typeConverter != null) { - final mappedType = leaf.dartCode(leaf.writer.dartType(col)); - filters.writeln( - "ColumnWithTypeConverterFilters<$mappedType,$innerColumnType> get ${getterName}Ref => ColumnWithTypeConverterFilters(state.table.${col.nameInDart});"); - } - - orderings.writeln( - "ColumnOrderings get $getterName => ColumnOrderings(state.table.${col.nameInDart});"); - - if (col.isForeignKey) { - final referencedCol = col.constraints - .whereType() - .firstOrNull - ?.otherColumn; - if (referencedCol != null) { - if (referencedCol.owner is DriftTable) { - final referencedTableGetter = referencedCol.owner.dbGetterName; - final referencedTableName = - Names(referencedCol.owner.entityInfoName); - - if (referencedTableGetter != null) { - filters.write(''' -ComposableFilter ${col.nameInDart}Filter( - ComposableFilter Function(${referencedTableName.filterComposer} f) f) { - return referenced( - referencedTable: state.db.$referencedTableGetter, - getCurrentColumn: (f) => f.${col.nameInDart}, - getReferencedColumn: (f) => f.${referencedCol.nameInDart}, - getReferencedComposer: (db, table) => ${referencedTableName.filterComposer}(db, table), - builder: f); - } - '''); - - orderings.write(''' -ComposableOrdering ${col.nameInDart}OrderBy( - ComposableOrdering Function(${referencedTableName.orderingComposer} o) o) { - return referenced( - referencedTable: state.db.$referencedTableGetter, - getCurrentColumn: (f) => f.${col.nameInDart}, - getReferencedColumn: (f) => f.${referencedCol.nameInDart}, - getReferencedComposer: (db, table) => ${referencedTableName.orderingComposer}(db, table), - builder: o); - } - '''); - } - } - } - } - } - - // Any other table who has a reference to this table should have a back ref created for it - for (var otherTable in otherTables) { - for (var otherColumn in otherTable.columns) { - if (otherColumn.isForeignKey) { - final foreignKey = otherColumn.constraints - .whereType() - .firstOrNull; - // Check if this is a foreign key - if (foreignKey == null) { - continue; - } - // Check if the foreign key references this table - final thisColumn = foreignKey.otherColumn; - final thisTable = thisColumn.owner; - - final otherColumnName = Names(otherTable.entityInfoName); - // Check if the foreign key references this table - if (thisTable is DriftTable && - table.schemaName == thisTable.schemaName) { - filters.write(''' -ComposableFilter ${("referenced ${otherTable.dbGetterName} ${otherColumn.nameInDart}").camelCase}( - ComposableFilter Function(${otherColumnName.filterComposer} f) f) { - return referenced( - getCurrentColumn: (f) => f.${thisColumn.nameInDart}, - referencedTable: state.db.${otherTable.dbGetterName}, - getReferencedColumn: (f) => f.${otherColumn.nameInDart}, - getReferencedComposer: (db, table) => ${otherColumnName.filterComposer}(db, table), - builder: f); - } - '''); - } - } - } - } - final names = Names(table.entityInfoName); - - leaf.write(""" - - - - - - - -class ${names.filterComposer} - extends FilterComposer<$_dbClassName, ${table.entityInfoName}> { - ${names.filterComposer}(super.db, super.table); - - $filters - -} - -class ${names.orderingComposer} - extends OrderingComposer<$_dbClassName, ${table.entityInfoName}> { - ${names.orderingComposer}(super.db, super.table); - - $orderings - - -}"""); - writeManagers(leaf, table); - } - String get managerGetter { - return '''${_dbClassName}Manager get managers => ${_dbClassName}Manager(this);'''; + return '''$_dbMangerName get managers => $_dbMangerName(this);'''; } void addTable(DriftTable table) { _addedTables.add(table); } + String get _dbMangerName => '${_dbClassName}Manager'; + void write() { - for (var table in _addedTables) { - _writeTableManagers(table, _addedTables); - } final leaf = _scope.leaf(); + final tableNames = <_TableNames>[]; + for (var table in _addedTables) { + final t = _TableNames(table); + t.addFiltersAndOrderings(_addedTables, leaf); + t.writeManager(leaf, _dbClassName); + tableNames.add(t); + } final tableManagerGetters = StringBuffer(); - for (var table in _addedTables) { + for (var table in tableNames) { tableManagerGetters.writeln( - "${table.entityInfoName}TableManager get ${table.dbGetterName} => ${table.entityInfoName}TableManager(_db, _db.${table.dbGetterName});"); + "${table.rootTableManager} get ${table.tableGetterName} => ${table.rootTableManager}(_db, _db.${table.tableGetterName});"); } leaf.write(""" -class ${_dbClassName}Manager { +class $_dbMangerName{ final $_dbClassName _db; - ${_dbClassName}Manager(this._db); + $_dbMangerName(this._db); $tableManagerGetters } From e4cc10d7c93925bab8acdf43754767cff56e13c9 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 10:36:51 -0400 Subject: [PATCH 15/74] add duplicate checker --- drift_dev/lib/src/writer/manager.dart | 70 ++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index d12ad18d..f6f46c4e 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -1,6 +1,7 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/writer.dart'; +import 'package:path/path.dart'; import 'package:recase/recase.dart'; extension on DriftColumn { @@ -10,14 +11,18 @@ extension on DriftColumn { } abstract class _Filter { + final String filterName; + _Filter( + this.filterName, + ); + void writeFilter(TextEmitter leaf); } class _RegularFilter extends _Filter { final String fieldGetter; final String type; - final String filterName; - _RegularFilter(this.fieldGetter, this.type, this.filterName); + _RegularFilter(this.fieldGetter, this.type, super.filterName); @override void writeFilter(TextEmitter leaf) { @@ -32,10 +37,9 @@ class _RegularFilter extends _Filter { class _FilterWithConverter extends _Filter { final String fieldGetter; final String type; - final String filterName; final String converterType; _FilterWithConverter( - this.fieldGetter, this.type, this.filterName, this.converterType); + this.fieldGetter, this.type, super.filterName, this.converterType); @override void writeFilter(TextEmitter leaf) { @@ -54,10 +58,9 @@ class _FilterWithConverter extends _Filter { class _ReferencedFilter extends _Filter { final String fieldGetter; - final String filterName; final _TableNames referencedTable; final _ColumnNames referencedColumn; - _ReferencedFilter(this.fieldGetter, this.filterName, this.referencedTable, + _ReferencedFilter(this.fieldGetter, super.filterName, this.referencedTable, this.referencedColumn); @override @@ -79,14 +82,17 @@ return referenced( } abstract class _Ordering { + final String orderingName; + + _Ordering(this.orderingName); + void writeOrdering(TextEmitter leaf); } class _RegularOrdering extends _Ordering { final String fieldGetter; final String type; - final String orderingName; - _RegularOrdering(this.fieldGetter, this.type, this.orderingName); + _RegularOrdering(this.fieldGetter, this.type, super.orderingName); @override void writeOrdering(TextEmitter leaf) { @@ -100,11 +106,10 @@ class _RegularOrdering extends _Ordering { class _ReferencedOrdering extends _Ordering { final String fieldGetter; - final String orderingName; final _TableNames referencedTable; final _ColumnNames referencedColumn; - _ReferencedOrdering(this.fieldGetter, this.orderingName, this.referencedTable, - this.referencedColumn); + _ReferencedOrdering(this.fieldGetter, super.orderingName, + this.referencedTable, this.referencedColumn); @override void writeOrdering(TextEmitter leaf) { @@ -343,6 +348,49 @@ class _TableNames { } } } + // If there arey duplicate filters or orderings, remove both of them, use filterName and orderingName as the key + final filterNames = []; + final filtersToRemove = []; + final orderingNames = []; + final orderingsToRemove = []; + for (var c in columns) { + for (var f in c.filters) { + filterNames.add(f.filterName); + } + for (var o in c.orderings) { + orderingNames.add(o.orderingName); + } + } + filterNames.addAll(backRefFilters.map((e) => e.filterName)); + + // Add any filter or ordering that has a duplicate name to the remove list + for (var c in columns) { + for (var f in c.filters) { + if (filterNames.count(f.filterName) > 1) { + filtersToRemove.add(f.filterName); + } + } + for (var o in c.orderings) { + if (orderingNames.count(o.orderingName) > 1) { + orderingsToRemove.add(o.orderingName); + } + } + } + // Remove the filters and orderings that have duplicates + + // TODO: Add warnings for duplicate filters and orderings + for (var c in columns) { + c.filters.removeWhere((e) => filtersToRemove.contains(e.filterName)); + c.orderings + .removeWhere((e) => orderingsToRemove.contains(e.orderingName)); + } + backRefFilters.removeWhere((e) => filtersToRemove.contains(e.filterName)); + } +} + +extension on List { + int count(String element) { + return where((e) => e == element).length; } } From 16db7abf794995e3269b3b634f2b0375f7b52983 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 13:39:17 -0400 Subject: [PATCH 16/74] Add create and bulk create --- drift/lib/src/runtime/manager/manager.dart | 53 ++++++++---- drift_dev/lib/src/writer/manager.dart | 98 +++++++++++++--------- 2 files changed, 96 insertions(+), 55 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 8c8c8f2c..b6405b98 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -323,29 +323,31 @@ class TableManagerWithOrdering< } /// A table manager with top level function for creating, reading, updating, and deleting items -class RootTableManager< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer, - C extends ProcessedTableManager, - CF extends TableManagerWithFiltering, - CO extends TableManagerWithOrdering> - extends BaseTableManager - with ProcessedTableManagerMixin { +abstract class RootTableManager< + DB extends GeneratedDatabase, + T extends TableInfo, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager, + CF extends TableManagerWithFiltering, + CO extends TableManagerWithOrdering, + CI extends Function> extends BaseTableManager { final CF Function(TableManagerState) _getChildManagerWithFiltering; final CO Function(TableManagerState) _getChildManagerWithOrdering; + final CI _createInsertable; - const RootTableManager(super.state, + RootTableManager(super.state, {required CF Function(TableManagerState) getChildManagerWithFiltering, required CO Function(TableManagerState) - getChildManagerWithOrdering}) + getChildManagerWithOrdering, + required CI createInsertable}) : _getChildManagerWithFiltering = getChildManagerWithFiltering, - _getChildManagerWithOrdering = getChildManagerWithOrdering; + _getChildManagerWithOrdering = getChildManagerWithOrdering, + _createInsertable = createInsertable; CO filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); return _getChildManagerWithOrdering(state.copyWith( @@ -353,6 +355,29 @@ class RootTableManager< joinBuilders: state.joinBuilders.union(filter.joinBuilders))); } + CO all() { + return _getChildManagerWithOrdering(state); + } + + Future create( + Insertable Function(CI o) f, + InsertMode? mode, + UpsertClause? onConflict, + ) { + return state.db + .into(state.table) + .insert(f(_createInsertable), mode: mode, onConflict: onConflict); + } + + Future bulkCreate( + Iterable> Function(CI o) f, + InsertMode? mode, + UpsertClause? onConflict, + ) { + return state.db.batch((b) => b.insertAll(state.table, f(_createInsertable), + mode: mode, onConflict: onConflict)); + } + CF orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); return _getChildManagerWithFiltering(state.copyWith( diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index f6f46c4e..4fc4b486 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -1,7 +1,6 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/writer.dart'; -import 'package:path/path.dart'; import 'package:recase/recase.dart'; extension on DriftColumn { @@ -267,18 +266,48 @@ class _TableNames { 'const $tableManagerWithOrdering(super.state,{required super.getChildManager});') ..writeln('}'); // Write the Root Table Manager + + // We need to build a function type which will create insertable items + // We then need to create the actual function + + final companionClassName = leaf.dartCode(leaf.companionType(table)); + final createInsertableFunctionTypeDef = + StringBuffer("$companionClassName Function({"); + final createInsertableFunctionArgs = StringBuffer("({"); + final createInsertableFunctionBody = + StringBuffer("=> $companionClassName.insert("); + + for (final column in table.columns) { + final value = leaf.drift('Value'); + final param = column.nameInDart; + final typeName = leaf.dartCode(leaf.dartType(column)); + createInsertableFunctionBody.write('$param: $param,'); + if (!column.isImplicitRowId && table.isColumnRequiredForInsert(column)) { + createInsertableFunctionTypeDef.write('required $typeName $param,'); + createInsertableFunctionArgs.write('required $typeName $param,'); + } else { + createInsertableFunctionTypeDef.write('$value<$typeName> $param,'); + createInsertableFunctionArgs + .write('$value<$typeName> $param = const $value.absent(),'); + } + } + createInsertableFunctionTypeDef.write('})'); + createInsertableFunctionArgs.write('})'); + createInsertableFunctionBody.write(")"); + leaf ..write('class $rootTableManager extends ') ..writeDriftRef('RootTableManager') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$tableManagerWithFiltering,$tableManagerWithOrdering> {') + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$tableManagerWithFiltering,$tableManagerWithOrdering, $createInsertableFunctionTypeDef> {') ..writeln('$rootTableManager($dbClassName db, $tableClassName table)') ..writeln(": super(") ..writeDriftRef("TableManagerState") ..write( """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table)), getChildManagerWithFiltering: (f) => $tableManagerWithFiltering(f,getChildManager: (f) => $processedTableManager(f)), - getChildManagerWithOrdering: (f) => $tableManagerWithOrdering(f,getChildManager: (f) =>$processedTableManager(f)));""") + getChildManagerWithOrdering: (f) => $tableManagerWithOrdering(f,getChildManager: (f) =>$processedTableManager(f)) + ,createInsertable: $createInsertableFunctionArgs$createInsertableFunctionBody);""") ..writeln('}'); } @@ -348,49 +377,36 @@ class _TableNames { } } } - // If there arey duplicate filters or orderings, remove both of them, use filterName and orderingName as the key - final filterNames = []; - final filtersToRemove = []; - final orderingNames = []; - final orderingsToRemove = []; - for (var c in columns) { - for (var f in c.filters) { - filterNames.add(f.filterName); - } - for (var o in c.orderings) { - orderingNames.add(o.orderingName); - } - } - filterNames.addAll(backRefFilters.map((e) => e.filterName)); - // Add any filter or ordering that has a duplicate name to the remove list - for (var c in columns) { - for (var f in c.filters) { - if (filterNames.count(f.filterName) > 1) { - filtersToRemove.add(f.filterName); - } - } - for (var o in c.orderings) { - if (orderingNames.count(o.orderingName) > 1) { - orderingsToRemove.add(o.orderingName); - } - } - } // Remove the filters and orderings that have duplicates - // TODO: Add warnings for duplicate filters and orderings - for (var c in columns) { - c.filters.removeWhere((e) => filtersToRemove.contains(e.filterName)); - c.orderings - .removeWhere((e) => orderingsToRemove.contains(e.orderingName)); + List duplicates(List items) { + final seen = {}; + final duplicates = []; + for (var item in items) { + if (!seen.add(item)) { + duplicates.add(item); + } + } + return duplicates; } - backRefFilters.removeWhere((e) => filtersToRemove.contains(e.filterName)); - } -} -extension on List { - int count(String element) { - return where((e) => e == element).length; + final filterNamesToRemove = duplicates(columns + .map((e) => e.filters.map((e) => e.filterName)) + .expand((e) => e) + .toList() + + backRefFilters.map((e) => e.filterName).toList()); + final orderingNamesToRemove = duplicates(columns + .map((e) => e.orderings.map((e) => e.orderingName)) + .expand((e) => e) + .toList()); + for (var c in columns) { + c.filters.removeWhere((e) => filterNamesToRemove.contains(e.filterName)); + c.orderings + .removeWhere((e) => orderingNamesToRemove.contains(e.orderingName)); + } + backRefFilters + .removeWhere((e) => filterNamesToRemove.contains(e.filterName)); } } From 42f32e02277bfb13124fea889da0c15ca1b221c3 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 15:50:21 -0400 Subject: [PATCH 17/74] Refactor --- drift/lib/src/runtime/manager/manager.dart | 48 +++++++-------- drift_dev/lib/src/writer/manager.dart | 71 +++++++++++++++------- 2 files changed, 73 insertions(+), 46 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index b6405b98..d5c610a5 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -30,10 +30,10 @@ class TableManagerState< DT extends DataClass, FS extends FilterComposer, OS extends OrderingComposer> { - /// The database that the query will be executed on + /// The database that the query will be exeCCted on final DB db; - /// The table that the query will be executed on + /// The table that the query will be exeCCted on final T table; /// The expression that will be applied to the query @@ -234,17 +234,18 @@ abstract class BaseTableManager< } /// Mixin for adding select functionality to a table manager -mixin ProcessedTableManagerMixin< +abstract class ProcessedTableManager< DB extends GeneratedDatabase, T extends TableInfo, D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer> - on BaseTableManager + extends BaseTableManager implements MultiSelectable, SingleSelectable, SingleOrNullSelectable { + const ProcessedTableManager(super.state); @override Future getSingle() => state.buildSelectStatement().getSingle(); @override @@ -259,33 +260,19 @@ mixin ProcessedTableManagerMixin< @override Stream watchSingleOrNull() => state.buildSelectStatement().watchSingleOrNull(); + Future delete() => state.buildDeleteStatement().go(); } -/// A table manager that only has functions to return items based on the state build by parent managers -class ProcessedTableManager< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer> - extends BaseTableManager - with ProcessedTableManagerMixin { - /// A table manager that only has functions to return items based on the state build by parent managers - const ProcessedTableManager(super.state); -} - /// A table manager that has methods to filter the query -class TableManagerWithFiltering< +abstract class TableManagerWithFiltering< DB extends GeneratedDatabase, T extends TableInfo, D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, C extends ProcessedTableManager> - extends BaseTableManager - with ProcessedTableManagerMixin { - /// Callback for + extends ProcessedTableManager { final C Function(TableManagerState) _getChildManager; const TableManagerWithFiltering(super.state, {required C Function(TableManagerState) @@ -300,15 +287,14 @@ class TableManagerWithFiltering< } /// A table manager that has methods to order the query -class TableManagerWithOrdering< +abstract class TableManagerWithOrdering< DB extends GeneratedDatabase, T extends TableInfo, D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, C extends ProcessedTableManager> - extends BaseTableManager - with ProcessedTableManagerMixin { + extends ProcessedTableManager { final C Function(TableManagerState) _getChildManager; const TableManagerWithOrdering(super.state, {required C Function(TableManagerState) @@ -338,7 +324,6 @@ abstract class RootTableManager< final CO Function(TableManagerState) _getChildManagerWithOrdering; final CI _createInsertable; - RootTableManager(super.state, {required CF Function(TableManagerState) getChildManagerWithFiltering, @@ -369,6 +354,15 @@ abstract class RootTableManager< .insert(f(_createInsertable), mode: mode, onConflict: onConflict); } + Future createReturning( + Insertable Function(CI o) f, + InsertMode? mode, + UpsertClause? onConflict, + ) { + return state.db.into(state.table).insertReturning(f(_createInsertable), + mode: mode, onConflict: onConflict) as Future; + } + Future bulkCreate( Iterable> Function(CI o) f, InsertMode? mode, @@ -378,6 +372,10 @@ abstract class RootTableManager< mode: mode, onConflict: onConflict)); } + Future replace(Insertable entry) { + return state.db.update(state.table).replace(entry); + } + CF orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); return _getChildManagerWithFiltering(state.copyWith( diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 4fc4b486..0722013b 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -3,12 +3,6 @@ import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/writer.dart'; import 'package:recase/recase.dart'; -extension on DriftColumn { - bool get isForeignKey { - return constraints.whereType().isNotEmpty; - } -} - abstract class _Filter { final String filterName; _Filter( @@ -190,6 +184,7 @@ class _TableNames { /// Columns with their names, filters and orderings final List<_ColumnNames> columns; + /// Filters for back references final List<_ReferencedFilter> backRefFilters; _TableNames(this.table) @@ -207,7 +202,7 @@ class _TableNames { backRefFilters = [], columns = []; - void writeManager(TextEmitter leaf, String dbClassName) { + void _writeFilterComposer(TextEmitter leaf, String dbClassName) { // Write the FilterComposer leaf ..write('class $filterComposer extends ') @@ -223,7 +218,9 @@ class _TableNames { f.writeFilter(leaf); } leaf.writeln('}'); + } + void _writeOrderingComposer(TextEmitter leaf, String dbClassName) { // Write the OrderingComposer leaf ..write('class $orderingComposer extends ') @@ -236,8 +233,9 @@ class _TableNames { } } leaf.writeln('}'); + } - // Write the ProcessedTableManager + void _writeProcessedTableManager(TextEmitter leaf, String dbClassName) { leaf ..write('class $processedTableManager extends ') ..writeDriftRef('ProcessedTableManager') @@ -245,8 +243,9 @@ class _TableNames { '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer> {') ..writeln('const $processedTableManager(super.state);') ..writeln('}'); + } - // Write the TableManagerWithFiltering + void _writeTableManagerWithFiltering(TextEmitter leaf, String dbClassName) { leaf ..write('class $tableManagerWithFiltering extends ') ..writeDriftRef('TableManagerWithFiltering') @@ -255,33 +254,42 @@ class _TableNames { ..writeln( 'const $tableManagerWithFiltering(super.state,{required super.getChildManager});') ..writeln('}'); + } - // Write the TableManagerWithOrdering + void _writeTableManagerWithOrdering(TextEmitter leaf, String dbClassName) { leaf ..write('class $tableManagerWithOrdering extends ') ..writeDriftRef('TableManagerWithOrdering') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager> {') + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager>{') ..writeln( 'const $tableManagerWithOrdering(super.state,{required super.getChildManager});') ..writeln('}'); - // Write the Root Table Manager - - // We need to build a function type which will create insertable items - // We then need to create the actual function + } + void _writeRootTable(TextEmitter leaf, String dbClassName) { final companionClassName = leaf.dartCode(leaf.companionType(table)); + // final updateCompanionTypedef = StringBuffer( + // 'typedef $updateCompanionTypedefName = $companionClassName Function({'); final createInsertableFunctionTypeDef = - StringBuffer("$companionClassName Function({"); - final createInsertableFunctionArgs = StringBuffer("({"); + StringBuffer('$companionClassName Function({'); + // final updateCompanionArguments = StringBuffer('({'); + final createInsertableFunctionArgs = StringBuffer('({'); + // final updateCompanionBody = StringBuffer('=> $companionClassName('); final createInsertableFunctionBody = - StringBuffer("=> $companionClassName.insert("); - + StringBuffer('=> $companionClassName.insert('); for (final column in table.columns) { final value = leaf.drift('Value'); final param = column.nameInDart; final typeName = leaf.dartCode(leaf.dartType(column)); - createInsertableFunctionBody.write('$param: $param,'); + + // The update companion has no required fields, they are all defaulted to absent + // updateCompanionTypedef.write('$value<$typeName> $param,'); + // updateCompanionArguments + // .write('$value<$typeName> $param = const $value.absent(),'); + // updateCompanionBody.write('$param: $param,'); + + // The insert compantion has some required arguments and some that are defaulted to absent if (!column.isImplicitRowId && table.isColumnRequiredForInsert(column)) { createInsertableFunctionTypeDef.write('required $typeName $param,'); createInsertableFunctionArgs.write('required $typeName $param,'); @@ -290,16 +298,28 @@ class _TableNames { createInsertableFunctionArgs .write('$value<$typeName> $param = const $value.absent(),'); } + createInsertableFunctionBody.write('$param: $param,'); } + + // Close + // updateCompanionTypedef.write('})'); createInsertableFunctionTypeDef.write('})'); createInsertableFunctionArgs.write('})'); createInsertableFunctionBody.write(")"); + // leaf.writeln(updateCompanionTypedef); + // leaf.writeln(insertCompanionTypedef); + + // updateCompanionArguments.write('})'); + // insertCompanionArguments.write('})'); + // updateCompanionBody.write(")"); + // insertCompanionBody.write(")"); + leaf ..write('class $rootTableManager extends ') ..writeDriftRef('RootTableManager') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$tableManagerWithFiltering,$tableManagerWithOrdering, $createInsertableFunctionTypeDef> {') + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$tableManagerWithFiltering,$tableManagerWithOrdering,$createInsertableFunctionTypeDef> {') ..writeln('$rootTableManager($dbClassName db, $tableClassName table)') ..writeln(": super(") ..writeDriftRef("TableManagerState") @@ -311,6 +331,15 @@ class _TableNames { ..writeln('}'); } + void writeManager(TextEmitter leaf, String dbClassName) { + _writeFilterComposer(leaf, dbClassName); + _writeOrderingComposer(leaf, dbClassName); + _writeProcessedTableManager(leaf, dbClassName); + _writeTableManagerWithFiltering(leaf, dbClassName); + _writeTableManagerWithOrdering(leaf, dbClassName); + _writeRootTable(leaf, dbClassName); + } + void addFiltersAndOrderings(List tables, TextEmitter leaf) { /// First add the filters and orderings for the columns /// of the current table From f9407945ba4800327e210a598b05e1b0dfa02685 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 16:05:39 -0400 Subject: [PATCH 18/74] pos --- drift/lib/src/runtime/manager/manager.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index d5c610a5..44e59b83 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -346,8 +346,8 @@ abstract class RootTableManager< Future create( Insertable Function(CI o) f, - InsertMode? mode, - UpsertClause? onConflict, + {InsertMode? mode, + UpsertClause? onConflict}, ) { return state.db .into(state.table) @@ -356,8 +356,8 @@ abstract class RootTableManager< Future createReturning( Insertable Function(CI o) f, - InsertMode? mode, - UpsertClause? onConflict, + {InsertMode? mode, + UpsertClause? onConflict}, ) { return state.db.into(state.table).insertReturning(f(_createInsertable), mode: mode, onConflict: onConflict) as Future; @@ -365,8 +365,8 @@ abstract class RootTableManager< Future bulkCreate( Iterable> Function(CI o) f, - InsertMode? mode, - UpsertClause? onConflict, + {InsertMode? mode, + UpsertClause? onConflict}, ) { return state.db.batch((b) => b.insertAll(state.table, f(_createInsertable), mode: mode, onConflict: onConflict)); From 443d48c43a738b8bb994e279c161ab6fce84489c Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 16:48:14 -0400 Subject: [PATCH 19/74] sim --- drift/lib/src/runtime/manager/manager.dart | 139 ++++++--------------- drift_dev/lib/src/writer/manager.dart | 31 +---- 2 files changed, 44 insertions(+), 126 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 44e59b83..0c8b01b1 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -224,28 +224,48 @@ abstract class BaseTableManager< T extends TableInfo, DT extends DataClass, FS extends FilterComposer, - OS extends OrderingComposer> { + OS extends OrderingComposer, + C extends ProcessedTableManager> { /// The state for this manager final TableManagerState state; + final C Function(TableManagerState) _getChildManager; /// Create a new [BaseTableManager] instance - const BaseTableManager(this.state); + const BaseTableManager(this.state, this._getChildManager); Future delete() => state.buildDeleteStatement().go(); + + C orderBy(ComposableOrdering Function(OS o) o) { + final orderings = o(state.orderingComposer); + return _getChildManager(state.copyWith( + orderingBuilders: + orderings.orderingBuilders.union(state.orderingBuilders), + joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); + } + + C filter(ComposableFilter Function(FS f) f) { + final filter = f(state.filteringComposer); + return _getChildManager(state.copyWith( + filter: state.filter == null + ? filter.expression + : filter.expression & state.filter!, + joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + } } -/// Mixin for adding select functionality to a table manager abstract class ProcessedTableManager< DB extends GeneratedDatabase, T extends TableInfo, D extends DataClass, FS extends FilterComposer, - OS extends OrderingComposer> - extends BaseTableManager + OS extends OrderingComposer, + C extends ProcessedTableManager> + extends BaseTableManager implements MultiSelectable, SingleSelectable, SingleOrNullSelectable { - const ProcessedTableManager(super.state); + const ProcessedTableManager(super.state, super.getChildManager); + @override Future getSingle() => state.buildSelectStatement().getSingle(); @override @@ -264,50 +284,6 @@ abstract class ProcessedTableManager< Future delete() => state.buildDeleteStatement().go(); } -/// A table manager that has methods to filter the query -abstract class TableManagerWithFiltering< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer, - C extends ProcessedTableManager> - extends ProcessedTableManager { - final C Function(TableManagerState) _getChildManager; - const TableManagerWithFiltering(super.state, - {required C Function(TableManagerState) - getChildManager}) - : _getChildManager = getChildManager; - C filter(ComposableFilter Function(FS f) f) { - final filter = f(state.filteringComposer); - return _getChildManager(state.copyWith( - filter: filter.expression, - joinBuilders: state.joinBuilders.union(filter.joinBuilders))); - } -} - -/// A table manager that has methods to order the query -abstract class TableManagerWithOrdering< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer, - C extends ProcessedTableManager> - extends ProcessedTableManager { - final C Function(TableManagerState) _getChildManager; - const TableManagerWithOrdering(super.state, - {required C Function(TableManagerState) - getChildManager}) - : _getChildManager = getChildManager; - C orderBy(ComposableOrdering Function(OS o) o) { - final orderings = o(state.orderingComposer); - return _getChildManager(state.copyWith( - orderingBuilders: orderings.orderingBuilders, - joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); - } -} - /// A table manager with top level function for creating, reading, updating, and deleting items abstract class RootTableManager< DB extends GeneratedDatabase, @@ -315,59 +291,33 @@ abstract class RootTableManager< D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CF extends TableManagerWithFiltering, - CO extends TableManagerWithOrdering, - CI extends Function> extends BaseTableManager { - final CF Function(TableManagerState) - _getChildManagerWithFiltering; - final CO Function(TableManagerState) - _getChildManagerWithOrdering; + C extends ProcessedTableManager, + CI extends Function> extends BaseTableManager { final CI _createInsertable; - RootTableManager(super.state, - {required CF Function(TableManagerState) - getChildManagerWithFiltering, - required CO Function(TableManagerState) - getChildManagerWithOrdering, - required CI createInsertable}) - : _getChildManagerWithFiltering = getChildManagerWithFiltering, - _getChildManagerWithOrdering = getChildManagerWithOrdering, - _createInsertable = createInsertable; - CO filter(ComposableFilter Function(FS f) f) { - final filter = f(state.filteringComposer); - return _getChildManagerWithOrdering(state.copyWith( - filter: filter.expression, - joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + + RootTableManager(super.state, super.getChildManager, + {required CI createInsertable}) + : _createInsertable = createInsertable; + + C all() { + return _getChildManager(state); } - CO all() { - return _getChildManagerWithOrdering(state); - } - - Future create( - Insertable Function(CI o) f, - {InsertMode? mode, - UpsertClause? onConflict}, - ) { + Future create(Insertable Function(CI o) f, + {InsertMode? mode, UpsertClause? onConflict}) { return state.db .into(state.table) .insert(f(_createInsertable), mode: mode, onConflict: onConflict); } - Future createReturning( - Insertable Function(CI o) f, - {InsertMode? mode, - UpsertClause? onConflict}, - ) { + Future createReturning(Insertable Function(CI o) f, + {InsertMode? mode, UpsertClause? onConflict}) { return state.db.into(state.table).insertReturning(f(_createInsertable), mode: mode, onConflict: onConflict) as Future; } - Future bulkCreate( - Iterable> Function(CI o) f, - {InsertMode? mode, - UpsertClause? onConflict}, - ) { + Future bulkCreate(Iterable> Function(CI o) f, + {InsertMode? mode, UpsertClause? onConflict}) { return state.db.batch((b) => b.insertAll(state.table, f(_createInsertable), mode: mode, onConflict: onConflict)); } @@ -375,11 +325,4 @@ abstract class RootTableManager< Future replace(Insertable entry) { return state.db.update(state.table).replace(entry); } - - CF orderBy(ComposableOrdering Function(OS o) o) { - final orderings = o(state.orderingComposer); - return _getChildManagerWithFiltering(state.copyWith( - orderingBuilders: orderings.orderingBuilders, - joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); - } } diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 0722013b..4a447124 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -239,31 +239,10 @@ class _TableNames { leaf ..write('class $processedTableManager extends ') ..writeDriftRef('ProcessedTableManager') - ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer> {') - ..writeln('const $processedTableManager(super.state);') - ..writeln('}'); - } - - void _writeTableManagerWithFiltering(TextEmitter leaf, String dbClassName) { - leaf - ..write('class $tableManagerWithFiltering extends ') - ..writeDriftRef('TableManagerWithFiltering') ..writeln( '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager> {') ..writeln( - 'const $tableManagerWithFiltering(super.state,{required super.getChildManager});') - ..writeln('}'); - } - - void _writeTableManagerWithOrdering(TextEmitter leaf, String dbClassName) { - leaf - ..write('class $tableManagerWithOrdering extends ') - ..writeDriftRef('TableManagerWithOrdering') - ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager>{') - ..writeln( - 'const $tableManagerWithOrdering(super.state,{required super.getChildManager});') + 'const $processedTableManager(super.state,super.getChildManager);') ..writeln('}'); } @@ -319,14 +298,12 @@ class _TableNames { ..write('class $rootTableManager extends ') ..writeDriftRef('RootTableManager') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$tableManagerWithFiltering,$tableManagerWithOrdering,$createInsertableFunctionTypeDef> {') + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$createInsertableFunctionTypeDef> {') ..writeln('$rootTableManager($dbClassName db, $tableClassName table)') ..writeln(": super(") ..writeDriftRef("TableManagerState") ..write( - """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table)), - getChildManagerWithFiltering: (f) => $tableManagerWithFiltering(f,getChildManager: (f) => $processedTableManager(f)), - getChildManagerWithOrdering: (f) => $tableManagerWithOrdering(f,getChildManager: (f) =>$processedTableManager(f)) + """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table)) ,createInsertable: $createInsertableFunctionArgs$createInsertableFunctionBody);""") ..writeln('}'); } @@ -335,8 +312,6 @@ class _TableNames { _writeFilterComposer(leaf, dbClassName); _writeOrderingComposer(leaf, dbClassName); _writeProcessedTableManager(leaf, dbClassName); - _writeTableManagerWithFiltering(leaf, dbClassName); - _writeTableManagerWithOrdering(leaf, dbClassName); _writeRootTable(leaf, dbClassName); } From de56ca033adc53826ff07805111334ad5601e6d0 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 16:55:31 -0400 Subject: [PATCH 20/74] simplify --- drift/lib/src/runtime/manager/manager.dart | 29 +++++++++++++--------- drift_dev/lib/src/writer/manager.dart | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 0c8b01b1..cf4aabc7 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -228,27 +228,32 @@ abstract class BaseTableManager< C extends ProcessedTableManager> { /// The state for this manager final TableManagerState state; - final C Function(TableManagerState) _getChildManager; + final C Function(TableManagerState, + BaseTableManager) getChildManager; /// Create a new [BaseTableManager] instance - const BaseTableManager(this.state, this._getChildManager); + const BaseTableManager(this.state, this.getChildManager); Future delete() => state.buildDeleteStatement().go(); C orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); - return _getChildManager(state.copyWith( - orderingBuilders: - orderings.orderingBuilders.union(state.orderingBuilders), - joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); + return getChildManager( + state.copyWith( + orderingBuilders: + orderings.orderingBuilders.union(state.orderingBuilders), + joinBuilders: state.joinBuilders.union(orderings.joinBuilders)), + this); } C filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); - return _getChildManager(state.copyWith( - filter: state.filter == null - ? filter.expression - : filter.expression & state.filter!, - joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + return getChildManager( + state.copyWith( + filter: state.filter == null + ? filter.expression + : filter.expression & state.filter!, + joinBuilders: state.joinBuilders.union(filter.joinBuilders)), + this); } } @@ -300,7 +305,7 @@ abstract class RootTableManager< : _createInsertable = createInsertable; C all() { - return _getChildManager(state); + return getChildManager(state, this); } Future create(Insertable Function(CI o) f, diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 4a447124..5cfe6b87 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -304,7 +304,7 @@ class _TableNames { ..writeDriftRef("TableManagerState") ..write( """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table)) - ,createInsertable: $createInsertableFunctionArgs$createInsertableFunctionBody);""") + ,(p0,p1) => $processedTableManager(p0,p1.getChildManager),createInsertable: $createInsertableFunctionArgs$createInsertableFunctionBody);""") ..writeln('}'); } From 4b3dd99a0a43e10914542d8ec7e908600e5c6af2 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 20:08:41 -0400 Subject: [PATCH 21/74] sd --- drift/lib/src/runtime/manager/manager.dart | 115 ++++++++++----------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index cf4aabc7..1ab06612 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -29,7 +29,9 @@ class TableManagerState< T extends Table, DT extends DataClass, FS extends FilterComposer, - OS extends OrderingComposer> { + OS extends OrderingComposer, + C extends ProcessedTableManager, + CH extends C Function(TableManagerState)> { /// The database that the query will be exeCCted on final DB db; @@ -66,6 +68,9 @@ class TableManagerState< /// which will be applied to the statement when its eventually created final OS orderingComposer; + /// This function is used internaly to return a new instance of the child manager + final CH _getChildManagerBuilder; + /// Defines a class which holds the state for a table manager /// It contains the database instance, the table instance, and any filters/orderings that will be applied to the query /// This is held in a seperate class than the [BaseTableManager] so that the state can be passed down from the root manager to the lower level managers @@ -74,16 +79,17 @@ class TableManagerState< required this.table, required this.filteringComposer, required this.orderingComposer, + required CH getChildManagerBuilder, this.filter, this.distinct, this.limit, this.offset, this.orderingBuilders = const {}, this.joinBuilders = const {}, - }); + }) : _getChildManagerBuilder = getChildManagerBuilder; /// Copy this state with the given values - TableManagerState copyWith({ + TableManagerState copyWith({ bool? distinct, int? limit, int? offset, @@ -96,6 +102,7 @@ class TableManagerState< table: table, filteringComposer: filteringComposer, orderingComposer: orderingComposer, + getChildManagerBuilder: _getChildManagerBuilder, filter: filter ?? this.filter, joinBuilders: joinBuilders ?? this.joinBuilders, orderingBuilders: orderingBuilders ?? this.orderingBuilders, @@ -221,55 +228,51 @@ class TableManagerState< @internal abstract class BaseTableManager< DB extends GeneratedDatabase, - T extends TableInfo, + T extends Table, DT extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager> { + C extends ProcessedTableManager, + CH extends C Function(TableManagerState)> { /// The state for this manager - final TableManagerState state; - final C Function(TableManagerState, - BaseTableManager) getChildManager; + final TableManagerState state; /// Create a new [BaseTableManager] instance - const BaseTableManager(this.state, this.getChildManager); + BaseTableManager(this.state); Future delete() => state.buildDeleteStatement().go(); C orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); - return getChildManager( - state.copyWith( - orderingBuilders: - orderings.orderingBuilders.union(state.orderingBuilders), - joinBuilders: state.joinBuilders.union(orderings.joinBuilders)), - this); + return state._getChildManagerBuilder(state.copyWith( + orderingBuilders: + state.orderingBuilders.union(orderings.orderingBuilders), + joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); } C filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); - return getChildManager( - state.copyWith( - filter: state.filter == null - ? filter.expression - : filter.expression & state.filter!, - joinBuilders: state.joinBuilders.union(filter.joinBuilders)), - this); + return state._getChildManagerBuilder(state.copyWith( + filter: state.filter == null + ? filter.expression + : filter.expression & state.filter!, + joinBuilders: state.joinBuilders.union(filter.joinBuilders))); } } abstract class ProcessedTableManager< DB extends GeneratedDatabase, - T extends TableInfo, + T extends Table, D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager> - extends BaseTableManager + C extends ProcessedTableManager, + CH extends C Function(TableManagerState)> + extends BaseTableManager implements MultiSelectable, SingleSelectable, SingleOrNullSelectable { - const ProcessedTableManager(super.state, super.getChildManager); + ProcessedTableManager(super.state); @override Future getSingle() => state.buildSelectStatement().getSingle(); @@ -291,43 +294,39 @@ abstract class ProcessedTableManager< /// A table manager with top level function for creating, reading, updating, and deleting items abstract class RootTableManager< - DB extends GeneratedDatabase, - T extends TableInfo, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer, - C extends ProcessedTableManager, - CI extends Function> extends BaseTableManager { - final CI _createInsertable; - - RootTableManager(super.state, super.getChildManager, - {required CI createInsertable}) - : _createInsertable = createInsertable; + DB extends GeneratedDatabase, + T extends Table, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager, + CH extends C Function(TableManagerState)> + extends BaseTableManager { + RootTableManager(super.state); C all() { - return getChildManager(state, this); + return state._getChildManagerBuilder(state); } - Future create(Insertable Function(CI o) f, - {InsertMode? mode, UpsertClause? onConflict}) { - return state.db - .into(state.table) - .insert(f(_createInsertable), mode: mode, onConflict: onConflict); - } + // Future create(Insertable Function(CI o) f, + // {InsertMode? mode, UpsertClause? onConflict}) { + // return state.db.into(state.table) + // .insert(f(_createInsertable), mode: mode, onConflict: onConflict); + // } - Future createReturning(Insertable Function(CI o) f, - {InsertMode? mode, UpsertClause? onConflict}) { - return state.db.into(state.table).insertReturning(f(_createInsertable), - mode: mode, onConflict: onConflict) as Future; - } + // Future createReturning(Insertable Function(CI o) f, + // {InsertMode? mode, UpsertClause? onConflict}) { + // return state.db.into(state.table).insertReturning(f(_createInsertable), + // mode: mode, onConflict: onConflict) as Future; + // } - Future bulkCreate(Iterable> Function(CI o) f, - {InsertMode? mode, UpsertClause? onConflict}) { - return state.db.batch((b) => b.insertAll(state.table, f(_createInsertable), - mode: mode, onConflict: onConflict)); - } + // Future bulkCreate(Iterable> Function(CI o) f, + // {InsertMode? mode, UpsertClause? onConflict}) { + // return state.db.batch((b) => b.insertAll(state.table, f(_createInsertable), + // mode: mode, onConflict: onConflict)); + // } - Future replace(Insertable entry) { - return state.db.update(state.table).replace(entry); - } + // Future replace(Insertable entry) { + // return state.db.update(state.table).replace(entry); + // } } From ce3e4e2b7000984d0e6fff69d9f1bcbdbc3cd8f0 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 20:36:23 -0400 Subject: [PATCH 22/74] df --- drift/lib/src/runtime/manager/manager.dart | 50 +++++++++++++++------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 1ab06612..8e388dc7 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -30,8 +30,8 @@ class TableManagerState< DT extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CH extends C Function(TableManagerState)> { + C extends ProcessedTableManager, + CI extends CompanionBuilder
> { /// The database that the query will be exeCCted on final DB db; @@ -69,7 +69,12 @@ class TableManagerState< final OS orderingComposer; /// This function is used internaly to return a new instance of the child manager - final CH _getChildManagerBuilder; + final C Function(TableManagerState) + _getChildManagerBuilder; + + /// This function will return a class which contains a function which + /// + final CI _getInsertableCompanionBuilder; /// Defines a class which holds the state for a table manager /// It contains the database instance, the table instance, and any filters/orderings that will be applied to the query @@ -79,17 +84,20 @@ class TableManagerState< required this.table, required this.filteringComposer, required this.orderingComposer, - required CH getChildManagerBuilder, + required C Function(TableManagerState) + getChildManagerBuilder, + required CI getInsertableCompanionBuilder, this.filter, this.distinct, this.limit, this.offset, this.orderingBuilders = const {}, this.joinBuilders = const {}, - }) : _getChildManagerBuilder = getChildManagerBuilder; + }) : _getChildManagerBuilder = getChildManagerBuilder, + _getInsertableCompanionBuilder = getInsertableCompanionBuilder; /// Copy this state with the given values - TableManagerState copyWith({ + TableManagerState copyWith({ bool? distinct, int? limit, int? offset, @@ -103,6 +111,7 @@ class TableManagerState< filteringComposer: filteringComposer, orderingComposer: orderingComposer, getChildManagerBuilder: _getChildManagerBuilder, + getInsertableCompanionBuilder: _getInsertableCompanionBuilder, filter: filter ?? this.filter, joinBuilders: joinBuilders ?? this.joinBuilders, orderingBuilders: orderingBuilders ?? this.orderingBuilders, @@ -232,10 +241,10 @@ abstract class BaseTableManager< DT extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CH extends C Function(TableManagerState)> { + C extends ProcessedTableManager, + CI extends CompanionBuilder
> { /// The state for this manager - final TableManagerState state; + final TableManagerState state; /// Create a new [BaseTableManager] instance BaseTableManager(this.state); @@ -265,9 +274,9 @@ abstract class ProcessedTableManager< D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CH extends C Function(TableManagerState)> - extends BaseTableManager + C extends ProcessedTableManager, + CI extends CompanionBuilder> + extends BaseTableManager implements MultiSelectable, SingleSelectable, @@ -299,9 +308,9 @@ abstract class RootTableManager< D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CH extends C Function(TableManagerState)> - extends BaseTableManager { + C extends ProcessedTableManager, + CI extends CompanionBuilder> + extends BaseTableManager { RootTableManager(super.state); C all() { @@ -310,7 +319,10 @@ abstract class RootTableManager< // Future create(Insertable Function(CI o) f, // {InsertMode? mode, UpsertClause? onConflict}) { - // return state.db.into(state.table) + // (state._getInsertableCompanionBuilder.create() as CI).companion = f; + + // return state.db + // .into(state.table) // .insert(f(_createInsertable), mode: mode, onConflict: onConflict); // } @@ -330,3 +342,9 @@ abstract class RootTableManager< // return state.db.update(state.table).replace(entry); // } } + +abstract class CompanionBuilder { + late final UpdateCompanion companion; + Function get insert; + Function get create; +} From 276319e3ab936ae3c8c6b2b97c6bf06417a0808a Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 21:04:14 -0400 Subject: [PATCH 23/74] streamline! --- drift/lib/src/runtime/manager/manager.dart | 116 +++++++++++---------- drift_dev/lib/src/writer/manager.dart | 83 +++++++++------ 2 files changed, 110 insertions(+), 89 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 8e388dc7..e1f6c9e0 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -30,8 +30,9 @@ class TableManagerState< DT extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CI extends CompanionBuilder
> { + C extends ProcessedTableManager, + CI extends Function, + CU extends Function> { /// The database that the query will be exeCCted on final DB db; @@ -69,12 +70,16 @@ class TableManagerState< final OS orderingComposer; /// This function is used internaly to return a new instance of the child manager - final C Function(TableManagerState) + final C Function(TableManagerState) _getChildManagerBuilder; - /// This function will return a class which contains a function which - /// - final CI _getInsertableCompanionBuilder; + /// This function is passed to the user to create a companion + /// for inserting data into the table + final CI _getInsertCompanionBuilder; + + /// This function is passed to the user to create a companion + /// for updating data in the table + final CU _getUpdateCompanionBuilder; /// Defines a class which holds the state for a table manager /// It contains the database instance, the table instance, and any filters/orderings that will be applied to the query @@ -84,9 +89,10 @@ class TableManagerState< required this.table, required this.filteringComposer, required this.orderingComposer, - required C Function(TableManagerState) + required C Function(TableManagerState) getChildManagerBuilder, - required CI getInsertableCompanionBuilder, + required CI getInsertCompanionBuilder, + required CU getUpdateCompanionBuilder, this.filter, this.distinct, this.limit, @@ -94,10 +100,11 @@ class TableManagerState< this.orderingBuilders = const {}, this.joinBuilders = const {}, }) : _getChildManagerBuilder = getChildManagerBuilder, - _getInsertableCompanionBuilder = getInsertableCompanionBuilder; + _getInsertCompanionBuilder = getInsertCompanionBuilder, + _getUpdateCompanionBuilder = getUpdateCompanionBuilder; /// Copy this state with the given values - TableManagerState copyWith({ + TableManagerState copyWith({ bool? distinct, int? limit, int? offset, @@ -111,7 +118,8 @@ class TableManagerState< filteringComposer: filteringComposer, orderingComposer: orderingComposer, getChildManagerBuilder: _getChildManagerBuilder, - getInsertableCompanionBuilder: _getInsertableCompanionBuilder, + getInsertCompanionBuilder: _getInsertCompanionBuilder, + getUpdateCompanionBuilder: _getUpdateCompanionBuilder, filter: filter ?? this.filter, joinBuilders: joinBuilders ?? this.joinBuilders, orderingBuilders: orderingBuilders ?? this.orderingBuilders, @@ -241,13 +249,14 @@ abstract class BaseTableManager< DT extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CI extends CompanionBuilder
> { + C extends ProcessedTableManager, + CI extends Function, + CU extends Function> { /// The state for this manager - final TableManagerState state; + final TableManagerState state; /// Create a new [BaseTableManager] instance - BaseTableManager(this.state); + const BaseTableManager(this.state); Future delete() => state.buildDeleteStatement().go(); C orderBy(ComposableOrdering Function(OS o) o) { @@ -274,14 +283,15 @@ abstract class ProcessedTableManager< D extends DataClass, FS extends FilterComposer, OS extends OrderingComposer, - C extends ProcessedTableManager, - CI extends CompanionBuilder> - extends BaseTableManager + C extends ProcessedTableManager, + CI extends Function, + CU extends Function> + extends BaseTableManager implements MultiSelectable, SingleSelectable, SingleOrNullSelectable { - ProcessedTableManager(super.state); + const ProcessedTableManager(super.state); @override Future getSingle() => state.buildSelectStatement().getSingle(); @@ -303,48 +313,44 @@ abstract class ProcessedTableManager< /// A table manager with top level function for creating, reading, updating, and deleting items abstract class RootTableManager< - DB extends GeneratedDatabase, - T extends Table, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer, - C extends ProcessedTableManager, - CI extends CompanionBuilder> - extends BaseTableManager { - RootTableManager(super.state); + DB extends GeneratedDatabase, + T extends Table, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager, + CI extends Function, + CU extends Function> extends BaseTableManager { + const RootTableManager(super.state); C all() { return state._getChildManagerBuilder(state); } - // Future create(Insertable Function(CI o) f, - // {InsertMode? mode, UpsertClause? onConflict}) { - // (state._getInsertableCompanionBuilder.create() as CI).companion = f; + Future create(Insertable Function(CI o) f, + {InsertMode? mode, UpsertClause? onConflict}) { + return state.db.into(state._tableAsTableInfo).insert( + f(state._getInsertCompanionBuilder), + mode: mode, + onConflict: onConflict); + } - // return state.db - // .into(state.table) - // .insert(f(_createInsertable), mode: mode, onConflict: onConflict); - // } + Future createReturning(Insertable Function(CI o) f, + {InsertMode? mode, UpsertClause? onConflict}) { + return state.db.into(state._tableAsTableInfo).insertReturning( + f(state._getInsertCompanionBuilder), + mode: mode, + onConflict: onConflict); + } - // Future createReturning(Insertable Function(CI o) f, - // {InsertMode? mode, UpsertClause? onConflict}) { - // return state.db.into(state.table).insertReturning(f(_createInsertable), - // mode: mode, onConflict: onConflict) as Future; - // } + Future bulkCreate(Iterable> Function(CI o) f, + {InsertMode? mode, UpsertClause? onConflict}) { + return state.db.batch((b) => b.insertAll( + state._tableAsTableInfo, f(state._getInsertCompanionBuilder), + mode: mode, onConflict: onConflict)); + } - // Future bulkCreate(Iterable> Function(CI o) f, - // {InsertMode? mode, UpsertClause? onConflict}) { - // return state.db.batch((b) => b.insertAll(state.table, f(_createInsertable), - // mode: mode, onConflict: onConflict)); - // } - - // Future replace(Insertable entry) { - // return state.db.update(state.table).replace(entry); - // } -} - -abstract class CompanionBuilder { - late final UpdateCompanion companion; - Function get insert; - Function get create; + Future replace(Insertable entry) { + return state.db.update(state._tableAsTableInfo).replace(entry); + } } diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 5cfe6b87..7326d4bc 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -176,6 +176,16 @@ class _TableNames { /// E.G `categories` final String tableGetterName; + /// Name of the typedef for the insertCompanionBuilder + /// + /// E.G. `insertCompanionBuilder` + final String insertCompanionBuilderTypeDefName; + + /// Name of the arguments for the updateCompanionBuilder + /// + /// E.G. `updateCompanionBuilderTypeDef` + final String updateCompanionBuilderTypeDefName; + /// Name of the class that cooresponds to a table row /// /// E.G `Category` @@ -196,6 +206,10 @@ class _TableNames { tableManagerWithOrdering = '${table.entityInfoName}TableManagerWithOrdering', rootTableManager = '${table.entityInfoName}TableManager', + insertCompanionBuilderTypeDefName = + '${table.entityInfoName}InsertCompanionBuilder', + updateCompanionBuilderTypeDefName = + '${table.entityInfoName}UpdateCompanionBuilder', rowClassName = table.nameOfRowClass, tableClassName = table.entityInfoName, tableGetterName = table.dbGetterName, @@ -240,71 +254,72 @@ class _TableNames { ..write('class $processedTableManager extends ') ..writeDriftRef('ProcessedTableManager') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager> {') - ..writeln( - 'const $processedTableManager(super.state,super.getChildManager);') + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') + ..writeln('const $processedTableManager(super.state);') ..writeln('}'); } void _writeRootTable(TextEmitter leaf, String dbClassName) { final companionClassName = leaf.dartCode(leaf.companionType(table)); - // final updateCompanionTypedef = StringBuffer( - // 'typedef $updateCompanionTypedefName = $companionClassName Function({'); - final createInsertableFunctionTypeDef = - StringBuffer('$companionClassName Function({'); - // final updateCompanionArguments = StringBuffer('({'); - final createInsertableFunctionArgs = StringBuffer('({'); - // final updateCompanionBody = StringBuffer('=> $companionClassName('); - final createInsertableFunctionBody = + + final updateCompanionBuilderTypeDef = StringBuffer( + 'typedef $updateCompanionBuilderTypeDefName = $companionClassName Function({'); + final insertCompanionBuilderTypeDef = StringBuffer( + 'typedef $insertCompanionBuilderTypeDefName = $companionClassName Function({'); + + final updateCompanionBuilderArguments = StringBuffer('({'); + final insertCompanionBuilderArguments = StringBuffer('({'); + + final updateCompanionBuilderBody = StringBuffer('=> $companionClassName('); + final insertCompanionBuilderBody = StringBuffer('=> $companionClassName.insert('); + for (final column in table.columns) { final value = leaf.drift('Value'); final param = column.nameInDart; final typeName = leaf.dartCode(leaf.dartType(column)); // The update companion has no required fields, they are all defaulted to absent - // updateCompanionTypedef.write('$value<$typeName> $param,'); - // updateCompanionArguments - // .write('$value<$typeName> $param = const $value.absent(),'); - // updateCompanionBody.write('$param: $param,'); + updateCompanionBuilderTypeDef.write('$value<$typeName> $param,'); + updateCompanionBuilderArguments + .write('$value<$typeName> $param = const $value.absent(),'); + updateCompanionBuilderBody.write('$param: $param,'); // The insert compantion has some required arguments and some that are defaulted to absent if (!column.isImplicitRowId && table.isColumnRequiredForInsert(column)) { - createInsertableFunctionTypeDef.write('required $typeName $param,'); - createInsertableFunctionArgs.write('required $typeName $param,'); + insertCompanionBuilderTypeDef.write('required $typeName $param,'); + insertCompanionBuilderArguments.write('required $typeName $param,'); } else { - createInsertableFunctionTypeDef.write('$value<$typeName> $param,'); - createInsertableFunctionArgs + insertCompanionBuilderTypeDef.write('$value<$typeName> $param,'); + insertCompanionBuilderArguments .write('$value<$typeName> $param = const $value.absent(),'); } - createInsertableFunctionBody.write('$param: $param,'); + insertCompanionBuilderBody.write('$param: $param,'); } // Close // updateCompanionTypedef.write('})'); - createInsertableFunctionTypeDef.write('})'); - createInsertableFunctionArgs.write('})'); - createInsertableFunctionBody.write(")"); - - // leaf.writeln(updateCompanionTypedef); - // leaf.writeln(insertCompanionTypedef); - - // updateCompanionArguments.write('})'); - // insertCompanionArguments.write('})'); - // updateCompanionBody.write(")"); - // insertCompanionBody.write(")"); + insertCompanionBuilderTypeDef.write('});'); + updateCompanionBuilderTypeDef.write('});'); + insertCompanionBuilderArguments.write('})'); + updateCompanionBuilderArguments.write('})'); + insertCompanionBuilderBody.write(")"); + updateCompanionBuilderBody.write(")"); + leaf.writeln(insertCompanionBuilderTypeDef); + leaf.writeln(updateCompanionBuilderTypeDef); leaf ..write('class $rootTableManager extends ') ..writeDriftRef('RootTableManager') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$createInsertableFunctionTypeDef> {') + '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') ..writeln('$rootTableManager($dbClassName db, $tableClassName table)') ..writeln(": super(") ..writeDriftRef("TableManagerState") ..write( - """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table)) - ,(p0,p1) => $processedTableManager(p0,p1.getChildManager),createInsertable: $createInsertableFunctionArgs$createInsertableFunctionBody);""") + """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table), + getChildManagerBuilder :(p0) => $processedTableManager(p0),getUpdateCompanionBuilder: $updateCompanionBuilderArguments$updateCompanionBuilderBody, + getInsertCompanionBuilder:$insertCompanionBuilderArguments$insertCompanionBuilderBody));""") ..writeln('}'); } From 9f2f4522ee8a09d0b188cbe7f78978f253d91cd9 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Mon, 1 Apr 2024 21:50:50 -0400 Subject: [PATCH 24/74] add update --- drift/lib/src/runtime/manager/manager.dart | 46 +++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index e1f6c9e0..9df065d5 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -135,7 +135,7 @@ class TableManagerState< /// Builds a select statement with the given target columns, or all columns if none are provided _StatementType _buildSelectStatement( - {Iterable? targetColumns, + {Iterable? targetColumns, required bool addJoins, required bool applyFilters, required bool applyOrdering, @@ -214,9 +214,41 @@ class TableManagerState< }; } + UpdateStatement buildUpdateStatement() { + final UpdateStatement deleteStatement; + if (joinBuilders.isEmpty) { + deleteStatement = db.update(_tableAsTableInfo); + if (filter != null) { + deleteStatement.where((_) => filter!); + } + } else { + deleteStatement = db.update(_tableAsTableInfo); + for (var col in _tableAsTableInfo.primaryKey) { + final subquery = _buildSelectStatement( + targetColumns: [col], + addJoins: true, + applyFilters: true, + applyOrdering: false, + applyLimit: false) as _JoinedResult; + deleteStatement.where((tbl) => col.isInQuery(subquery.statement)); + } + } + return deleteStatement; + } + + Future count() { + final count = countAll(); + final result = _buildSelectStatement( + targetColumns: [count], + addJoins: true, + applyFilters: true, + applyOrdering: false, + applyLimit: false) as _JoinedResult; + return result.statement.map((row) => row.read(count)!).getSingle(); + } + // Build a delete statement based on the manager state DeleteStatement buildDeleteStatement() { - // If there are any joins we will have to use a subquery to get the rowIds final DeleteStatement deleteStatement; if (joinBuilders.isEmpty) { deleteStatement = db.delete(_tableAsTableInfo); @@ -259,6 +291,7 @@ abstract class BaseTableManager< const BaseTableManager(this.state); Future delete() => state.buildDeleteStatement().go(); + /// Add ordering to the statement C orderBy(ComposableOrdering Function(OS o) o) { final orderings = o(state.orderingComposer); return state._getChildManagerBuilder(state.copyWith( @@ -267,6 +300,7 @@ abstract class BaseTableManager< joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); } + /// Add a filter to the statement C filter(ComposableFilter Function(FS f) f) { final filter = f(state.filteringComposer); return state._getChildManagerBuilder(state.copyWith( @@ -275,6 +309,10 @@ abstract class BaseTableManager< : filter.expression & state.filter!, joinBuilders: state.joinBuilders.union(filter.joinBuilders))); } + + Future count() => state.count(); + Future write(Insertable
Function(CU o) f) => + state.buildUpdateStatement().write(f(state._getUpdateCompanionBuilder)); } abstract class ProcessedTableManager< @@ -323,9 +361,7 @@ abstract class RootTableManager< CU extends Function> extends BaseTableManager { const RootTableManager(super.state); - C all() { - return state._getChildManagerBuilder(state); - } + C all() => state._getChildManagerBuilder(state); Future create(Insertable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { From f9d77b5fa20163ecdd247132f15f4411efba1507 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 2 Apr 2024 13:51:07 -0400 Subject: [PATCH 25/74] add bool filter | add modular generics --- drift/lib/src/runtime/manager/filter.dart | 10 +++ drift_dev/lib/src/writer/manager.dart | 74 ++++++++++++++--------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 990f0ec6..65f9b5fe 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -45,6 +45,16 @@ class ColumnFilters { ColumnFilters get count => ColumnFilters(column.count()); } +extension BoolFilters on ColumnFilters { + /// Create a filter to check if the column is bigger than a value + ComposableFilter isTrue(bool value) => + ComposableFilter.simple(column.equals(true)); + + /// Create a filter to check if the column is small than a value + ComposableFilter isFalse(bool value) => + ComposableFilter.simple(column.equals(true)); +} + /// Built in filters for int/double columns extension NumFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 7326d4bc..3eb87655 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -9,7 +9,7 @@ abstract class _Filter { this.filterName, ); - void writeFilter(TextEmitter leaf); + void writeFilter(TextEmitter leaf, bool isModular); } class _RegularFilter extends _Filter { @@ -18,7 +18,7 @@ class _RegularFilter extends _Filter { _RegularFilter(this.fieldGetter, this.type, super.filterName); @override - void writeFilter(TextEmitter leaf) { + void writeFilter(TextEmitter leaf, bool isModular) { leaf ..writeDriftRef("ColumnFilters") ..write("<$type> get $filterName =>") @@ -35,7 +35,7 @@ class _FilterWithConverter extends _Filter { this.fieldGetter, this.type, super.filterName, this.converterType); @override - void writeFilter(TextEmitter leaf) { + void writeFilter(TextEmitter leaf, bool isModular) { leaf ..writeDriftRef("ColumnWithTypeConverterFilters") ..write("<$converterType,$type> get $filterName =>") @@ -57,7 +57,10 @@ class _ReferencedFilter extends _Filter { this.referencedColumn); @override - void writeFilter(TextEmitter leaf) { + void writeFilter(TextEmitter leaf, bool isModular) { + final String referencedTableGetter = isModular + ? "state.db.resultSet<${referencedTable.tableClassName}>('${referencedTable.table.schemaName}')" + : "state.db.${referencedTable.tableGetterName}"; leaf ..writeDriftRef("ComposableFilter") ..write(" $filterName(") @@ -65,7 +68,7 @@ class _ReferencedFilter extends _Filter { ..writeln(" Function( ${referencedTable.filterComposer} f) f) {") ..writeln(''' return referenced( - referencedTable: state.db.${referencedTable.tableGetterName}, + referencedTable: $referencedTableGetter, getCurrentColumn: (f) => f.$fieldGetter, getReferencedColumn: (f) => f.${referencedColumn.fieldGetter}, getReferencedComposer: (db, table) => ${referencedTable.filterComposer}(db, table), @@ -79,7 +82,7 @@ abstract class _Ordering { _Ordering(this.orderingName); - void writeOrdering(TextEmitter leaf); + void writeOrdering(TextEmitter leaf, bool isModular); } class _RegularOrdering extends _Ordering { @@ -88,7 +91,7 @@ class _RegularOrdering extends _Ordering { _RegularOrdering(this.fieldGetter, this.type, super.orderingName); @override - void writeOrdering(TextEmitter leaf) { + void writeOrdering(TextEmitter leaf, bool isModular) { leaf ..writeDriftRef("ColumnOrderings") ..write(" get $orderingName =>") @@ -105,7 +108,11 @@ class _ReferencedOrdering extends _Ordering { this.referencedTable, this.referencedColumn); @override - void writeOrdering(TextEmitter leaf) { + void writeOrdering(TextEmitter leaf, bool isModular) { + final String referencedTableGetter = isModular + ? "state.db.resultSet<${referencedTable.tableClassName}>('${referencedTable.table.schemaName}')" + : "state.db.${referencedTable.tableGetterName}"; + leaf ..writeDriftRef("ComposableOrdering") ..write(" $orderingName(") @@ -113,7 +120,7 @@ class _ReferencedOrdering extends _Ordering { ..writeln(" Function( ${referencedTable.orderingComposer} o) o) {") ..writeln(''' return referenced( - referencedTable: state.db.${referencedTable.tableGetterName}, + referencedTable: $referencedTableGetter, getCurrentColumn: (f) => f.$fieldGetter, getReferencedColumn: (f) => f.${referencedColumn.fieldGetter}, getReferencedComposer: (db, table) => ${referencedTable.orderingComposer}(db, table), @@ -136,6 +143,9 @@ class _TableNames { /// The current table final DriftTable table; + /// Generation Scope + final Scope _scope; + /// The name of the filter composer class /// /// E.G `UserFilterComposer` @@ -197,19 +207,20 @@ class _TableNames { /// Filters for back references final List<_ReferencedFilter> backRefFilters; - _TableNames(this.table) - : filterComposer = '${table.entityInfoName}FilterComposer', - orderingComposer = '${table.entityInfoName}OrderingComposer', - processedTableManager = '${table.entityInfoName}ProcessedTableManager', + _TableNames(this.table, this._scope) + : filterComposer = '\$${table.entityInfoName}FilterComposer', + orderingComposer = '\$${table.entityInfoName}OrderingComposer', + processedTableManager = + '\$${table.entityInfoName}ProcessedTableManager', tableManagerWithFiltering = - '${table.entityInfoName}TableManagerWithFiltering', + '\$${table.entityInfoName}TableManagerWithFiltering', tableManagerWithOrdering = - '${table.entityInfoName}TableManagerWithOrdering', - rootTableManager = '${table.entityInfoName}TableManager', + '\$${table.entityInfoName}TableManagerWithOrdering', + rootTableManager = '\$${table.entityInfoName}TableManager', insertCompanionBuilderTypeDefName = - '${table.entityInfoName}InsertCompanionBuilder', + '\$${table.entityInfoName}InsertCompanionBuilder', updateCompanionBuilderTypeDefName = - '${table.entityInfoName}UpdateCompanionBuilder', + '\$${table.entityInfoName}UpdateCompanionBuilder', rowClassName = table.nameOfRowClass, tableClassName = table.entityInfoName, tableGetterName = table.dbGetterName, @@ -225,11 +236,11 @@ class _TableNames { ..writeln('$filterComposer(super.db, super.table);'); for (var c in columns) { for (var f in c.filters) { - f.writeFilter(leaf); + f.writeFilter(leaf, _scope.generationOptions.isModular); } } for (var f in backRefFilters) { - f.writeFilter(leaf); + f.writeFilter(leaf, _scope.generationOptions.isModular); } leaf.writeln('}'); } @@ -243,7 +254,7 @@ class _TableNames { ..writeln('$orderingComposer(super.db, super.table);'); for (var c in columns) { for (var o in c.orderings) { - o.writeOrdering(leaf); + o.writeOrdering(leaf, _scope.generationOptions.isModular); } } leaf.writeln('}'); @@ -354,7 +365,7 @@ class _TableNames { ?.otherColumn; final referencedTable = referencedCol?.owner; if (referencedCol != null && referencedTable is DriftTable) { - final referencedTableNames = _TableNames(referencedTable); + final referencedTableNames = _TableNames(referencedTable, _scope); final referencedColumnNames = _ColumnNames(referencedCol.nameInDart, [], []); c.filters.add(_ReferencedFilter(c.fieldGetter, "${c.fieldGetter}Ref", @@ -368,7 +379,7 @@ class _TableNames { columns.add(c); } for (var otherTable in tables) { - final otherTableNames = _TableNames(otherTable); + final otherTableNames = _TableNames(otherTable, _scope); /// We are adding backrefs now, skip the current table if (otherTableNames.tableClassName == tableClassName) { @@ -381,7 +392,7 @@ class _TableNames { ?.otherColumn; final referencedTable = referencedCol?.owner; if (referencedCol != null && referencedTable is DriftTable) { - final referencedTableNames = _TableNames(referencedTable); + final referencedTableNames = _TableNames(referencedTable, _scope); final referencedColumnNames = _ColumnNames(referencedCol.nameInDart, [], []); @@ -432,9 +443,11 @@ class _TableNames { class ManagerWriter { final Scope _scope; final String _dbClassName; - final List _addedTables; + late final List _addedTables; - ManagerWriter(this._scope, this._dbClassName) : _addedTables = []; + ManagerWriter(this._scope, this._dbClassName) { + _addedTables = []; + } String get managerGetter { return '''$_dbMangerName get managers => $_dbMangerName(this);'''; @@ -450,14 +463,19 @@ class ManagerWriter { final leaf = _scope.leaf(); final tableNames = <_TableNames>[]; for (var table in _addedTables) { - final t = _TableNames(table); + final t = _TableNames(table, _scope); t.addFiltersAndOrderings(_addedTables, leaf); - t.writeManager(leaf, _dbClassName); tableNames.add(t); } + final tableManagerGetters = StringBuffer(); for (var table in tableNames) { + table.writeManager( + leaf, + _scope.generationOptions.isModular + ? _scope.drift("GeneratedDatabase") + : _dbClassName); tableManagerGetters.writeln( "${table.rootTableManager} get ${table.tableGetterName} => ${table.rootTableManager}(_db, _db.${table.tableGetterName});"); } From d03414bd800675b253d715f2b863df3852a74ab6 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 2 Apr 2024 14:52:42 -0400 Subject: [PATCH 26/74] add mod --- drift_dev/lib/src/writer/database_writer.dart | 11 +-- drift_dev/lib/src/writer/manager.dart | 76 +++++++++---------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/drift_dev/lib/src/writer/database_writer.dart b/drift_dev/lib/src/writer/database_writer.dart index e93738e5..5b812d39 100644 --- a/drift_dev/lib/src/writer/database_writer.dart +++ b/drift_dev/lib/src/writer/database_writer.dart @@ -39,22 +39,17 @@ class DatabaseWriter { void write() { final elements = input.resolvedAccessor.availableElements; - final managerWriter = ManagerWriter(scope.child(), dbClassName); - // Write data classes, companions and info classes if (!scope.generationOptions.isModular) { for (final reference in elements) { if (reference is DriftTable) { TableWriter(reference, scope.child()).writeInto(); - managerWriter.addTable(reference); } else if (reference is DriftView) { ViewWriter(reference, scope.child(), this).write(); } } } - managerWriter.write(); - // Write the database class final dbScope = scope.child(); @@ -153,6 +148,12 @@ class DatabaseWriter { } } + final managerWriter = ManagerWriter(scope.child(), dbScope, dbClassName); + for (var table in elements.whereType()) { + managerWriter.addTable(table); + } + managerWriter.write(); + firstLeaf.writeln(managerWriter.managerGetter); // Write implementation for query methods diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 3eb87655..7c4ffea0 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -1,7 +1,6 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/writer.dart'; -import 'package:recase/recase.dart'; abstract class _Filter { final String filterName; @@ -60,13 +59,13 @@ class _ReferencedFilter extends _Filter { void writeFilter(TextEmitter leaf, bool isModular) { final String referencedTableGetter = isModular ? "state.db.resultSet<${referencedTable.tableClassName}>('${referencedTable.table.schemaName}')" - : "state.db.${referencedTable.tableGetterName}"; + : "state.db.${referencedTable.table.dbGetterName}"; leaf ..writeDriftRef("ComposableFilter") ..write(" $filterName(") ..writeDriftRef("ComposableFilter") ..writeln(" Function( ${referencedTable.filterComposer} f) f) {") - ..writeln(''' + ..write(''' return referenced( referencedTable: $referencedTableGetter, getCurrentColumn: (f) => f.$fieldGetter, @@ -111,7 +110,7 @@ class _ReferencedOrdering extends _Ordering { void writeOrdering(TextEmitter leaf, bool isModular) { final String referencedTableGetter = isModular ? "state.db.resultSet<${referencedTable.tableClassName}>('${referencedTable.table.schemaName}')" - : "state.db.${referencedTable.tableGetterName}"; + : "state.db.${referencedTable.table.dbGetterName}"; leaf ..writeDriftRef("ComposableOrdering") @@ -144,7 +143,10 @@ class _TableNames { final DriftTable table; /// Generation Scope - final Scope _scope; + final Scope scope; + + /// Generation Scope for the entire database + final Scope dbScope; /// The name of the filter composer class /// @@ -176,38 +178,34 @@ class _TableNames { /// E.G `UserTableManager` final String rootTableManager; - /// Name of the table class that will be generated - /// - /// E.G `$CategoriesTable` - final String tableClassName; - - /// Name of the getter for the table - /// - /// E.G `categories` - final String tableGetterName; - /// Name of the typedef for the insertCompanionBuilder /// /// E.G. `insertCompanionBuilder` final String insertCompanionBuilderTypeDefName; + /// Table class name, this may be different from the entity name + /// if modular generation is enabled + /// E.G. `i5.$CategoriesTable` + String get tableClassName { + return dbScope.dartCode(dbScope.entityInfoType(table)); + } + + String get rowClassName { + return dbScope.dartCode(dbScope.writer.rowType(table)); + } + /// Name of the arguments for the updateCompanionBuilder /// /// E.G. `updateCompanionBuilderTypeDef` final String updateCompanionBuilderTypeDefName; - /// Name of the class that cooresponds to a table row - /// - /// E.G `Category` - final String rowClassName; - /// Columns with their names, filters and orderings final List<_ColumnNames> columns; /// Filters for back references final List<_ReferencedFilter> backRefFilters; - _TableNames(this.table, this._scope) + _TableNames(this.table, this.scope, this.dbScope) : filterComposer = '\$${table.entityInfoName}FilterComposer', orderingComposer = '\$${table.entityInfoName}OrderingComposer', processedTableManager = @@ -221,9 +219,6 @@ class _TableNames { '\$${table.entityInfoName}InsertCompanionBuilder', updateCompanionBuilderTypeDefName = '\$${table.entityInfoName}UpdateCompanionBuilder', - rowClassName = table.nameOfRowClass, - tableClassName = table.entityInfoName, - tableGetterName = table.dbGetterName, backRefFilters = [], columns = []; @@ -236,11 +231,11 @@ class _TableNames { ..writeln('$filterComposer(super.db, super.table);'); for (var c in columns) { for (var f in c.filters) { - f.writeFilter(leaf, _scope.generationOptions.isModular); + f.writeFilter(leaf, scope.generationOptions.isModular); } } for (var f in backRefFilters) { - f.writeFilter(leaf, _scope.generationOptions.isModular); + f.writeFilter(leaf, scope.generationOptions.isModular); } leaf.writeln('}'); } @@ -254,18 +249,19 @@ class _TableNames { ..writeln('$orderingComposer(super.db, super.table);'); for (var c in columns) { for (var o in c.orderings) { - o.writeOrdering(leaf, _scope.generationOptions.isModular); + o.writeOrdering(leaf, scope.generationOptions.isModular); } } leaf.writeln('}'); } void _writeProcessedTableManager(TextEmitter leaf, String dbClassName) { + print(table.entityInfoName); leaf ..write('class $processedTableManager extends ') ..writeDriftRef('ProcessedTableManager') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') + '<$dbClassName,${tableClassName},${rowClassName},$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') ..writeln('const $processedTableManager(super.state);') ..writeln('}'); } @@ -365,7 +361,8 @@ class _TableNames { ?.otherColumn; final referencedTable = referencedCol?.owner; if (referencedCol != null && referencedTable is DriftTable) { - final referencedTableNames = _TableNames(referencedTable, _scope); + final referencedTableNames = + _TableNames(referencedTable, scope, dbScope); final referencedColumnNames = _ColumnNames(referencedCol.nameInDart, [], []); c.filters.add(_ReferencedFilter(c.fieldGetter, "${c.fieldGetter}Ref", @@ -379,10 +376,10 @@ class _TableNames { columns.add(c); } for (var otherTable in tables) { - final otherTableNames = _TableNames(otherTable, _scope); + final otherTableNames = _TableNames(otherTable, scope, dbScope); /// We are adding backrefs now, skip the current table - if (otherTableNames.tableClassName == tableClassName) { + if (otherTableNames.table.entityInfoName == table.entityInfoName) { continue; } for (var otherColumn in otherTable.columns) { @@ -392,15 +389,17 @@ class _TableNames { ?.otherColumn; final referencedTable = referencedCol?.owner; if (referencedCol != null && referencedTable is DriftTable) { - final referencedTableNames = _TableNames(referencedTable, _scope); + final referencedTableNames = + _TableNames(referencedTable, scope, dbScope); final referencedColumnNames = _ColumnNames(referencedCol.nameInDart, [], []); // If we are referencing the current table, add a back ref - if (referencedTableNames.tableClassName == tableClassName) { + if (referencedTableNames.table.entityInfoName == + table.entityInfoName) { backRefFilters.add(_ReferencedFilter( referencedColumnNames.fieldGetter, - "${otherTableNames.tableClassName.camelCase}Refs", + "${otherTableNames.table.dbGetterName}Refs", otherTableNames, referencedColumnNames)); } @@ -442,15 +441,16 @@ class _TableNames { class ManagerWriter { final Scope _scope; + final Scope _dbScope; final String _dbClassName; late final List _addedTables; - ManagerWriter(this._scope, this._dbClassName) { + ManagerWriter(this._scope, this._dbScope, this._dbClassName) { _addedTables = []; } String get managerGetter { - return '''$_dbMangerName get managers => $_dbMangerName(this);'''; + return '$_dbMangerName get managers => $_dbMangerName(this);'; } void addTable(DriftTable table) { @@ -463,7 +463,7 @@ class ManagerWriter { final leaf = _scope.leaf(); final tableNames = <_TableNames>[]; for (var table in _addedTables) { - final t = _TableNames(table, _scope); + final t = _TableNames(table, _scope, _dbScope); t.addFiltersAndOrderings(_addedTables, leaf); tableNames.add(t); } @@ -477,7 +477,7 @@ class ManagerWriter { ? _scope.drift("GeneratedDatabase") : _dbClassName); tableManagerGetters.writeln( - "${table.rootTableManager} get ${table.tableGetterName} => ${table.rootTableManager}(_db, _db.${table.tableGetterName});"); + "${table.rootTableManager} get ${table.table.dbGetterName} => ${table.rootTableManager}(_db, _db.${table.table.dbGetterName});"); } leaf.write(""" From 091469a498c79e08097546c931f8e251bac855cf Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 2 Apr 2024 14:55:36 -0400 Subject: [PATCH 27/74] remove print --- drift_dev/lib/src/writer/manager.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 7c4ffea0..8d294937 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -256,7 +256,6 @@ class _TableNames { } void _writeProcessedTableManager(TextEmitter leaf, String dbClassName) { - print(table.entityInfoName); leaf ..write('class $processedTableManager extends ') ..writeDriftRef('ProcessedTableManager') From a439b67ebb24e11e6fa27089678cb5132e448a59 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 2 Apr 2024 16:50:47 -0400 Subject: [PATCH 28/74] Add Reference Name --- drift/lib/src/dsl/columns.dart | 31 +++++++++++++++++++ .../src/analysis/resolver/dart/column.dart | 17 ++++++++++ .../lib/src/analysis/results/column.dart | 5 +++ drift_dev/lib/src/writer/manager.dart | 4 ++- 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/drift/lib/src/dsl/columns.dart b/drift/lib/src/dsl/columns.dart index 000e948e..f85c51d2 100644 --- a/drift/lib/src/dsl/columns.dart +++ b/drift/lib/src/dsl/columns.dart @@ -409,3 +409,34 @@ class JsonKey { /// generated json. See the documentation for [JsonKey] for details. const JsonKey(this.key); } + +/// Annotation to use on reference columns inside of a [Table] to define the name +/// of the filters and orderings for the reverse relation. +/// +/// Example: +/// ```dart +/// class TodoEntries extends Table { +/// IntColumn get id => integer().autoIncrement()(); +/// TextColumn get body => text()(); +/// @ReferenceName("categories") +/// IntColumn get category => integer().nullable().references(Categories, #id)(); +/// } +/// class Categories extends Table { +/// IntColumn get id => integer().autoIncrement()(); +/// } +/// /// Filter all Categories which contain a TodoEntries with a body of "Todo" +/// categories.filter((f) => f.categories((f) => f.body("Todo"))) +/// +/// ``` +/// When these aren't specified, drift will use the referenced tables name followed by `Refs`. +/// If a reference name clashes with other fields on the table, the generator will show a warning, +/// and filters and orderings wont be generated +class ReferenceName { + /// The name that this reference will use when generating filters and ordering in the reverse direction + /// for [ReferenceName] for details. + final String name; + + /// Annotation to use on reference columns inside of a [Table] to define the name + /// of the filters and orderings for the reverse relation. + const ReferenceName(this.name); +} diff --git a/drift_dev/lib/src/analysis/resolver/dart/column.dart b/drift_dev/lib/src/analysis/resolver/dart/column.dart index ef9b9e4a..68e74608 100644 --- a/drift_dev/lib/src/analysis/resolver/dart/column.dart +++ b/drift_dev/lib/src/analysis/resolver/dart/column.dart @@ -472,6 +472,7 @@ class ColumnParser { documentationComment: docString, constraints: foundConstraints, customConstraints: foundCustomConstraint, + referenceName: _readJsonKey(element), ), referencesColumnInSameTable: referencesColumnInSameTable, ); @@ -507,6 +508,22 @@ class ColumnParser { return object.computeConstantValue()!.getField('key')!.toStringValue(); } + String? _readReferenceName(Element getter) { + final annotations = getter.metadata; + final object = annotations.firstWhereOrNull((e) { + final value = e.computeConstantValue(); + final valueType = value?.type; + + return valueType is InterfaceType && + isFromDrift(valueType) && + valueType.element.name == 'ReferenceName'; + }); + + if (object == null) return null; + + return object.computeConstantValue()!.getField('name')!.toStringValue(); + } + Future> _driftConstraintsFromCustomConstraints({ required bool isNullable, String? customConstraints, diff --git a/drift_dev/lib/src/analysis/results/column.dart b/drift_dev/lib/src/analysis/results/column.dart index bbd150aa..14f23d35 100644 --- a/drift_dev/lib/src/analysis/results/column.dart +++ b/drift_dev/lib/src/analysis/results/column.dart @@ -69,6 +69,10 @@ class DriftColumn implements HasType { /// set. final AnnotatedDartCode? clientDefaultCode; + /// If this column references another column, this column will contain the name + /// that the filters and orderings should use to denote the reverse direction + final String? referenceName; + @override final AppliedTypeConverter? typeConverter; @@ -92,6 +96,7 @@ class DriftColumn implements HasType { this.documentationComment, this.constraints = const [], this.customConstraints, + this.referenceName, bool foreignConverter = false, }) { if (typeConverter != null && !foreignConverter) { diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 8d294937..222aa760 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -396,9 +396,11 @@ class _TableNames { // If we are referencing the current table, add a back ref if (referencedTableNames.table.entityInfoName == table.entityInfoName) { + final backRefName = referencedCol.referenceName ?? + "${otherTableNames.table.dbGetterName}Refs"; backRefFilters.add(_ReferencedFilter( referencedColumnNames.fieldGetter, - "${otherTableNames.table.dbGetterName}Refs", + backRefName, otherTableNames, referencedColumnNames)); } From 50811336334293b94d5d8aa86bdf0794caf5e78e Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 00:55:36 -0400 Subject: [PATCH 29/74] fix refernced name --- .../src/analysis/resolver/dart/column.dart | 3 +- drift_dev/lib/src/analysis/serializer.dart | 2 + drift_dev/lib/src/writer/manager.dart | 426 ++++++++++-------- 3 files changed, 251 insertions(+), 180 deletions(-) diff --git a/drift_dev/lib/src/analysis/resolver/dart/column.dart b/drift_dev/lib/src/analysis/resolver/dart/column.dart index 68e74608..9f92dd3b 100644 --- a/drift_dev/lib/src/analysis/resolver/dart/column.dart +++ b/drift_dev/lib/src/analysis/resolver/dart/column.dart @@ -457,7 +457,6 @@ class ColumnParser { customConstraints: foundCustomConstraint, sourceForCustomConstraints: customConstraintSource, )); - return PendingColumnInformation( DriftColumn( sqlType: columnType, @@ -472,7 +471,7 @@ class ColumnParser { documentationComment: docString, constraints: foundConstraints, customConstraints: foundCustomConstraint, - referenceName: _readJsonKey(element), + referenceName: _readReferenceName(element), ), referencesColumnInSameTable: referencesColumnInSameTable, ); diff --git a/drift_dev/lib/src/analysis/serializer.dart b/drift_dev/lib/src/analysis/serializer.dart index aba81a2f..79acd69e 100644 --- a/drift_dev/lib/src/analysis/serializer.dart +++ b/drift_dev/lib/src/analysis/serializer.dart @@ -226,6 +226,7 @@ class ElementSerializer { 'clientDefaultCode': column.clientDefaultCode?.toJson(), 'defaultArgument': column.defaultArgument?.toJson(), 'overriddenJsonName': column.overriddenJsonName, + 'referenceName': column.referenceName, 'documentationComment': column.documentationComment, 'constraints': [ for (final constraint in column.constraints) @@ -773,6 +774,7 @@ class ElementDeserializer { ? AnnotatedDartCode.fromJson(json['defaultArgument'] as Map) : null, overriddenJsonName: json['overriddenJsonName'] as String?, + referenceName: json['referenceName'] as String?, documentationComment: json['documentationComment'] as String?, constraints: [ for (final rawConstraint in json['constraints'] as List) diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 222aa760..8cccc440 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -3,21 +3,32 @@ import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/writer.dart'; abstract class _Filter { - final String filterName; - _Filter( - this.filterName, - ); + /// The getter for the column on this table + /// + /// E.G `id` + final String fieldGetter; - void writeFilter(TextEmitter leaf, bool isModular); + /// The getter for the columns filter + /// + /// E.G `id` + final String filterName; + + /// Abstract class for all filters + _Filter(this.filterName, {required this.fieldGetter}); + + void writeFilter(TextEmitter leaf); } class _RegularFilter extends _Filter { - final String fieldGetter; + /// The type that this filter is for final String type; - _RegularFilter(this.fieldGetter, this.type, super.filterName); + + /// A class for regular filters + _RegularFilter(super.filterName, + {required super.fieldGetter, required this.type}); @override - void writeFilter(TextEmitter leaf, bool isModular) { + void writeFilter(TextEmitter leaf) { leaf ..writeDriftRef("ColumnFilters") ..write("<$type> get $filterName =>") @@ -27,14 +38,20 @@ class _RegularFilter extends _Filter { } class _FilterWithConverter extends _Filter { - final String fieldGetter; + /// The type that this filter is for final String type; + + /// The type of the converter final String converterType; - _FilterWithConverter( - this.fieldGetter, this.type, super.filterName, this.converterType); + + /// A class for filters with converters + _FilterWithConverter(super.filterName, + {required super.fieldGetter, + required this.type, + required this.converterType}); @override - void writeFilter(TextEmitter leaf, bool isModular) { + void writeFilter(TextEmitter leaf) { leaf ..writeDriftRef("ColumnWithTypeConverterFilters") ..write("<$converterType,$type> get $filterName =>") @@ -48,49 +65,77 @@ class _FilterWithConverter extends _Filter { } } +/// A class for filters that reference other tables class _ReferencedFilter extends _Filter { - final String fieldGetter; - final _TableNames referencedTable; - final _ColumnNames referencedColumn; - _ReferencedFilter(this.fieldGetter, super.filterName, this.referencedTable, - this.referencedColumn); + /// The full function used to get the referenced table + /// + /// E.G `state.db.resultSet<$CategoryTable>('categories')` + /// or `state.db.categories` + final String referencedTableField; + + /// The getter for the column on the referenced table + /// + /// E.G `id` + final String referencedColumnGetter; + + /// The name of the referenced table's filter composer + /// + /// E.G `CategoryFilterComposer` + final String referencedFilterComposer; + + _ReferencedFilter( + super.filterName, { + required this.referencedTableField, + required this.referencedColumnGetter, + required this.referencedFilterComposer, + required super.fieldGetter, + }); @override - void writeFilter(TextEmitter leaf, bool isModular) { - final String referencedTableGetter = isModular - ? "state.db.resultSet<${referencedTable.tableClassName}>('${referencedTable.table.schemaName}')" - : "state.db.${referencedTable.table.dbGetterName}"; + void writeFilter(TextEmitter leaf) { leaf ..writeDriftRef("ComposableFilter") ..write(" $filterName(") ..writeDriftRef("ComposableFilter") - ..writeln(" Function( ${referencedTable.filterComposer} f) f) {") + ..writeln(" Function( $referencedFilterComposer f) f) {") ..write(''' return referenced( - referencedTable: $referencedTableGetter, + referencedTable: $referencedTableField, getCurrentColumn: (f) => f.$fieldGetter, - getReferencedColumn: (f) => f.${referencedColumn.fieldGetter}, - getReferencedComposer: (db, table) => ${referencedTable.filterComposer}(db, table), + getReferencedColumn: (f) => f.$referencedColumnGetter, + getReferencedComposer: (db, table) => $referencedFilterComposer(db, table), builder: f); }'''); } } abstract class _Ordering { + /// The getter for the column on this table + /// + /// E.G `id` + final String fieldGetter; + + /// The getter for the columns ordering + /// + /// E.G `id` final String orderingName; - _Ordering(this.orderingName); + /// Abstract class for all orderings + _Ordering(this.orderingName, {required this.fieldGetter}); - void writeOrdering(TextEmitter leaf, bool isModular); + void writeOrdering(TextEmitter leaf); } class _RegularOrdering extends _Ordering { - final String fieldGetter; + /// The type that this ordering is for final String type; - _RegularOrdering(this.fieldGetter, this.type, super.orderingName); + + /// A class for regular orderings + _RegularOrdering(super.orderingName, + {required super.fieldGetter, required this.type}); @override - void writeOrdering(TextEmitter leaf, bool isModular) { + void writeOrdering(TextEmitter leaf) { leaf ..writeDriftRef("ColumnOrderings") ..write(" get $orderingName =>") @@ -100,29 +145,41 @@ class _RegularOrdering extends _Ordering { } class _ReferencedOrdering extends _Ordering { - final String fieldGetter; - final _TableNames referencedTable; - final _ColumnNames referencedColumn; - _ReferencedOrdering(this.fieldGetter, super.orderingName, - this.referencedTable, this.referencedColumn); + /// The full function used to get the referenced table + /// + /// E.G `state.db.resultSet<$CategoryTable>('categories')` + /// or `state.db.categories` + final String referencedTableField; + + /// The getter for the column on the referenced table + /// + /// E.G `id` + final String referencedColumnGetter; + + /// The name of the referenced table's ordering composer + /// + /// E.G `CategoryOrderingComposer` + final String referencedOrderingComposer; + + _ReferencedOrdering(super.orderingName, + {required this.referencedTableField, + required this.referencedColumnGetter, + required this.referencedOrderingComposer, + required super.fieldGetter}); @override - void writeOrdering(TextEmitter leaf, bool isModular) { - final String referencedTableGetter = isModular - ? "state.db.resultSet<${referencedTable.tableClassName}>('${referencedTable.table.schemaName}')" - : "state.db.${referencedTable.table.dbGetterName}"; - + void writeOrdering(TextEmitter leaf) { leaf ..writeDriftRef("ComposableOrdering") ..write(" $orderingName(") ..writeDriftRef("ComposableOrdering") - ..writeln(" Function( ${referencedTable.orderingComposer} o) o) {") + ..writeln(" Function( $referencedOrderingComposer o) o) {") ..writeln(''' return referenced( - referencedTable: $referencedTableGetter, + referencedTable: $referencedTableField, getCurrentColumn: (f) => f.$fieldGetter, - getReferencedColumn: (f) => f.${referencedColumn.fieldGetter}, - getReferencedComposer: (db, table) => ${referencedTable.orderingComposer}(db, table), + getReferencedColumn: (f) => f.$referencedColumnGetter, + getReferencedComposer: (db, table) => $referencedOrderingComposer(db, table), builder: o); }'''); } @@ -135,7 +192,9 @@ class _ColumnNames { final String fieldGetter; final List<_Filter> filters; final List<_Ordering> orderings; - _ColumnNames(this.fieldGetter, this.filters, this.orderings); + _ColumnNames(this.fieldGetter) + : filters = [], + orderings = []; } class _TableNames { @@ -151,53 +210,50 @@ class _TableNames { /// The name of the filter composer class /// /// E.G `UserFilterComposer` - final String filterComposer; + String get filterComposer => '\$${table.entityInfoName}FilterComposer'; /// The name of the filter composer class /// /// E.G `UserOrderingComposer` - final String orderingComposer; + String get orderingComposer => '\$${table.entityInfoName}OrderingComposer'; /// The name of the processed table manager class /// /// E.G `UserProcessedTableManager` - final String processedTableManager; - - /// The name of the table manager with filtering class - /// - /// E.G `UserTableManagerWithFiltering` - final String tableManagerWithFiltering; - - /// The name of the table manager with ordering class - /// - /// E.G `UserTableManagerWithOrdering` - final String tableManagerWithOrdering; + String get processedTableManager => + '\$${table.entityInfoName}ProcessedTableManager'; /// The name of the root table manager class /// /// E.G `UserTableManager` - final String rootTableManager; + String get rootTableManager => '\$${table.entityInfoName}TableManager'; /// Name of the typedef for the insertCompanionBuilder /// /// E.G. `insertCompanionBuilder` - final String insertCompanionBuilderTypeDefName; - - /// Table class name, this may be different from the entity name - /// if modular generation is enabled - /// E.G. `i5.$CategoriesTable` - String get tableClassName { - return dbScope.dartCode(dbScope.entityInfoType(table)); - } - - String get rowClassName { - return dbScope.dartCode(dbScope.writer.rowType(table)); - } + String get insertCompanionBuilderTypeDefName => + '\$${table.entityInfoName}InsertCompanionBuilder'; /// Name of the arguments for the updateCompanionBuilder /// /// E.G. `updateCompanionBuilderTypeDef` - final String updateCompanionBuilderTypeDefName; + String get updateCompanionBuilderTypeDefName => + '\$${table.entityInfoName}UpdateCompanionBuilder'; + + /// Table class name, this may be different from the entity name + /// if modular generation is enabled + /// E.G. `i5.$CategoriesTable` + String get tableClassName => dbScope.dartCode(dbScope.entityInfoType(table)); + + /// Row class name, this may be different from the entity name + /// if modular generation is enabled + /// E.G. `i5.$Category` + String get rowClassName => dbScope.dartCode(dbScope.writer.rowType(table)); + + /// The name of the database class + /// + /// E.G. `i5.$GeneratedDatabase` + final String databaseGenericName; /// Columns with their names, filters and orderings final List<_ColumnNames> columns; @@ -205,67 +261,53 @@ class _TableNames { /// Filters for back references final List<_ReferencedFilter> backRefFilters; - _TableNames(this.table, this.scope, this.dbScope) - : filterComposer = '\$${table.entityInfoName}FilterComposer', - orderingComposer = '\$${table.entityInfoName}OrderingComposer', - processedTableManager = - '\$${table.entityInfoName}ProcessedTableManager', - tableManagerWithFiltering = - '\$${table.entityInfoName}TableManagerWithFiltering', - tableManagerWithOrdering = - '\$${table.entityInfoName}TableManagerWithOrdering', - rootTableManager = '\$${table.entityInfoName}TableManager', - insertCompanionBuilderTypeDefName = - '\$${table.entityInfoName}InsertCompanionBuilder', - updateCompanionBuilderTypeDefName = - '\$${table.entityInfoName}UpdateCompanionBuilder', - backRefFilters = [], + _TableNames(this.table, this.scope, this.dbScope, this.databaseGenericName) + : backRefFilters = [], columns = []; - void _writeFilterComposer(TextEmitter leaf, String dbClassName) { - // Write the FilterComposer + void _writeFilterComposer(TextEmitter leaf) { leaf ..write('class $filterComposer extends ') ..writeDriftRef('FilterComposer') - ..writeln('<$dbClassName,$tableClassName> {') + ..writeln('<$databaseGenericName,$tableClassName> {') ..writeln('$filterComposer(super.db, super.table);'); for (var c in columns) { for (var f in c.filters) { - f.writeFilter(leaf, scope.generationOptions.isModular); + f.writeFilter(leaf); } } for (var f in backRefFilters) { - f.writeFilter(leaf, scope.generationOptions.isModular); + f.writeFilter(leaf); } leaf.writeln('}'); } - void _writeOrderingComposer(TextEmitter leaf, String dbClassName) { + void _writeOrderingComposer(TextEmitter leaf) { // Write the OrderingComposer leaf ..write('class $orderingComposer extends ') ..writeDriftRef('OrderingComposer') - ..writeln('<$dbClassName,$tableClassName> {') + ..writeln('<$databaseGenericName,$tableClassName> {') ..writeln('$orderingComposer(super.db, super.table);'); for (var c in columns) { for (var o in c.orderings) { - o.writeOrdering(leaf, scope.generationOptions.isModular); + o.writeOrdering(leaf); } } leaf.writeln('}'); } - void _writeProcessedTableManager(TextEmitter leaf, String dbClassName) { + void _writeProcessedTableManager(TextEmitter leaf) { leaf ..write('class $processedTableManager extends ') ..writeDriftRef('ProcessedTableManager') ..writeln( - '<$dbClassName,${tableClassName},${rowClassName},$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') + '<$databaseGenericName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') ..writeln('const $processedTableManager(super.state);') ..writeln('}'); } - void _writeRootTable(TextEmitter leaf, String dbClassName) { + void _writeRootTable(TextEmitter leaf) { final companionClassName = leaf.dartCode(leaf.companionType(table)); final updateCompanionBuilderTypeDef = StringBuffer( @@ -318,8 +360,9 @@ class _TableNames { ..write('class $rootTableManager extends ') ..writeDriftRef('RootTableManager') ..writeln( - '<$dbClassName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') - ..writeln('$rootTableManager($dbClassName db, $tableClassName table)') + '<$databaseGenericName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') + ..writeln( + '$rootTableManager($databaseGenericName db, $tableClassName table)') ..writeln(": super(") ..writeDriftRef("TableManagerState") ..write( @@ -329,85 +372,105 @@ class _TableNames { ..writeln('}'); } - void writeManager(TextEmitter leaf, String dbClassName) { - _writeFilterComposer(leaf, dbClassName); - _writeOrderingComposer(leaf, dbClassName); - _writeProcessedTableManager(leaf, dbClassName); - _writeRootTable(leaf, dbClassName); + void writeManager(TextEmitter leaf) { + _writeFilterComposer(leaf); + _writeOrderingComposer(leaf); + _writeProcessedTableManager(leaf); + _writeRootTable(leaf); } - void addFiltersAndOrderings(List tables, TextEmitter leaf) { - /// First add the filters and orderings for the columns - /// of the current table - for (var column in table.columns) { - final c = _ColumnNames(column.nameInDart, [], []); - final innerColumnType = - leaf.dartCode(leaf.innerColumnType(column.sqlType)); - if (column.typeConverter != null) { - final mappedType = leaf.dartCode(leaf.writer.dartType(column)); - c.filters.add(_FilterWithConverter( - c.fieldGetter, innerColumnType, c.fieldGetter, mappedType)); - } else { - c.filters - .add(_RegularFilter(c.fieldGetter, innerColumnType, c.fieldGetter)); - } - c.orderings - .add(_RegularOrdering(c.fieldGetter, innerColumnType, c.fieldGetter)); - + void addFiltersAndOrderings(List tables) { + // Utility function to get the referenced table and column + (DriftTable, DriftColumn)? getReferencedTableAndColumn( + DriftColumn column, List tables) { final referencedCol = column.constraints .whereType() .firstOrNull ?.otherColumn; - final referencedTable = referencedCol?.owner; - if (referencedCol != null && referencedTable is DriftTable) { + if (referencedCol != null && referencedCol.owner is DriftTable) { + final referencedTable = tables.firstWhere( + (t) => t.entityInfoName == referencedCol.owner.entityInfoName); + return (referencedTable, referencedCol); + } + return null; + } + + /// First add the filters and orderings for the columns + /// of the current table + for (var column in table.columns) { + final c = _ColumnNames(column.nameInDart); + + // The type that this column is (int, string, etc) + final innerColumnType = + scope.dartCode(scope.innerColumnType(column.sqlType)); + + // If the column has a type converter, add a filter with a converter + if (column.typeConverter != null) { + final converterType = scope.dartCode(scope.writer.dartType(column)); + c.filters.add(_FilterWithConverter(c.fieldGetter, + converterType: converterType, + fieldGetter: c.fieldGetter, + type: innerColumnType)); + } else { + c.filters.add(_RegularFilter(c.fieldGetter, + type: innerColumnType, fieldGetter: c.fieldGetter)); + } + + // Add the ordering for the column + c.orderings.add(_RegularOrdering(c.fieldGetter, + type: innerColumnType, fieldGetter: c.fieldGetter)); + + /// If this column is a foreign key to another table, add a filter and ordering + /// for the referenced table + final referenced = getReferencedTableAndColumn(column, tables); + if (referenced != null) { + final (referencedTable, referencedCol) = referenced; final referencedTableNames = - _TableNames(referencedTable, scope, dbScope); - final referencedColumnNames = - _ColumnNames(referencedCol.nameInDart, [], []); - c.filters.add(_ReferencedFilter(c.fieldGetter, "${c.fieldGetter}Ref", - referencedTableNames, referencedColumnNames)); - c.orderings.add(_ReferencedOrdering( - c.fieldGetter, - "${c.fieldGetter}OrderBy", - referencedTableNames, - referencedColumnNames)); + _TableNames(referencedTable, scope, dbScope, databaseGenericName); + final referencedColumnNames = _ColumnNames(referencedCol.nameInDart); + final String referencedTableField = scope.generationOptions.isModular + ? "state.db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" + : "state.db.${referencedTable.dbGetterName}"; + + c.filters.add(_ReferencedFilter("${c.fieldGetter}Ref", + fieldGetter: c.fieldGetter, + referencedColumnGetter: referencedColumnNames.fieldGetter, + referencedFilterComposer: referencedTableNames.filterComposer, + referencedTableField: referencedTableField)); + c.orderings.add(_ReferencedOrdering("${c.fieldGetter}Ref", + fieldGetter: c.fieldGetter, + referencedColumnGetter: referencedColumnNames.fieldGetter, + referencedOrderingComposer: referencedTableNames.orderingComposer, + referencedTableField: referencedTableField)); } columns.add(c); } - for (var otherTable in tables) { - final otherTableNames = _TableNames(otherTable, scope, dbScope); - /// We are adding backrefs now, skip the current table - if (otherTableNames.table.entityInfoName == table.entityInfoName) { - continue; - } - for (var otherColumn in otherTable.columns) { - final referencedCol = otherColumn.constraints - .whereType() - .firstOrNull - ?.otherColumn; - final referencedTable = referencedCol?.owner; - if (referencedCol != null && referencedTable is DriftTable) { + // Iterate over all other tables to find back references + for (var ot in tables) { + for (var oc in ot.columns) { + // Check if the column is a foreign key to the current table + final reference = getReferencedTableAndColumn(oc, tables); + if (reference != null && + reference.$1.entityInfoName == table.entityInfoName) { final referencedTableNames = - _TableNames(referencedTable, scope, dbScope); - final referencedColumnNames = - _ColumnNames(referencedCol.nameInDart, [], []); + _TableNames(ot, scope, dbScope, databaseGenericName); + final referencedColumnNames = _ColumnNames(oc.nameInDart); + final String referencedTableField = scope.generationOptions.isModular + ? "state.db.resultSet<${referencedTableNames.tableClassName}>('${ot.schemaName}')" + : "state.db.${ot.dbGetterName}"; - // If we are referencing the current table, add a back ref - if (referencedTableNames.table.entityInfoName == - table.entityInfoName) { - final backRefName = referencedCol.referenceName ?? - "${otherTableNames.table.dbGetterName}Refs"; - backRefFilters.add(_ReferencedFilter( - referencedColumnNames.fieldGetter, - backRefName, - otherTableNames, - referencedColumnNames)); - } + final filterName = oc.referenceName ?? + "${referencedTableNames.table.dbGetterName}Refs"; + + backRefFilters.add(_ReferencedFilter(filterName, + fieldGetter: reference.$2.nameInDart, + referencedColumnGetter: referencedColumnNames.fieldGetter, + referencedFilterComposer: referencedTableNames.filterComposer, + referencedTableField: referencedTableField)); } } } - // Remove the filters and orderings that have duplicates // TODO: Add warnings for duplicate filters and orderings List duplicates(List items) { @@ -421,6 +484,7 @@ class _TableNames { return duplicates; } + // Gather which filters and orderings are duplicates final filterNamesToRemove = duplicates(columns .map((e) => e.filters.map((e) => e.filterName)) .expand((e) => e) @@ -430,6 +494,8 @@ class _TableNames { .map((e) => e.orderings.map((e) => e.orderingName)) .expand((e) => e) .toList()); + + // Remove the duplicates for (var c in columns) { c.filters.removeWhere((e) => filterNamesToRemove.contains(e.filterName)); c.orderings @@ -450,42 +516,46 @@ class ManagerWriter { _addedTables = []; } - String get managerGetter { - return '$_dbMangerName get managers => $_dbMangerName(this);'; - } - void addTable(DriftTable table) { _addedTables.add(table); } - String get _dbMangerName => '${_dbClassName}Manager'; + String get databaseGenericName { + if (_scope.generationOptions.isModular) { + return _scope.drift("GeneratedDatabase"); + } else { + return _dbClassName; + } + } + + String get databaseManagerName => '${_dbClassName}Manager'; + + String get managerGetter { + return '$databaseManagerName get managers => $databaseManagerName(this);'; + } void write() { final leaf = _scope.leaf(); final tableNames = <_TableNames>[]; + for (var table in _addedTables) { - final t = _TableNames(table, _scope, _dbScope); - t.addFiltersAndOrderings(_addedTables, leaf); - tableNames.add(t); + tableNames.add(_TableNames(table, _scope, _dbScope, databaseGenericName) + ..addFiltersAndOrderings(_addedTables)); } final tableManagerGetters = StringBuffer(); for (var table in tableNames) { - table.writeManager( - leaf, - _scope.generationOptions.isModular - ? _scope.drift("GeneratedDatabase") - : _dbClassName); + table.writeManager(leaf); tableManagerGetters.writeln( "${table.rootTableManager} get ${table.table.dbGetterName} => ${table.rootTableManager}(_db, _db.${table.table.dbGetterName});"); } leaf.write(""" -class $_dbMangerName{ +class $databaseManagerName{ final $_dbClassName _db; - $_dbMangerName(this._db); + $databaseManagerName(this._db); $tableManagerGetters } From f105cdf62e03c6480e3148634b9f0f10c0d99924 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 01:00:58 -0400 Subject: [PATCH 30/74] rename refs --- drift_dev/lib/src/writer/manager.dart | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager.dart index 8cccc440..4974dcd3 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager.dart @@ -404,6 +404,10 @@ class _TableNames { final innerColumnType = scope.dartCode(scope.innerColumnType(column.sqlType)); + // Get the referenced table and column if this column is a foreign key + final referenced = getReferencedTableAndColumn(column, tables); + final isForeignKey = referenced != null; + // If the column has a type converter, add a filter with a converter if (column.typeConverter != null) { final converterType = scope.dartCode(scope.writer.dartType(column)); @@ -412,17 +416,21 @@ class _TableNames { fieldGetter: c.fieldGetter, type: innerColumnType)); } else { - c.filters.add(_RegularFilter(c.fieldGetter, - type: innerColumnType, fieldGetter: c.fieldGetter)); + c.filters.add(_RegularFilter( + c.fieldGetter + (isForeignKey ? "Value" : ""), + type: innerColumnType, + fieldGetter: c.fieldGetter)); } // Add the ordering for the column - c.orderings.add(_RegularOrdering(c.fieldGetter, - type: innerColumnType, fieldGetter: c.fieldGetter)); + c.orderings.add(_RegularOrdering( + c.fieldGetter + (isForeignKey ? "Value" : ""), + type: innerColumnType, + fieldGetter: c.fieldGetter)); /// If this column is a foreign key to another table, add a filter and ordering /// for the referenced table - final referenced = getReferencedTableAndColumn(column, tables); + if (referenced != null) { final (referencedTable, referencedCol) = referenced; final referencedTableNames = @@ -432,12 +440,12 @@ class _TableNames { ? "state.db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" : "state.db.${referencedTable.dbGetterName}"; - c.filters.add(_ReferencedFilter("${c.fieldGetter}Ref", + c.filters.add(_ReferencedFilter(c.fieldGetter, fieldGetter: c.fieldGetter, referencedColumnGetter: referencedColumnNames.fieldGetter, referencedFilterComposer: referencedTableNames.filterComposer, referencedTableField: referencedTableField)); - c.orderings.add(_ReferencedOrdering("${c.fieldGetter}Ref", + c.orderings.add(_ReferencedOrdering(c.fieldGetter, fieldGetter: c.fieldGetter, referencedColumnGetter: referencedColumnNames.fieldGetter, referencedOrderingComposer: referencedTableNames.orderingComposer, From 242ec4d68623f3cbe1ff8420287243f7ccb3427f Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 12:37:37 -0400 Subject: [PATCH 31/74] Refactor --- drift_dev/lib/src/writer/database_writer.dart | 2 +- .../{manager.dart => manager_writer.dart} | 291 ++++++++++-------- 2 files changed, 162 insertions(+), 131 deletions(-) rename drift_dev/lib/src/writer/{manager.dart => manager_writer.dart} (68%) diff --git a/drift_dev/lib/src/writer/database_writer.dart b/drift_dev/lib/src/writer/database_writer.dart index 5b812d39..48bb6223 100644 --- a/drift_dev/lib/src/writer/database_writer.dart +++ b/drift_dev/lib/src/writer/database_writer.dart @@ -1,7 +1,7 @@ import 'package:drift/drift.dart' as drift; // ignore: implementation_imports import 'package:drift/src/runtime/executor/stream_queries.dart'; -import 'package:drift_dev/src/writer/manager.dart'; +import 'package:drift_dev/src/writer/manager_writer.dart'; import 'package:drift_dev/src/writer/utils/memoized_getter.dart'; import 'package:recase/recase.dart'; diff --git a/drift_dev/lib/src/writer/manager.dart b/drift_dev/lib/src/writer/manager_writer.dart similarity index 68% rename from drift_dev/lib/src/writer/manager.dart rename to drift_dev/lib/src/writer/manager_writer.dart index 4974dcd3..dbd24e16 100644 --- a/drift_dev/lib/src/writer/manager.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -2,29 +2,32 @@ import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/writer.dart'; -abstract class _Filter { +abstract class _FilterWriter { /// The getter for the column on this table /// - /// E.G `id` + /// E.G `id` in `table.id` final String fieldGetter; /// The getter for the columns filter /// - /// E.G `id` + /// E.G `id` in `f.id.equals(5)` final String filterName; - /// Abstract class for all filters - _Filter(this.filterName, {required this.fieldGetter}); + /// An abstract class for all filters + _FilterWriter(this.filterName, {required this.fieldGetter}); + /// Write the filter to a provider [TextEmitter] void writeFilter(TextEmitter leaf); } -class _RegularFilter extends _Filter { - /// The type that this filter is for +class _RegularFilterWriter extends _FilterWriter { + /// The type that this column is + /// + /// E.G `int`, `String`, etc final String type; - /// A class for regular filters - _RegularFilter(super.filterName, + /// A class used for writing `ColumnFilters` with regular types + _RegularFilterWriter(super.filterName, {required super.fieldGetter, required this.type}); @override @@ -37,15 +40,19 @@ class _RegularFilter extends _Filter { } } -class _FilterWithConverter extends _Filter { - /// The type that this filter is for +class _FilterWithConverterWriter extends _FilterWriter { + /// The type that this column is + /// + /// E.G `int`, `String`, etc final String type; - /// The type of the converter + /// The type of the user provided converter + /// + /// E.G `Color` etc final String converterType; - /// A class for filters with converters - _FilterWithConverter(super.filterName, + /// A class used for writing `ColumnFilters` with custom converters + _FilterWithConverterWriter(super.filterName, {required super.fieldGetter, required this.type, required this.converterType}); @@ -57,16 +64,10 @@ class _FilterWithConverter extends _Filter { ..write("<$converterType,$type> get $filterName =>") ..writeDriftRef("ColumnWithTypeConverterFilters") ..writeln("(state.table.$fieldGetter);"); - leaf - ..writeDriftRef("ColumnFilters") - ..write("<$type> get ${filterName}Value =>") - ..writeDriftRef("ColumnFilters") - ..writeln("(state.table.$fieldGetter);"); } } -/// A class for filters that reference other tables -class _ReferencedFilter extends _Filter { +class _ReferencedFilter extends _FilterWriter { /// The full function used to get the referenced table /// /// E.G `state.db.resultSet<$CategoryTable>('categories')` @@ -75,7 +76,7 @@ class _ReferencedFilter extends _Filter { /// The getter for the column on the referenced table /// - /// E.G `id` + /// E.G `id` in `table.id` final String referencedColumnGetter; /// The name of the referenced table's filter composer @@ -83,6 +84,7 @@ class _ReferencedFilter extends _Filter { /// E.G `CategoryFilterComposer` final String referencedFilterComposer; + /// A class used for building filters for referenced tables _ReferencedFilter( super.filterName, { required this.referencedTableField, @@ -109,29 +111,32 @@ return referenced( } } -abstract class _Ordering { +abstract class _OrderingWriter { /// The getter for the column on this table /// - /// E.G `id` + /// E.G `id` in `table.id` final String fieldGetter; /// The getter for the columns ordering /// - /// E.G `id` + /// E.G `id` in `f.id.equals(5)` final String orderingName; /// Abstract class for all orderings - _Ordering(this.orderingName, {required this.fieldGetter}); + _OrderingWriter(this.orderingName, {required this.fieldGetter}); + /// Write the ordering to a provider [TextEmitter] void writeOrdering(TextEmitter leaf); } -class _RegularOrdering extends _Ordering { - /// The type that this ordering is for +class _RegularOrderingWriter extends _OrderingWriter { + /// The type that this column is + /// + /// E.G `int`, `String`, etc final String type; - /// A class for regular orderings - _RegularOrdering(super.orderingName, + /// A class used for writing `ColumnOrderings` with regular types + _RegularOrderingWriter(super.orderingName, {required super.fieldGetter, required this.type}); @override @@ -144,7 +149,7 @@ class _RegularOrdering extends _Ordering { } } -class _ReferencedOrdering extends _Ordering { +class _ReferencedOrderingWriter extends _OrderingWriter { /// The full function used to get the referenced table /// /// E.G `state.db.resultSet<$CategoryTable>('categories')` @@ -153,7 +158,7 @@ class _ReferencedOrdering extends _Ordering { /// The getter for the column on the referenced table /// - /// E.G `id` + /// E.G `id` in `table.id` final String referencedColumnGetter; /// The name of the referenced table's ordering composer @@ -161,7 +166,8 @@ class _ReferencedOrdering extends _Ordering { /// E.G `CategoryOrderingComposer` final String referencedOrderingComposer; - _ReferencedOrdering(super.orderingName, + /// A class used for building orderings for referenced tables + _ReferencedOrderingWriter(super.orderingName, {required this.referencedTableField, required this.referencedColumnGetter, required this.referencedOrderingComposer, @@ -185,19 +191,25 @@ return referenced( } } -class _ColumnNames { +class _ColumnWriter { /// The getter for the field /// - /// E.G `id` + /// E.G `id` in `table.id` final String fieldGetter; - final List<_Filter> filters; - final List<_Ordering> orderings; - _ColumnNames(this.fieldGetter) + + /// List of filters for this column + final List<_FilterWriter> filters; + + /// List of orderings for this column + final List<_OrderingWriter> orderings; + + /// A class used for writing filters and orderings for columns + _ColumnWriter(this.fieldGetter) : filters = [], orderings = []; } -class _TableNames { +class _TableWriter { /// The current table final DriftTable table; @@ -255,13 +267,13 @@ class _TableNames { /// E.G. `i5.$GeneratedDatabase` final String databaseGenericName; - /// Columns with their names, filters and orderings - final List<_ColumnNames> columns; + /// Writers for the columns of this table + final List<_ColumnWriter> columns; /// Filters for back references final List<_ReferencedFilter> backRefFilters; - _TableNames(this.table, this.scope, this.dbScope, this.databaseGenericName) + _TableWriter(this.table, this.scope, this.dbScope, this.databaseGenericName) : backRefFilters = [], columns = []; @@ -307,52 +319,66 @@ class _TableNames { ..writeln('}'); } - void _writeRootTable(TextEmitter leaf) { - final companionClassName = leaf.dartCode(leaf.companionType(table)); + /// Build the builder for a companion class + /// This is used to build the insert and update companions + /// Returns a tuple with the typedef and the builder + /// Use [isUpdate] to determine if the builder is for an update or insert companion + (String, String) _companionBuilder(String typedefName, + {required bool isUpdate}) { + final companionClassName = scope.dartCode(scope.companionType(table)); - final updateCompanionBuilderTypeDef = StringBuffer( - 'typedef $updateCompanionBuilderTypeDefName = $companionClassName Function({'); - final insertCompanionBuilderTypeDef = StringBuffer( - 'typedef $insertCompanionBuilderTypeDefName = $companionClassName Function({'); + final companionBuilderTypeDef = + StringBuffer('typedef $typedefName = $companionClassName Function({'); - final updateCompanionBuilderArguments = StringBuffer('({'); - final insertCompanionBuilderArguments = StringBuffer('({'); + final companionBuilderArguments = StringBuffer('({'); - final updateCompanionBuilderBody = StringBuffer('=> $companionClassName('); - final insertCompanionBuilderBody = - StringBuffer('=> $companionClassName.insert('); - - for (final column in table.columns) { - final value = leaf.drift('Value'); - final param = column.nameInDart; - final typeName = leaf.dartCode(leaf.dartType(column)); - - // The update companion has no required fields, they are all defaulted to absent - updateCompanionBuilderTypeDef.write('$value<$typeName> $param,'); - updateCompanionBuilderArguments - .write('$value<$typeName> $param = const $value.absent(),'); - updateCompanionBuilderBody.write('$param: $param,'); - - // The insert compantion has some required arguments and some that are defaulted to absent - if (!column.isImplicitRowId && table.isColumnRequiredForInsert(column)) { - insertCompanionBuilderTypeDef.write('required $typeName $param,'); - insertCompanionBuilderArguments.write('required $typeName $param,'); - } else { - insertCompanionBuilderTypeDef.write('$value<$typeName> $param,'); - insertCompanionBuilderArguments - .write('$value<$typeName> $param = const $value.absent(),'); - } - insertCompanionBuilderBody.write('$param: $param,'); + final StringBuffer companionBuilderBody; + if (isUpdate) { + companionBuilderBody = StringBuffer('=> $companionClassName('); + } else { + companionBuilderBody = StringBuffer('=> $companionClassName.insert('); } - // Close - // updateCompanionTypedef.write('})'); - insertCompanionBuilderTypeDef.write('});'); - updateCompanionBuilderTypeDef.write('});'); - insertCompanionBuilderArguments.write('})'); - updateCompanionBuilderArguments.write('})'); - insertCompanionBuilderBody.write(")"); - updateCompanionBuilderBody.write(")"); + for (final column in table.columns) { + final value = scope.drift('Value'); + final param = column.nameInDart; + final typeName = scope.dartCode(scope.dartType(column)); + + companionBuilderBody.write('$param: $param,'); + + if (isUpdate) { + // The update companion has no required fields, they are all defaulted to absent + companionBuilderTypeDef.write('$value<$typeName> $param,'); + companionBuilderArguments + .write('$value<$typeName> $param = const $value.absent(),'); + } else { + // The insert compantion has some required arguments and some that are defaulted to absent + if (!column.isImplicitRowId && + table.isColumnRequiredForInsert(column)) { + companionBuilderTypeDef.write('required $typeName $param,'); + companionBuilderArguments.write('required $typeName $param,'); + } else { + companionBuilderTypeDef.write('$value<$typeName> $param,'); + companionBuilderArguments + .write('$value<$typeName> $param = const $value.absent(),'); + } + } + } + companionBuilderTypeDef.write('});'); + companionBuilderArguments.write('})'); + companionBuilderBody.write(")"); + return ( + companionBuilderTypeDef.toString(), + companionBuilderArguments.toString() + companionBuilderBody.toString() + ); + } + + void _writeRootTable(TextEmitter leaf) { + final (insertCompanionBuilderTypeDef, insertCompanionBuilder) = + _companionBuilder(insertCompanionBuilderTypeDefName, isUpdate: false); + final (updateCompanionBuilderTypeDef, updateCompanionBuilder) = + _companionBuilder(updateCompanionBuilderTypeDefName, isUpdate: true); + leaf.writeln(insertCompanionBuilderTypeDef); leaf.writeln(updateCompanionBuilderTypeDef); @@ -367,11 +393,12 @@ class _TableNames { ..writeDriftRef("TableManagerState") ..write( """(db: db, table: table, filteringComposer:$filterComposer(db, table),orderingComposer:$orderingComposer(db, table), - getChildManagerBuilder :(p0) => $processedTableManager(p0),getUpdateCompanionBuilder: $updateCompanionBuilderArguments$updateCompanionBuilderBody, - getInsertCompanionBuilder:$insertCompanionBuilderArguments$insertCompanionBuilderBody));""") + getChildManagerBuilder :(p0) => $processedTableManager(p0),getUpdateCompanionBuilder: $updateCompanionBuilder, + getInsertCompanionBuilder:$insertCompanionBuilder));""") ..writeln('}'); } + /// Write the manager for this table, with all the filters and orderings void writeManager(TextEmitter leaf) { _writeFilterComposer(leaf); _writeOrderingComposer(leaf); @@ -379,6 +406,7 @@ class _TableNames { _writeRootTable(leaf); } + /// Add filters and orderings for the columns of this table void addFiltersAndOrderings(List tables) { // Utility function to get the referenced table and column (DriftTable, DriftColumn)? getReferencedTableAndColumn( @@ -395,10 +423,22 @@ class _TableNames { return null; } + // Utility function to get the duplicates in a list + List duplicates(List items) { + final seen = {}; + final duplicates = []; + for (var item in items) { + if (!seen.add(item)) { + duplicates.add(item); + } + } + return duplicates; + } + /// First add the filters and orderings for the columns /// of the current table for (var column in table.columns) { - final c = _ColumnNames(column.nameInDart); + final c = _ColumnWriter(column.nameInDart); // The type that this column is (int, string, etc) final innerColumnType = @@ -411,31 +451,32 @@ class _TableNames { // If the column has a type converter, add a filter with a converter if (column.typeConverter != null) { final converterType = scope.dartCode(scope.writer.dartType(column)); - c.filters.add(_FilterWithConverter(c.fieldGetter, + c.filters.add(_RegularFilterWriter("${c.fieldGetter}Value", + type: innerColumnType, fieldGetter: c.fieldGetter)); + c.filters.add(_FilterWithConverterWriter(c.fieldGetter, converterType: converterType, fieldGetter: c.fieldGetter, type: innerColumnType)); } else { - c.filters.add(_RegularFilter( - c.fieldGetter + (isForeignKey ? "Value" : ""), + c.filters.add(_RegularFilterWriter( + c.fieldGetter + (isForeignKey ? "Id" : ""), type: innerColumnType, fieldGetter: c.fieldGetter)); } // Add the ordering for the column - c.orderings.add(_RegularOrdering( - c.fieldGetter + (isForeignKey ? "Value" : ""), + c.orderings.add(_RegularOrderingWriter( + c.fieldGetter + (isForeignKey ? "Id" : ""), type: innerColumnType, fieldGetter: c.fieldGetter)); /// If this column is a foreign key to another table, add a filter and ordering /// for the referenced table - if (referenced != null) { final (referencedTable, referencedCol) = referenced; final referencedTableNames = - _TableNames(referencedTable, scope, dbScope, databaseGenericName); - final referencedColumnNames = _ColumnNames(referencedCol.nameInDart); + _TableWriter(referencedTable, scope, dbScope, databaseGenericName); + final referencedColumnNames = _ColumnWriter(referencedCol.nameInDart); final String referencedTableField = scope.generationOptions.isModular ? "state.db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" : "state.db.${referencedTable.dbGetterName}"; @@ -445,7 +486,7 @@ class _TableNames { referencedColumnGetter: referencedColumnNames.fieldGetter, referencedFilterComposer: referencedTableNames.filterComposer, referencedTableField: referencedTableField)); - c.orderings.add(_ReferencedOrdering(c.fieldGetter, + c.orderings.add(_ReferencedOrderingWriter(c.fieldGetter, fieldGetter: c.fieldGetter, referencedColumnGetter: referencedColumnNames.fieldGetter, referencedOrderingComposer: referencedTableNames.orderingComposer, @@ -462,8 +503,8 @@ class _TableNames { if (reference != null && reference.$1.entityInfoName == table.entityInfoName) { final referencedTableNames = - _TableNames(ot, scope, dbScope, databaseGenericName); - final referencedColumnNames = _ColumnNames(oc.nameInDart); + _TableWriter(ot, scope, dbScope, databaseGenericName); + final referencedColumnNames = _ColumnWriter(oc.nameInDart); final String referencedTableField = scope.generationOptions.isModular ? "state.db.resultSet<${referencedTableNames.tableClassName}>('${ot.schemaName}')" : "state.db.${ot.dbGetterName}"; @@ -479,38 +520,27 @@ class _TableNames { } } } + // Remove the filters and orderings that have duplicates // TODO: Add warnings for duplicate filters and orderings - List duplicates(List items) { - final seen = {}; - final duplicates = []; - for (var item in items) { - if (!seen.add(item)) { - duplicates.add(item); - } - } - return duplicates; - } - - // Gather which filters and orderings are duplicates - final filterNamesToRemove = duplicates(columns + final duplicatedFilterNames = duplicates(columns .map((e) => e.filters.map((e) => e.filterName)) .expand((e) => e) .toList() + backRefFilters.map((e) => e.filterName).toList()); - final orderingNamesToRemove = duplicates(columns + final duplicatedOrderingNames = duplicates(columns .map((e) => e.orderings.map((e) => e.orderingName)) .expand((e) => e) .toList()); - // Remove the duplicates for (var c in columns) { - c.filters.removeWhere((e) => filterNamesToRemove.contains(e.filterName)); + c.filters + .removeWhere((e) => duplicatedFilterNames.contains(e.filterName)); c.orderings - .removeWhere((e) => orderingNamesToRemove.contains(e.orderingName)); + .removeWhere((e) => duplicatedOrderingNames.contains(e.orderingName)); } backRefFilters - .removeWhere((e) => filterNamesToRemove.contains(e.filterName)); + .removeWhere((e) => duplicatedFilterNames.contains(e.filterName)); } } @@ -520,14 +550,19 @@ class ManagerWriter { final String _dbClassName; late final List _addedTables; + /// Class used to write a manager for a database ManagerWriter(this._scope, this._dbScope, this._dbClassName) { _addedTables = []; } + /// Add a table to the manager void addTable(DriftTable table) { _addedTables.add(table); } + /// The generic of the database that the manager will use + /// Will be `GeneratedDatabase` if modular generation is enabled + /// or the name of the database class if not String get databaseGenericName { if (_scope.generationOptions.isModular) { return _scope.drift("GeneratedDatabase"); @@ -536,37 +571,33 @@ class ManagerWriter { } } + /// The name of the manager class String get databaseManagerName => '${_dbClassName}Manager'; + /// The getter for the manager that will be added to the database String get managerGetter { return '$databaseManagerName get managers => $databaseManagerName(this);'; } + /// Write the manager to a provider [TextEmitter] void write() { final leaf = _scope.leaf(); - final tableNames = <_TableNames>[]; - + final tableNames = <_TableWriter>[]; for (var table in _addedTables) { - tableNames.add(_TableNames(table, _scope, _dbScope, databaseGenericName) + tableNames.add(_TableWriter(table, _scope, _dbScope, databaseGenericName) ..addFiltersAndOrderings(_addedTables)); } - final tableManagerGetters = StringBuffer(); - for (var table in tableNames) { table.writeManager(leaf); tableManagerGetters.writeln( "${table.rootTableManager} get ${table.table.dbGetterName} => ${table.rootTableManager}(_db, _db.${table.table.dbGetterName});"); } - - leaf.write(""" -class $databaseManagerName{ - final $_dbClassName _db; - - $databaseManagerName(this._db); - - $tableManagerGetters -} -"""); + leaf + ..writeln('class $databaseManagerName{') + ..writeln('final $_dbClassName _db;') + ..writeln('$databaseManagerName(this._db);') + ..writeln(tableManagerGetters) + ..writeln('}'); } } From 0af30cfb118e0d60d265c7d808051f52db66d210 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 14:00:43 -0400 Subject: [PATCH 32/74] Add docstings, limit & complete modular support --- drift/lib/internal/manager.dart | 53 +++++ drift/lib/src/runtime/manager/composer.dart | 47 +--- drift/lib/src/runtime/manager/filter.dart | 1 + drift/lib/src/runtime/manager/manager.dart | 214 +++++++++++++++---- drift_dev/lib/src/writer/manager_writer.dart | 88 +++++--- 5 files changed, 285 insertions(+), 118 deletions(-) create mode 100644 drift/lib/internal/manager.dart diff --git a/drift/lib/internal/manager.dart b/drift/lib/internal/manager.dart new file mode 100644 index 00000000..be1f3d42 --- /dev/null +++ b/drift/lib/internal/manager.dart @@ -0,0 +1,53 @@ +/// Internal library used by generated managers. +/// +/// This library is not part of drift's public API and should not be imported +/// manually. +library drift.internal.manager; + +import 'package:drift/drift.dart'; +import 'package:drift/src/runtime/manager/manager.dart'; + +/// Utility for creating a composer which contains the joins needed to +/// execute a query on a table that is referenced by a foreign key. +B composeWithJoins, B extends HasJoinBuilders>({ + required ComposerState $state, + required GeneratedColumn Function(CT) getCurrentColumn, + required RT referencedTable, + required GeneratedColumn Function(RT) getReferencedColumn, + required B Function(QC) builder, + required QC Function(DB db, RT table) getReferencedComposer, +}) { + // The name of the alias will be created using the following logic: + // "currentTableName__currentColumnName__referencedColumnName__referencedTableName" + // This is to ensure that the alias is unique + final currentColumn = getCurrentColumn($state.table); + final tempReferencedColumn = getReferencedColumn(referencedTable); + final aliasName = + '${currentColumn.tableName}__${currentColumn.name}__${tempReferencedColumn.tableName}__${tempReferencedColumn.name}'; + final aliasedReferencedTable = + $state.db.alias(referencedTable as TableInfo, aliasName); + final aliasedReferencedColumn = + getReferencedColumn(aliasedReferencedTable as RT); + + // Create a join builder for the referenced table + final joinBuilder = JoinBuilder( + currentTable: $state.table, + currentColumn: currentColumn, + referencedTable: aliasedReferencedTable, + referencedColumn: aliasedReferencedColumn, + ); + + // Get the query composer for the referenced table, passing in the aliased + // table and all the join builders + final referencedComposer = + getReferencedComposer($state.db, aliasedReferencedTable); + + // Run the user provided builder with the referencedQueryComposer + // This may return a filter or ordering, but we only enforce that it's + // a HasJoinBuilders + final result = builder(referencedComposer); + result.addJoinBuilders({joinBuilder}); + + return result; +} diff --git a/drift/lib/src/runtime/manager/composer.dart b/drift/lib/src/runtime/manager/composer.dart index 18e81ba8..0a981ae8 100644 --- a/drift/lib/src/runtime/manager/composer.dart +++ b/drift/lib/src/runtime/manager/composer.dart @@ -34,50 +34,7 @@ class ComposerState { @internal sealed class Composer { /// The state of the composer - final ComposerState state; + final ComposerState $state; - Composer(DB db, CT table) : state = ComposerState._(db, table); - - /// Method for create a join between two tables - B referenced, - B extends HasJoinBuilders>({ - required GeneratedColumn Function(CT) getCurrentColumn, - required RT referencedTable, - required GeneratedColumn Function(RT) getReferencedColumn, - required B Function(QC) builder, - required QC Function(DB db, RT table) getReferencedComposer, - }) { - // The name of the alias will be created using the following logic: - // "currentTableName__currentColumnName__referencedColumnName__referencedTableName" - // This is to ensure that the alias is unique - final currentColumn = getCurrentColumn(state.table); - final tempReferencedColumn = getReferencedColumn(referencedTable); - final aliasName = - '${currentColumn.tableName}__${currentColumn.name}__${tempReferencedColumn.tableName}__${tempReferencedColumn.name}'; - final aliasedReferencedTable = - state.db.alias(referencedTable as TableInfo, aliasName); - final aliasedReferencedColumn = - getReferencedColumn(aliasedReferencedTable as RT); - - // Create a join builder for the referenced table - final joinBuilder = JoinBuilder( - currentTable: state.table, - currentColumn: currentColumn, - referencedTable: aliasedReferencedTable, - referencedColumn: aliasedReferencedColumn, - ); - - // Get the query composer for the referenced table, passing in the aliased - // table and all the join builders - final referencedComposer = - getReferencedComposer(state.db, aliasedReferencedTable); - - // Run the user provided builder with the referencedQueryComposer - // This may return a filter or ordering, but we only enforce that it's - // a HasJoinBuilders - final result = builder(referencedComposer); - result.addJoinBuilders({joinBuilder}); - - return result; - } + Composer(DB db, CT table) : $state = ComposerState._(db, table); } diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 65f9b5fe..a349b439 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -45,6 +45,7 @@ class ColumnFilters { ColumnFilters get count => ColumnFilters(column.count()); } +/// Built in filters for bool columns extension BoolFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value ComposableFilter isTrue(bool value) => diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 9df065d5..c1a15e74 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -214,15 +214,16 @@ class TableManagerState< }; } + /// Build an update statement based on the manager state UpdateStatement buildUpdateStatement() { - final UpdateStatement deleteStatement; + final UpdateStatement updateStatement; if (joinBuilders.isEmpty) { - deleteStatement = db.update(_tableAsTableInfo); + updateStatement = db.update(_tableAsTableInfo); if (filter != null) { - deleteStatement.where((_) => filter!); + updateStatement.where((_) => filter!); } } else { - deleteStatement = db.update(_tableAsTableInfo); + updateStatement = db.update(_tableAsTableInfo); for (var col in _tableAsTableInfo.primaryKey) { final subquery = _buildSelectStatement( targetColumns: [col], @@ -230,12 +231,13 @@ class TableManagerState< applyFilters: true, applyOrdering: false, applyLimit: false) as _JoinedResult; - deleteStatement.where((tbl) => col.isInQuery(subquery.statement)); + updateStatement.where((tbl) => col.isInQuery(subquery.statement)); } } - return deleteStatement; + return updateStatement; } + /// Count the number of rows that would be returned by the built statement Future count() { final count = countAll(); final result = _buildSelectStatement( @@ -247,7 +249,7 @@ class TableManagerState< return result.statement.map((row) => row.read(count)!).getSingle(); } - // Build a delete statement based on the manager state + /// Build a delete statement based on the manager state DeleteStatement buildDeleteStatement() { final DeleteStatement deleteStatement; if (joinBuilders.isEmpty) { @@ -285,36 +287,63 @@ abstract class BaseTableManager< CI extends Function, CU extends Function> { /// The state for this manager - final TableManagerState state; + final TableManagerState $state; /// Create a new [BaseTableManager] instance - const BaseTableManager(this.state); - Future delete() => state.buildDeleteStatement().go(); + const BaseTableManager(this.$state); + + /// Deletes all rows matched by built statement + /// + /// Returns the amount of rows that were deleted by this statement directly + /// (not including additional rows that might be affected through triggers or + /// foreign key constraints). + Future delete() => $state.buildDeleteStatement().go(); /// Add ordering to the statement C orderBy(ComposableOrdering Function(OS o) o) { - final orderings = o(state.orderingComposer); - return state._getChildManagerBuilder(state.copyWith( + final orderings = o($state.orderingComposer); + return $state._getChildManagerBuilder($state.copyWith( orderingBuilders: - state.orderingBuilders.union(orderings.orderingBuilders), - joinBuilders: state.joinBuilders.union(orderings.joinBuilders))); + $state.orderingBuilders.union(orderings.orderingBuilders), + joinBuilders: $state.joinBuilders.union(orderings.joinBuilders))); } /// Add a filter to the statement C filter(ComposableFilter Function(FS f) f) { - final filter = f(state.filteringComposer); - return state._getChildManagerBuilder(state.copyWith( - filter: state.filter == null + final filter = f($state.filteringComposer); + return $state._getChildManagerBuilder($state.copyWith( + filter: $state.filter == null ? filter.expression - : filter.expression & state.filter!, - joinBuilders: state.joinBuilders.union(filter.joinBuilders))); + : filter.expression & $state.filter!, + joinBuilders: $state.joinBuilders.union(filter.joinBuilders))); } - Future count() => state.count(); + /// Add a limit to the statement + C limit(int limit, {int? offset}) { + return $state + ._getChildManagerBuilder($state.copyWith(limit: limit, offset: offset)); + } + + /// Return the count of rows matched by the built statement + Future count() => $state.count(); + + /// Writes all non-null fields from the entity into the columns of all rows + /// that match the [filter] clause. Warning: That also means that, when you're + /// not setting a where clause explicitly, this method will update all rows in + /// the [$state.table]. + /// + /// The fields that are null on the entity object will not be changed by + /// this operation, they will be ignored. + /// + /// Returns the amount of rows that have been affected by this operation. + /// + /// See also: [RootTableManager.replace], which does not require [filter] statements and + /// supports setting fields back to null. Future write(Insertable
Function(CU o) f) => - state.buildUpdateStatement().write(f(state._getUpdateCompanionBuilder)); + $state.buildUpdateStatement().write(f($state._getUpdateCompanionBuilder)); } +/// A table manager that can be used to select rows from a table abstract class ProcessedTableManager< DB extends GeneratedDatabase, T extends Table, @@ -329,24 +358,61 @@ abstract class ProcessedTableManager< MultiSelectable, SingleSelectable, SingleOrNullSelectable { - const ProcessedTableManager(super.state); + /// Create a new [ProcessedTableManager] instance + const ProcessedTableManager(super.$state); + /// Executes this statement, like [get], but only returns one + /// value. If the query returns no or too many rows, the returned future will + /// complete with an error. + /// + /// Be aware that this operation won't put a limit clause on this statement, + /// if that's needed you would have to do use [limit]: + /// You should only use this method if you know the query won't have more than + /// one row, for instance because you used `limit(1)` or you know the filters + /// you've applied will only match one row. + /// + /// See also: [getSingleOrNull], which returns `null` instead of + /// throwing if the query completes with no rows. @override - Future getSingle() => state.buildSelectStatement().getSingle(); + Future getSingle() => $state.buildSelectStatement().getSingle(); + + /// Creates an auto-updating stream of this statement, similar to + /// [watch]. However, it is assumed that the query will only emit + /// one result, so instead of returning a `Stream>`, this returns a + /// `Stream`. If, at any point, the query emits no or more than one rows, + /// an error will be added to the stream instead. @override - Stream watchSingle() => state.buildSelectStatement().watchSingle(); + Stream watchSingle() => $state.buildSelectStatement().watchSingle(); + + /// Executes the statement and returns the first all rows as a list. @override - Future> get() => state.buildSelectStatement().get(); + Future> get() => $state.buildSelectStatement().get(); + + /// Creates an auto-updating stream of the result that emits new items + /// whenever any table used in this statement changes. @override - Stream> watch() => state.buildSelectStatement().watch(); + Stream> watch() => $state.buildSelectStatement().watch(); + + /// Executes this statement, like [get], but only returns one + /// value. If the result too many values, this method will throw. If no + /// row is returned, `null` will be returned instead. + /// + /// See also: [getSingle], which can be used if the query will + /// always evaluate to exactly one row. @override Future getSingleOrNull() => - state.buildSelectStatement().getSingleOrNull(); + $state.buildSelectStatement().getSingleOrNull(); + + /// Creates an auto-updating stream of this statement, similar to + /// [watch]. However, it is assumed that the query will only + /// emit one result, so instead of returning a `Stream>`, this + /// returns a `Stream`. If the query emits more than one row at + /// some point, an error will be emitted to the stream instead. + /// If the query emits zero rows at some point, `null` will be added + /// to the stream instead. @override Stream watchSingleOrNull() => - state.buildSelectStatement().watchSingleOrNull(); - - Future delete() => state.buildDeleteStatement().go(); + $state.buildSelectStatement().watchSingleOrNull(); } /// A table manager with top level function for creating, reading, updating, and deleting items @@ -359,34 +425,102 @@ abstract class RootTableManager< C extends ProcessedTableManager, CI extends Function, CU extends Function> extends BaseTableManager { - const RootTableManager(super.state); + /// Create a new [RootTableManager] instance + const RootTableManager(super.$state); - C all() => state._getChildManagerBuilder(state); + /// Select all rows from the table + C all() => $state._getChildManagerBuilder($state); + /// Creates a new row in the table using the given function + /// + /// By default, an exception will be thrown if another row with the same + /// primary key already exists. This behavior can be overridden with [mode], + /// for instance by using [InsertMode.replace] or [InsertMode.insertOrIgnore]. + /// + /// To apply a partial or custom update in case of a conflict, you can also + /// use an [upsert clause](https://sqlite.org/lang_UPSERT.html) by using + /// [onConflict]. See [InsertStatement.insert] for more information. + /// + /// By default, the [onConflict] clause will only consider the table's primary + /// key. If you have additional columns with uniqueness constraints, you have + /// to manually add them to the clause's [DoUpdate.target]. + /// + /// Returns the `rowid` of the inserted row. For tables with an auto-increment + /// column, the `rowid` is the generated value of that column. The returned + /// value can be inaccurate when [onConflict] is set and the insert behaved + /// like an update. + /// + /// If the table doesn't have a `rowid`, you can't rely on the return value. + /// Still, the future will always complete with an error if the insert fails. Future create(Insertable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { - return state.db.into(state._tableAsTableInfo).insert( - f(state._getInsertCompanionBuilder), + return $state.db.into($state._tableAsTableInfo).insert( + f($state._getInsertCompanionBuilder), mode: mode, onConflict: onConflict); } + /// Inserts a row into the table and returns it. + /// + /// Depending on the [InsertMode] or the [DoUpdate] `onConflict` clause, the + /// insert statement may not actually insert a row into the database. Since + /// this function was declared to return a non-nullable row, it throws an + /// exception in that case. Use [createReturningOrNull] when performing an + /// insert with an insert mode like [InsertMode.insertOrIgnore] or when using + /// a [DoUpdate] with a `where` clause clause. Future createReturning(Insertable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { - return state.db.into(state._tableAsTableInfo).insertReturning( - f(state._getInsertCompanionBuilder), + return $state.db.into($state._tableAsTableInfo).insertReturning( + f($state._getInsertCompanionBuilder), mode: mode, onConflict: onConflict); } + /// Inserts a row into the table and returns it. + /// + /// When no row was inserted and no exception was thrown, for instance because + /// [InsertMode.insertOrIgnore] was used or because the upsert clause had a + /// `where` clause that didn't match, `null` is returned instead. + Future createReturningOrNull(Insertable Function(CI o) f, + {InsertMode? mode, UpsertClause? onConflict}) { + return $state.db.into($state._tableAsTableInfo).insertReturningOrNull( + f($state._getInsertCompanionBuilder), + mode: mode, + onConflict: onConflict); + } + + /// Create multiple rows in the table using the given function + /// + /// All fields in a row that don't have a default value or auto-increment + /// must be set and non-null. Otherwise, an [InvalidDataException] will be + /// thrown. + /// By default, an exception will be thrown if another row with the same + /// primary key already exists. This behavior can be overridden with [mode], + /// for instance by using [InsertMode.replace] or [InsertMode.insertOrIgnore]. + /// Using [bulkCreate] will not disable primary keys or any column constraint + /// checks. + /// [onConflict] can be used to create an upsert clause for engines that + /// support it. For details and examples, see [InsertStatement.insert]. Future bulkCreate(Iterable> Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { - return state.db.batch((b) => b.insertAll( - state._tableAsTableInfo, f(state._getInsertCompanionBuilder), + return $state.db.batch((b) => b.insertAll( + $state._tableAsTableInfo, f($state._getInsertCompanionBuilder), mode: mode, onConflict: onConflict)); } - Future replace(Insertable entry) { - return state.db.update(state._tableAsTableInfo).replace(entry); + /// Replaces the old version of [entity] that is stored in the database with + /// the fields of the [entity] provided here. This implicitly applies a + /// [filter] clause to rows with the same primary key as [entity], so that only + /// the row representing outdated data will be replaced. + /// + /// If [entity] has absent values (set to null on the [DataClass] or + /// explicitly to absent on the [UpdateCompanion]), and a default value for + /// the field exists, that default value will be used. Otherwise, the field + /// will be reset to null. This behavior is different to [write], which simply + /// ignores such fields without changing them in the database. + /// + /// Returns true if a row was affected by this operation. + Future replace(Insertable entity) { + return $state.db.update($state._tableAsTableInfo).replace(entity); } } diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index dbd24e16..2d7c7458 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -1,5 +1,6 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:drift_dev/src/analysis/results/results.dart'; +import 'package:drift_dev/src/writer/modules.dart'; import 'package:drift_dev/src/writer/writer.dart'; abstract class _FilterWriter { @@ -36,7 +37,7 @@ class _RegularFilterWriter extends _FilterWriter { ..writeDriftRef("ColumnFilters") ..write("<$type> get $filterName =>") ..writeDriftRef("ColumnFilters") - ..write("(state.table.$fieldGetter);"); + ..write("(\$state.table.$fieldGetter);"); } } @@ -63,15 +64,15 @@ class _FilterWithConverterWriter extends _FilterWriter { ..writeDriftRef("ColumnWithTypeConverterFilters") ..write("<$converterType,$type> get $filterName =>") ..writeDriftRef("ColumnWithTypeConverterFilters") - ..writeln("(state.table.$fieldGetter);"); + ..writeln("(\$state.table.$fieldGetter);"); } } class _ReferencedFilter extends _FilterWriter { /// The full function used to get the referenced table /// - /// E.G `state.db.resultSet<$CategoryTable>('categories')` - /// or `state.db.categories` + /// E.G `\$state.db.resultSet<$CategoryTable>('categories')` + /// or `\$state.db.categories` final String referencedTableField; /// The getter for the column on the referenced table @@ -100,14 +101,18 @@ class _ReferencedFilter extends _FilterWriter { ..write(" $filterName(") ..writeDriftRef("ComposableFilter") ..writeln(" Function( $referencedFilterComposer f) f) {") - ..write(''' -return referenced( - referencedTable: $referencedTableField, - getCurrentColumn: (f) => f.$fieldGetter, - getReferencedColumn: (f) => f.$referencedColumnGetter, - getReferencedComposer: (db, table) => $referencedFilterComposer(db, table), - builder: f); - }'''); + ..write("return ") + ..writeUriRef( + Uri.parse('package:drift/internal/manager.dart'), 'composeWithJoins') + ..writeln('(') + ..writeln("\$state: \$state,") + ..writeln("referencedTable: $referencedTableField,") + ..writeln("getCurrentColumn: (f) => f.$fieldGetter,") + ..writeln("getReferencedColumn: (f) => f.$referencedColumnGetter,") + ..writeln( + "getReferencedComposer: (db, table) => $referencedFilterComposer(db, table),") + ..writeln("builder: f);") + ..writeln("}"); } } @@ -145,15 +150,15 @@ class _RegularOrderingWriter extends _OrderingWriter { ..writeDriftRef("ColumnOrderings") ..write(" get $orderingName =>") ..writeDriftRef("ColumnOrderings") - ..write("(state.table.$fieldGetter);"); + ..write("(\$state.table.$fieldGetter);"); } } class _ReferencedOrderingWriter extends _OrderingWriter { /// The full function used to get the referenced table /// - /// E.G `state.db.resultSet<$CategoryTable>('categories')` - /// or `state.db.categories` + /// E.G `\$state.db.resultSet<$CategoryTable>('categories')` + /// or `\$state.db.categories` final String referencedTableField; /// The getter for the column on the referenced table @@ -180,14 +185,18 @@ class _ReferencedOrderingWriter extends _OrderingWriter { ..write(" $orderingName(") ..writeDriftRef("ComposableOrdering") ..writeln(" Function( $referencedOrderingComposer o) o) {") - ..writeln(''' -return referenced( - referencedTable: $referencedTableField, - getCurrentColumn: (f) => f.$fieldGetter, - getReferencedColumn: (f) => f.$referencedColumnGetter, - getReferencedComposer: (db, table) => $referencedOrderingComposer(db, table), - builder: o); - }'''); + ..write("return ") + ..writeUriRef( + Uri.parse('package:drift/internal/manager.dart'), 'composeWithJoins') + ..writeln('(') + ..writeln("\$state: \$state,") + ..writeln("referencedTable: $referencedTableField,") + ..writeln("getCurrentColumn: (f) => f.$fieldGetter,") + ..writeln("getReferencedColumn: (f) => f.$referencedColumnGetter,") + ..writeln( + "getReferencedComposer: (db, table) => $referencedOrderingComposer(db, table),") + ..writeln("builder: o);") + ..writeln("}"); } } @@ -315,7 +324,7 @@ class _TableWriter { ..writeDriftRef('ProcessedTableManager') ..writeln( '<$databaseGenericName,$tableClassName,$rowClassName,$filterComposer,$orderingComposer,$processedTableManager,$insertCompanionBuilderTypeDefName,$updateCompanionBuilderTypeDefName> {') - ..writeln('const $processedTableManager(super.state);') + ..writeln('const $processedTableManager(super.\$state);') ..writeln('}'); } @@ -478,8 +487,8 @@ class _TableWriter { _TableWriter(referencedTable, scope, dbScope, databaseGenericName); final referencedColumnNames = _ColumnWriter(referencedCol.nameInDart); final String referencedTableField = scope.generationOptions.isModular - ? "state.db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" - : "state.db.${referencedTable.dbGetterName}"; + ? "\$state.db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" + : "\$state.db.${referencedTable.dbGetterName}"; c.filters.add(_ReferencedFilter(c.fieldGetter, fieldGetter: c.fieldGetter, @@ -506,8 +515,8 @@ class _TableWriter { _TableWriter(ot, scope, dbScope, databaseGenericName); final referencedColumnNames = _ColumnWriter(oc.nameInDart); final String referencedTableField = scope.generationOptions.isModular - ? "state.db.resultSet<${referencedTableNames.tableClassName}>('${ot.schemaName}')" - : "state.db.${ot.dbGetterName}"; + ? "\$state.db.resultSet<${referencedTableNames.tableClassName}>('${ot.schemaName}')" + : "\$state.db.${ot.dbGetterName}"; final filterName = oc.referenceName ?? "${referencedTableNames.table.dbGetterName}Refs"; @@ -582,17 +591,30 @@ class ManagerWriter { /// Write the manager to a provider [TextEmitter] void write() { final leaf = _scope.leaf(); - final tableNames = <_TableWriter>[]; - for (var table in _addedTables) { - tableNames.add(_TableWriter(table, _scope, _dbScope, databaseGenericName) - ..addFiltersAndOrderings(_addedTables)); + + // When generating with modular generation, we need to add the imports + // for the internal `resultSet` helper + if (_scope.generationOptions.isModular) { + leaf.refUri(ModularAccessorWriter.modularSupport, ''); } + + // Write the manager class for each table + final tableWriters = <_TableWriter>[]; + for (var table in _addedTables) { + tableWriters.add( + _TableWriter(table, _scope, _dbScope, databaseGenericName) + ..addFiltersAndOrderings(_addedTables)); + } + + // Write each tables manager to the leaf and append the getter to the main manager final tableManagerGetters = StringBuffer(); - for (var table in tableNames) { + for (var table in tableWriters) { table.writeManager(leaf); tableManagerGetters.writeln( "${table.rootTableManager} get ${table.table.dbGetterName} => ${table.rootTableManager}(_db, _db.${table.table.dbGetterName});"); } + + // Write the main manager class leaf ..writeln('class $databaseManagerName{') ..writeln('final $_dbClassName _db;') From 3cd64901d65927f36557e26d477da2e42c76734b Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 14:10:47 -0400 Subject: [PATCH 33/74] add option --- drift_dev/lib/src/analysis/options.dart | 6 ++++++ .../lib/src/generated/analysis/options.g.dart | 5 +++++ drift_dev/lib/src/writer/database_writer.dart | 15 +++++++++------ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drift_dev/lib/src/analysis/options.dart b/drift_dev/lib/src/analysis/options.dart index a4d871cc..937093d4 100644 --- a/drift_dev/lib/src/analysis/options.dart +++ b/drift_dev/lib/src/analysis/options.dart @@ -61,6 +61,10 @@ class DriftOptions { @JsonKey(name: 'generate_connect_constructor', defaultValue: false) final bool generateConnectConstructor; + /// Generate managers to assist with common database operations. + @JsonKey(name: 'generate_manager', defaultValue: true) + final bool generateManager; + @JsonKey(name: 'sqlite_modules', defaultValue: []) @Deprecated('Use effectiveModules instead') final List modules; @@ -128,6 +132,7 @@ class DriftOptions { this.useColumnNameAsJsonKeyWhenDefinedInMoorFile = true, this.useSqlColumnNameAsJsonKey = false, this.generateConnectConstructor = false, + this.generateManager = true, this.dataClassToCompanions = true, this.generateMutableClasses = false, this.rawResultSetData = false, @@ -156,6 +161,7 @@ class DriftOptions { required this.useColumnNameAsJsonKeyWhenDefinedInMoorFile, required this.useSqlColumnNameAsJsonKey, required this.generateConnectConstructor, + required this.generateManager, required this.dataClassToCompanions, required this.generateMutableClasses, required this.rawResultSetData, diff --git a/drift_dev/lib/src/generated/analysis/options.g.dart b/drift_dev/lib/src/generated/analysis/options.g.dart index 3872438b..b53f7a7d 100644 --- a/drift_dev/lib/src/generated/analysis/options.g.dart +++ b/drift_dev/lib/src/generated/analysis/options.g.dart @@ -20,6 +20,7 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate( 'use_column_name_as_json_key_when_defined_in_moor_file', 'use_sql_column_name_as_json_key', 'generate_connect_constructor', + 'generate_manager', 'sqlite_modules', 'sqlite', 'sql', @@ -57,6 +58,8 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate( 'use_sql_column_name_as_json_key', (v) => v as bool? ?? false), generateConnectConstructor: $checkedConvert( 'generate_connect_constructor', (v) => v as bool? ?? false), + generateManager: + $checkedConvert('generate_manager', (v) => v as bool? ?? true), dataClassToCompanions: $checkedConvert( 'data_class_to_companions', (v) => v as bool? ?? true), generateMutableClasses: @@ -116,6 +119,7 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate( 'use_column_name_as_json_key_when_defined_in_moor_file', 'useSqlColumnNameAsJsonKey': 'use_sql_column_name_as_json_key', 'generateConnectConstructor': 'generate_connect_constructor', + 'generateManager': 'generate_manager', 'dataClassToCompanions': 'data_class_to_companions', 'generateMutableClasses': 'mutable_classes', 'rawResultSetData': 'raw_result_set_data', @@ -149,6 +153,7 @@ Map _$DriftOptionsToJson(DriftOptions instance) => instance.useColumnNameAsJsonKeyWhenDefinedInMoorFile, 'use_sql_column_name_as_json_key': instance.useSqlColumnNameAsJsonKey, 'generate_connect_constructor': instance.generateConnectConstructor, + 'generate_manager': instance.generateManager, 'sqlite_modules': instance.modules.map((e) => _$SqlModuleEnumMap[e]!).toList(), 'sqlite': instance.sqliteAnalysisOptions?.toJson(), diff --git a/drift_dev/lib/src/writer/database_writer.dart b/drift_dev/lib/src/writer/database_writer.dart index 48bb6223..1d1b00e4 100644 --- a/drift_dev/lib/src/writer/database_writer.dart +++ b/drift_dev/lib/src/writer/database_writer.dart @@ -148,13 +148,16 @@ class DatabaseWriter { } } - final managerWriter = ManagerWriter(scope.child(), dbScope, dbClassName); - for (var table in elements.whereType()) { - managerWriter.addTable(table); + // Write the main database manager & all the managers for tables + if (scope.options.generateManager) { + final managerWriter = ManagerWriter(scope.child(), dbScope, dbClassName); + for (var table in elements.whereType()) { + managerWriter.addTable(table); + } + managerWriter.write(); + // Add getter for the manager to the database class + firstLeaf.writeln(managerWriter.managerGetter); } - managerWriter.write(); - - firstLeaf.writeln(managerWriter.managerGetter); // Write implementation for query methods for (final query in input.availableRegularQueries) { From 6325a58b56b1c00f3b6bd32913d81cd87c778865 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 14:26:12 -0400 Subject: [PATCH 34/74] insertable bug --- drift/lib/src/runtime/manager/manager.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index c1a15e74..212561e7 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -339,7 +339,7 @@ abstract class BaseTableManager< /// /// See also: [RootTableManager.replace], which does not require [filter] statements and /// supports setting fields back to null. - Future write(Insertable
Function(CU o) f) => + Future write(DT Function(CU o) f) => $state.buildUpdateStatement().write(f($state._getUpdateCompanionBuilder)); } @@ -452,7 +452,7 @@ abstract class RootTableManager< /// /// If the table doesn't have a `rowid`, you can't rely on the return value. /// Still, the future will always complete with an error if the insert fails. - Future create(Insertable Function(CI o) f, + Future create(D Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insert( f($state._getInsertCompanionBuilder), @@ -468,7 +468,7 @@ abstract class RootTableManager< /// exception in that case. Use [createReturningOrNull] when performing an /// insert with an insert mode like [InsertMode.insertOrIgnore] or when using /// a [DoUpdate] with a `where` clause clause. - Future createReturning(Insertable Function(CI o) f, + Future createReturning(D Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insertReturning( f($state._getInsertCompanionBuilder), @@ -481,7 +481,7 @@ abstract class RootTableManager< /// When no row was inserted and no exception was thrown, for instance because /// [InsertMode.insertOrIgnore] was used or because the upsert clause had a /// `where` clause that didn't match, `null` is returned instead. - Future createReturningOrNull(Insertable Function(CI o) f, + Future createReturningOrNull(D Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insertReturningOrNull( f($state._getInsertCompanionBuilder), @@ -501,7 +501,7 @@ abstract class RootTableManager< /// checks. /// [onConflict] can be used to create an upsert clause for engines that /// support it. For details and examples, see [InsertStatement.insert]. - Future bulkCreate(Iterable> Function(CI o) f, + Future bulkCreate(Iterable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.batch((b) => b.insertAll( $state._tableAsTableInfo, f($state._getInsertCompanionBuilder), @@ -520,7 +520,7 @@ abstract class RootTableManager< /// ignores such fields without changing them in the database. /// /// Returns true if a row was affected by this operation. - Future replace(Insertable entity) { + Future replace(D entity) { return $state.db.update($state._tableAsTableInfo).replace(entity); } } From 1dc9282967ff7ad9083992d2b37bc2bc0230e74a Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 15:18:37 -0400 Subject: [PATCH 35/74] nice --- drift/lib/src/runtime/manager/manager.dart | 27 +++++++++++--------- drift_dev/lib/src/writer/manager_writer.dart | 10 ++++---- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 212561e7..5249f6c0 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -244,8 +244,8 @@ class TableManagerState< targetColumns: [count], addJoins: true, applyFilters: true, - applyOrdering: false, - applyLimit: false) as _JoinedResult; + applyOrdering: true, + applyLimit: true) as _JoinedResult; return result.statement.map((row) => row.read(count)!).getSingle(); } @@ -264,8 +264,8 @@ class TableManagerState< targetColumns: [col], addJoins: true, applyFilters: true, - applyOrdering: false, - applyLimit: false) as _JoinedResult; + applyOrdering: true, + applyLimit: true) as _JoinedResult; deleteStatement.where((tbl) => col.isInQuery(subquery.statement)); } } @@ -339,8 +339,9 @@ abstract class BaseTableManager< /// /// See also: [RootTableManager.replace], which does not require [filter] statements and /// supports setting fields back to null. - Future write(DT Function(CU o) f) => - $state.buildUpdateStatement().write(f($state._getUpdateCompanionBuilder)); + Future write(DT Function(CU o) f) => $state + .buildUpdateStatement() + .write(f($state._getUpdateCompanionBuilder) as Insertable
); } /// A table manager that can be used to select rows from a table @@ -455,7 +456,7 @@ abstract class RootTableManager< Future create(D Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insert( - f($state._getInsertCompanionBuilder), + f($state._getInsertCompanionBuilder) as Insertable, mode: mode, onConflict: onConflict); } @@ -471,7 +472,7 @@ abstract class RootTableManager< Future createReturning(D Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insertReturning( - f($state._getInsertCompanionBuilder), + f($state._getInsertCompanionBuilder) as Insertable, mode: mode, onConflict: onConflict); } @@ -484,7 +485,7 @@ abstract class RootTableManager< Future createReturningOrNull(D Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insertReturningOrNull( - f($state._getInsertCompanionBuilder), + f($state._getInsertCompanionBuilder) as Insertable, mode: mode, onConflict: onConflict); } @@ -503,8 +504,8 @@ abstract class RootTableManager< /// support it. For details and examples, see [InsertStatement.insert]. Future bulkCreate(Iterable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { - return $state.db.batch((b) => b.insertAll( - $state._tableAsTableInfo, f($state._getInsertCompanionBuilder), + return $state.db.batch((b) => b.insertAll($state._tableAsTableInfo, + f($state._getInsertCompanionBuilder) as Iterable>, mode: mode, onConflict: onConflict)); } @@ -521,6 +522,8 @@ abstract class RootTableManager< /// /// Returns true if a row was affected by this operation. Future replace(D entity) { - return $state.db.update($state._tableAsTableInfo).replace(entity); + return $state.db + .update($state._tableAsTableInfo) + .replace(entity as Insertable); } } diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 2d7c7458..8f42df5f 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -68,7 +68,7 @@ class _FilterWithConverterWriter extends _FilterWriter { } } -class _ReferencedFilter extends _FilterWriter { +class _ReferencedFilterWriter extends _FilterWriter { /// The full function used to get the referenced table /// /// E.G `\$state.db.resultSet<$CategoryTable>('categories')` @@ -86,7 +86,7 @@ class _ReferencedFilter extends _FilterWriter { final String referencedFilterComposer; /// A class used for building filters for referenced tables - _ReferencedFilter( + _ReferencedFilterWriter( super.filterName, { required this.referencedTableField, required this.referencedColumnGetter, @@ -280,7 +280,7 @@ class _TableWriter { final List<_ColumnWriter> columns; /// Filters for back references - final List<_ReferencedFilter> backRefFilters; + final List<_ReferencedFilterWriter> backRefFilters; _TableWriter(this.table, this.scope, this.dbScope, this.databaseGenericName) : backRefFilters = [], @@ -490,7 +490,7 @@ class _TableWriter { ? "\$state.db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" : "\$state.db.${referencedTable.dbGetterName}"; - c.filters.add(_ReferencedFilter(c.fieldGetter, + c.filters.add(_ReferencedFilterWriter(c.fieldGetter, fieldGetter: c.fieldGetter, referencedColumnGetter: referencedColumnNames.fieldGetter, referencedFilterComposer: referencedTableNames.filterComposer, @@ -521,7 +521,7 @@ class _TableWriter { final filterName = oc.referenceName ?? "${referencedTableNames.table.dbGetterName}Refs"; - backRefFilters.add(_ReferencedFilter(filterName, + backRefFilters.add(_ReferencedFilterWriter(filterName, fieldGetter: reference.$2.nameInDart, referencedColumnGetter: referencedColumnNames.fieldGetter, referencedFilterComposer: referencedTableNames.filterComposer, From 7b35a0add5d8df74019c7750e57b111360b23844 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 20:22:13 -0400 Subject: [PATCH 36/74] nits --- drift/lib/drift.dart | 7 +- drift/lib/internal/manager.dart | 12 +- drift/lib/src/dsl/columns.dart | 6 +- drift/lib/src/runtime/manager/composer.dart | 27 +---- drift/lib/src/runtime/manager/filter.dart | 111 ++++++++----------- drift/lib/src/runtime/manager/join.dart | 27 ++++- drift/lib/src/runtime/manager/manager.dart | 100 +++++++---------- drift/lib/src/runtime/manager/ordering.dart | 23 ++-- drift_dev/lib/src/writer/manager_writer.dart | 28 ++--- 9 files changed, 150 insertions(+), 191 deletions(-) diff --git a/drift/lib/drift.dart b/drift/lib/drift.dart index 996857fa..2e272ab7 100644 --- a/drift/lib/drift.dart +++ b/drift/lib/drift.dart @@ -21,12 +21,7 @@ export 'src/runtime/query_builder/query_builder.dart' export 'src/runtime/types/converters.dart'; export 'src/runtime/types/mapping.dart' hide BaseSqlType, UserDefinedSqlType; export 'src/runtime/manager/manager.dart' - hide - JoinBuilder, - ComposerState, - HasJoinBuilders, - Composer, - BaseTableManager; + hide JoinBuilder, HasJoinBuilders, Composer, BaseTableManager; export 'src/utils/lazy_database.dart'; /// A [ListEquality] instance used by generated drift code for the `==` and diff --git a/drift/lib/internal/manager.dart b/drift/lib/internal/manager.dart index be1f3d42..c3aa31b5 100644 --- a/drift/lib/internal/manager.dart +++ b/drift/lib/internal/manager.dart @@ -11,7 +11,8 @@ import 'package:drift/src/runtime/manager/manager.dart'; /// execute a query on a table that is referenced by a foreign key. B composeWithJoins, B extends HasJoinBuilders>({ - required ComposerState $state, + required DB $db, + required CT $table, required GeneratedColumn Function(CT) getCurrentColumn, required RT referencedTable, required GeneratedColumn Function(RT) getReferencedColumn, @@ -21,18 +22,18 @@ B composeWithJoins integer().autoIncrement()(); /// } -/// /// Filter all Categories which contain a TodoEntries with a body of "Todo" +/// /// The manager will now use the name `categories` /// categories.filter((f) => f.categories((f) => f.body("Todo"))) /// /// ``` -/// When these aren't specified, drift will use the referenced tables name followed by `Refs`. +/// When these aren't specified, the manager will use the referenced tables name followed by `Refs`. /// If a reference name clashes with other fields on the table, the generator will show a warning, /// and filters and orderings wont be generated class ReferenceName { diff --git a/drift/lib/src/runtime/manager/composer.dart b/drift/lib/src/runtime/manager/composer.dart index 0a981ae8..08008281 100644 --- a/drift/lib/src/runtime/manager/composer.dart +++ b/drift/lib/src/runtime/manager/composer.dart @@ -1,20 +1,6 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first part of 'manager.dart'; -/// A class that holds the state for a query composer -@internal -class ComposerState { - /// The database that the query will be executed on - final DB db; - - /// The table that the query will be executed on - final T table; - ComposerState._( - this.db, - this.table, - ); -} - /// Base class for all composers /// /// Any class that can be composed using the `&` or `|` operator is called a composable. @@ -27,14 +13,13 @@ class ComposerState { /// `f` in this example is a [Composer] object, and `f.id.equals(1)` returns a [ComposableFilter] object. /// /// The [Composer] class is responsible for creating joins between tables, and passing them down to the composable classes. -/// -/// The [ComposerState] that is held in this class only holds temporary state, as the final state will be held in the composable classes. -/// E.G. In the example above, only the resulting [ComposableFilter] object is returned, and the [FilterComposer] is discarded. -/// @internal sealed class Composer { - /// The state of the composer - final ComposerState $state; + /// The database that the query will be executed on + final DB $db; - Composer(DB db, CT table) : $state = ComposerState._(db, table); + /// The table that the query will be executed on + final CT $table; + + Composer(this.$db, this.$table); } diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index a349b439..7f92e5e4 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -13,33 +13,31 @@ class ColumnFilters { /// FitlerBuilder after2000() => isAfter(DateTime(2000)); ///} /// ``` - ColumnFilters(this.column); + const ColumnFilters(this.column); /// Column that this [ColumnFilters] wraps - Expression column; + final Expression column; /// Create a filter that checks if the column is null. - ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); + ComposableFilter isNull() => ComposableFilter(column.isNull()); /// Create a filter that checks if the column is not null. - ComposableFilter isNotNull() => ComposableFilter.simple(column.isNotNull()); + ComposableFilter isNotNull() => ComposableFilter(column.isNotNull()); /// Create a filter that checks if the column equals a value. - ComposableFilter equals(T value) => - ComposableFilter.simple(column.equals(value)); + ComposableFilter equals(T value) => ComposableFilter(column.equals(value)); /// Create a filter that checks if the column is in a list of values. ComposableFilter isIn(List values) => - ComposableFilter.simple(column.isIn(values)); + ComposableFilter(column.isIn(values)); /// Create a filter that checks if the column is not in a list of values. ComposableFilter isNotIn(List values) => - ComposableFilter.simple(column.isNotIn(values)); + ComposableFilter(column.isNotIn(values)); /// Shortcut for [equals] - ComposableFilter call(T value) => - ComposableFilter.simple(column.equals(value)); + ComposableFilter call(T value) => ComposableFilter(column.equals(value)); /// Nested column for filtering on the count of a column ColumnFilters get count => ColumnFilters(column.count()); @@ -48,39 +46,37 @@ class ColumnFilters { /// Built in filters for bool columns extension BoolFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value - ComposableFilter isTrue(bool value) => - ComposableFilter.simple(column.equals(true)); + ComposableFilter isTrue(bool value) => ComposableFilter(column.equals(true)); /// Create a filter to check if the column is small than a value - ComposableFilter isFalse(bool value) => - ComposableFilter.simple(column.equals(true)); + ComposableFilter isFalse(bool value) => ComposableFilter(column.equals(true)); } /// Built in filters for int/double columns extension NumFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => - ComposableFilter.simple(column.isBiggerThanValue(value)); + ComposableFilter(column.isBiggerThanValue(value)); /// Create a filter to check if the column is small than a value ComposableFilter isSmallerThan(T value) => - ComposableFilter.simple(column.isSmallerThanValue(value)); + ComposableFilter(column.isSmallerThanValue(value)); /// Create a filter to check if the column is bigger or equal to a value ComposableFilter isBiggerOrEqualTo(T value) => - ComposableFilter.simple(column.isBiggerOrEqualValue(value)); + ComposableFilter(column.isBiggerOrEqualValue(value)); /// Create a filter to check if the column is small or equal to a value ComposableFilter isSmallerOrEqualTo(T value) => - ComposableFilter.simple(column.isSmallerOrEqualValue(value)); + ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between two values ComposableFilter isBetween(T lower, T higher) => - ComposableFilter.simple(column.isBetweenValues(lower, higher)); + ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between two values ComposableFilter isNotBetween(T lower, T higher) => - isBetween(lower, higher)._reversed(); + isBetween(lower, higher)._invert(); /// Nested column for filtering on the average of a column ColumnFilters get avg => ColumnFilters(column.avg()); @@ -102,27 +98,27 @@ extension NumFilters on ColumnFilters { extension BigIntFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => - ComposableFilter.simple(column.isBiggerThanValue(value)); + ComposableFilter(column.isBiggerThanValue(value)); /// Create a filter to check if the column is small than a value ComposableFilter isSmallerThan(T value) => - ComposableFilter.simple(column.isSmallerThanValue(value)); + ComposableFilter(column.isSmallerThanValue(value)); /// Create a filter to check if the column is bigger or equal to a value ComposableFilter isBiggerOrEqualTo(T value) => - ComposableFilter.simple(column.isBiggerOrEqualValue(value)); + ComposableFilter(column.isBiggerOrEqualValue(value)); /// Create a filter to check if the column is small or equal to a value ComposableFilter isSmallerOrEqualTo(T value) => - ComposableFilter.simple(column.isSmallerOrEqualValue(value)); + ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between two values ComposableFilter isBetween(T lower, T higher) => - ComposableFilter.simple(column.isBetweenValues(lower, higher)); + ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between two values ComposableFilter isNotBetween(T lower, T higher) => - isBetween(lower, higher)._reversed(); + isBetween(lower, higher)._invert(); /// Nested column for filtering on the average of a column ColumnFilters get avg => ColumnFilters(column.avg()); @@ -144,83 +140,74 @@ extension BigIntFilters on ColumnFilters { extension DateFilters on ColumnFilters { /// Create a filter to check if the column is after a [DateTime] ComposableFilter isAfter(T value) => - ComposableFilter.simple(column.isBiggerThanValue(value)); + ComposableFilter(column.isBiggerThanValue(value)); /// Create a filter to check if the column is before a [DateTime] ComposableFilter isBefore(T value) => - ComposableFilter.simple(column.isSmallerThanValue(value)); + ComposableFilter(column.isSmallerThanValue(value)); /// Create a filter to check if the column is on or after a [DateTime] ComposableFilter isAfterOrOn(T value) => - ComposableFilter.simple(column.isBiggerOrEqualValue(value)); + ComposableFilter(column.isBiggerOrEqualValue(value)); /// Create a filter to check if the column is before or on a [DateTime] ComposableFilter isBeforeOrOn(T value) => - ComposableFilter.simple(column.isSmallerOrEqualValue(value)); + ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between 2 [DateTime]s ComposableFilter isBetween(T lower, T higher) => - ComposableFilter.simple(column.isBetweenValues(lower, higher)); + ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between 2 [DateTime]s ComposableFilter isNotBetween(T lower, T higher) => - isBetween(lower, higher)._reversed(); + isBetween(lower, higher)._invert(); } /// Defines a class which is used to wrap a column with a type converter to only expose filter functions class ColumnWithTypeConverterFilters { /// Similar to [ColumnFilters] but for columns with type converters\ - ColumnWithTypeConverterFilters(this.column); + const ColumnWithTypeConverterFilters(this.column); /// Column that this [ColumnWithTypeConverterFilters] wraps - GeneratedColumnWithTypeConverter column; + final GeneratedColumnWithTypeConverter column; /// Create a filter that checks if the column is null. - ComposableFilter isNull() => ComposableFilter.simple(column.isNull()); + ComposableFilter isNull() => ComposableFilter(column.isNull()); /// Create a filter that checks if the column is not null. - ComposableFilter isNotNull() => ComposableFilter.simple(column.isNotNull()); + ComposableFilter isNotNull() => ComposableFilter(column.isNotNull()); /// Create a filter that checks if the column equals a value. ComposableFilter equals(CUSTOM value) => - ComposableFilter.simple(column.equalsValue(value)); + ComposableFilter(column.equalsValue(value)); /// Shortcut for [equals] ComposableFilter call(CUSTOM value) => - ComposableFilter.simple(column.equalsValue(value)); + ComposableFilter(column.equalsValue(value)); } -/// Defines a class that can be used to compose filters for a column -class ComposableFilter implements HasJoinBuilders { +/// This class is wrapper on the expression class +/// +/// It contains the expression, along with any joins that are required +/// to execute the expression. See [HasJoinBuilders] for more information +/// on how joins are stored +class ComposableFilter extends HasJoinBuilders { @override final Set joinBuilders; - @override - void addJoinBuilders(Set builders) { - joinBuilders.addAll(builders); - } /// The expression that will be applied to the query final Expression expression; - /// This class is wrapper on expression class - /// - /// It contains the expression, along with any joins that are required - /// to execute the expression - /// - /// It also contains a list of aliases that were used in joins - /// This will be used to ensure that the same alias isn't used twice - ComposableFilter.withJoin( - this.expression, - this.joinBuilders, - ); + /// Create a new [ComposableFilter] for a column without any joins + ComposableFilter(this.expression) : joinBuilders = {}; - // ignore: public_member_api_docs - ComposableFilter.simple(this.expression) : joinBuilders = {}; + /// Create a new [ComposableFilter] for a column with joins + ComposableFilter._(this.expression, this.joinBuilders); /// Combine two filters with an AND ComposableFilter operator &(ComposableFilter other) { - return ComposableFilter.withJoin( + return ComposableFilter._( expression & other.expression, joinBuilders.union(other.joinBuilders), ); @@ -228,15 +215,15 @@ class ComposableFilter implements HasJoinBuilders { /// Combine two filters with an OR ComposableFilter operator |(ComposableFilter other) { - return ComposableFilter.withJoin( + return ComposableFilter._( expression | other.expression, joinBuilders.union(other.joinBuilders), ); } /// Returns a copy of this filter with the expression reversed - ComposableFilter _reversed() { - return ComposableFilter.withJoin( + ComposableFilter _invert() { + return ComposableFilter._( expression.not(), joinBuilders, ); @@ -247,5 +234,5 @@ class ComposableFilter implements HasJoinBuilders { class FilterComposer extends Composer { /// Create a filter composer with an empty state - FilterComposer(super.db, super.table); + FilterComposer(super.$db, super.$table); } diff --git a/drift/lib/src/runtime/manager/join.dart b/drift/lib/src/runtime/manager/join.dart index 3d415ef8..48f41fcc 100644 --- a/drift/lib/src/runtime/manager/join.dart +++ b/drift/lib/src/runtime/manager/join.dart @@ -9,11 +9,10 @@ class JoinBuilder { /// The referenced table that will be joined final Table referencedTable; - /// The column of the current database which will be use to create the join + /// The column of the [currentTable] which will be use to create the join final GeneratedColumn currentColumn; - /// The column of the referenced database which will be use to create the join - + /// The column of the [referencedTable] which will be use to create the join final GeneratedColumn referencedColumn; /// Class that describes how a ordering that is being @@ -46,16 +45,34 @@ class JoinBuilder { /// Build a join from this join builder Join buildJoin() { return leftOuterJoin( - referencedTable, currentColumn.equalsExp(referencedColumn)); + referencedTable, currentColumn.equalsExp(referencedColumn), + useColumns: false); } } /// An interface for classes that hold join builders +/// Typically used by classes whose composition requires joins +/// to be created +/// +/// Example: +/// ```dart +/// categories.filter((f) => f.todos((f) => f.dueDate.isBefore(DateTime.now()))) +/// ``` +/// +/// In the above example, f.todos() returns a [ComposableFilter] object, which +/// is a subclass of [HasJoinBuilders]. +/// This resulting where expression will require a join to be created +/// between the `categories` and `todos` table. +/// +/// This interface is used to ensure that the [ComposableFilter] object will have +/// the information needed to create the join. @internal abstract interface class HasJoinBuilders { /// The join builders that are associated with this class Set get joinBuilders; /// Add a join builder to this class - void addJoinBuilders(Set builders); + void addJoinBuilders(Set builders) { + joinBuilders.addAll(builders); + } } diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 5249f6c0..78844010 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -24,6 +24,14 @@ class _JoinedResult } /// Defines a class that holds the state for a [BaseTableManager] +/// +/// It holds the state for manager of [T] table in [DB] database, used to return [DT] data classes/rows. +/// It holds the [FS] Filters and [OS] Orderings for the manager. +/// +/// It also holds the [CI] and [CU] functions that are used to create companion builders for inserting and updating data. +/// E.G Instead of `CategoriesCompanion.insert(name: "School")` you would use `(f) => f(name: "School")` +/// +/// The [C] generic refers to the type of the child manager that will be created when a filter/ordering is applied class TableManagerState< DB extends GeneratedDatabase, T extends Table, @@ -135,30 +143,23 @@ class TableManagerState< /// Builds a select statement with the given target columns, or all columns if none are provided _StatementType _buildSelectStatement( - {Iterable? targetColumns, - required bool addJoins, - required bool applyFilters, - required bool applyOrdering, - required bool applyLimit}) { + {Iterable? targetColumns}) { final joins = joinBuilders.map((e) => e.buildJoin()).toList(); // If there are no joins and we are returning all columns, we can use a simple select statement - if (targetColumns == null && !addJoins) { + if (joins.isEmpty && targetColumns == null) { final simpleStatement = db.select(_tableAsTableInfo, distinct: distinct ?? false); // Apply the expression to the statement - if (applyFilters && filter != null) { + if (filter != null) { simpleStatement.where((_) => filter!); } - // Add orderings - if (applyOrdering) { - simpleStatement.orderBy( - orderingBuilders.map((e) => (_) => e.buildTerm()).toList()); - } - // Set the limit and offset - if (applyLimit && limit != null) { - simpleStatement.limit(limit!, offset: offset); - } + // Apply orderings and limits + + simpleStatement + .orderBy(orderingBuilders.map((e) => (_) => e.buildTerm()).toList()); + simpleStatement.limit(limit!, offset: offset); + return _SimpleResult(simpleStatement); } else { JoinedSelectStatement joinedStatement; @@ -168,45 +169,30 @@ class TableManagerState< (db.selectOnly(_tableAsTableInfo, distinct: distinct ?? false) ..addColumns(targetColumns)); // Add the joins to the statement - if (addJoins) { - joinedStatement = - joinedStatement.join(joins) as JoinedSelectStatement; - } + joinedStatement = + joinedStatement.join(joins) as JoinedSelectStatement; } else { joinedStatement = db .select(_tableAsTableInfo, distinct: distinct ?? false) .join(joins) as JoinedSelectStatement; } // Apply the expression to the statement - if (applyFilters && filter != null) { + if (filter != null) { joinedStatement.where(filter!); } - // Add orderings - if (applyOrdering) { - joinedStatement - .orderBy(orderingBuilders.map((e) => e.buildTerm()).toList()); - } - // Set the limit and offset - if (applyLimit && limit != null) { - joinedStatement.limit(limit!, offset: offset); - } + // Apply orderings and limits + + joinedStatement + .orderBy(orderingBuilders.map((e) => e.buildTerm()).toList()); + joinedStatement.limit(limit!, offset: offset); + return _JoinedResult(joinedStatement); } } /// Build a select statement based on the manager state - Selectable
buildSelectStatement( - {Iterable? targetColumns, - bool addJoins = true, - bool applyFilters = true, - bool applyOrdering = true, - bool applyLimit = true}) { - final result = _buildSelectStatement( - targetColumns: targetColumns, - addJoins: addJoins, - applyFilters: applyFilters, - applyOrdering: applyOrdering, - applyLimit: applyLimit); + Selectable
buildSelectStatement() { + final result = _buildSelectStatement(); return switch (result) { _SimpleResult() => result.statement, _JoinedResult() => @@ -225,12 +211,8 @@ class TableManagerState< } else { updateStatement = db.update(_tableAsTableInfo); for (var col in _tableAsTableInfo.primaryKey) { - final subquery = _buildSelectStatement( - targetColumns: [col], - addJoins: true, - applyFilters: true, - applyOrdering: false, - applyLimit: false) as _JoinedResult; + final subquery = + _buildSelectStatement(targetColumns: [col]) as _JoinedResult; updateStatement.where((tbl) => col.isInQuery(subquery.statement)); } } @@ -240,12 +222,8 @@ class TableManagerState< /// Count the number of rows that would be returned by the built statement Future count() { final count = countAll(); - final result = _buildSelectStatement( - targetColumns: [count], - addJoins: true, - applyFilters: true, - applyOrdering: true, - applyLimit: true) as _JoinedResult; + final result = + _buildSelectStatement(targetColumns: [count]) as _JoinedResult; return result.statement.map((row) => row.read(count)!).getSingle(); } @@ -260,12 +238,8 @@ class TableManagerState< } else { deleteStatement = db.delete(_tableAsTableInfo); for (var col in _tableAsTableInfo.primaryKey) { - final subquery = _buildSelectStatement( - targetColumns: [col], - addJoins: true, - applyFilters: true, - applyOrdering: true, - applyLimit: true) as _JoinedResult; + final subquery = + _buildSelectStatement(targetColumns: [col]) as _JoinedResult; deleteStatement.where((tbl) => col.isInQuery(subquery.statement)); } } @@ -299,6 +273,12 @@ abstract class BaseTableManager< /// foreign key constraints). Future delete() => $state.buildDeleteStatement().go(); + /// Set the distinct flag on the statement to true + /// This will ensure that only distinct rows are returned + C distict() { + return $state._getChildManagerBuilder($state.copyWith(distinct: true)); + } + /// Add ordering to the statement C orderBy(ComposableOrdering Function(OS o) o) { final orderings = o($state.orderingComposer); diff --git a/drift/lib/src/runtime/manager/ordering.dart b/drift/lib/src/runtime/manager/ordering.dart index 7bd0893e..ed607eb5 100644 --- a/drift/lib/src/runtime/manager/ordering.dart +++ b/drift/lib/src/runtime/manager/ordering.dart @@ -5,12 +5,7 @@ class ColumnOrderings { /// This class is a wrapper on top of the generated column class /// /// It's used to expose ordering functions for a column - /// - /// ```dart - /// extension on FilterComposer{ - /// FitlerBuilder after2000() => isAfter(DateTime(2000)); - ///} - /// ``` + ColumnOrderings(this.column); /// Column that this [ColumnOrderings] wraps @@ -20,13 +15,13 @@ class ColumnOrderings { /// /// 10 -> 1 | Z -> A | Dec 31 -> Jan 1 ComposableOrdering asc() => - ComposableOrdering.simple({OrderingBuilder(OrderingMode.asc, column)}); + ComposableOrdering({OrderingBuilder(OrderingMode.asc, column)}); /// Sort this column in descending order /// /// 1 -> 10 | A -> Z | Jan 1 -> Dec 31 ComposableOrdering desc() => - ComposableOrdering.simple({OrderingBuilder(OrderingMode.desc, column)}); + ComposableOrdering({OrderingBuilder(OrderingMode.desc, column)}); } /// Defines a class which will hold the information needed to create an ordering @@ -60,18 +55,16 @@ class OrderingBuilder { /// /// Multiple orderings can be composed together using the `&` operator. /// The orderings will be executed from left to right. -class ComposableOrdering implements HasJoinBuilders { +/// See [HasJoinBuilders] for more information +/// on how joins are stored +class ComposableOrdering extends HasJoinBuilders { /// The orderings that are being composed final Set orderingBuilders; @override final Set joinBuilders; - @override - void addJoinBuilders(Set builders) { - joinBuilders.addAll(builders); - } /// Create a new [ComposableOrdering] for a column without any joins - ComposableOrdering.simple(this.orderingBuilders) : joinBuilders = {}; + ComposableOrdering(this.orderingBuilders) : joinBuilders = {}; /// Create a new [ComposableOrdering] for a column with joins ComposableOrdering._(this.orderingBuilders, this.joinBuilders); @@ -92,5 +85,5 @@ class ComposableOrdering implements HasJoinBuilders { class OrderingComposer extends Composer { /// Create an ordering composer with an empty state - OrderingComposer(super.db, super.table); + OrderingComposer(super.$db, super.$table); } diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 8f42df5f..64bd7c1f 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -37,7 +37,7 @@ class _RegularFilterWriter extends _FilterWriter { ..writeDriftRef("ColumnFilters") ..write("<$type> get $filterName =>") ..writeDriftRef("ColumnFilters") - ..write("(\$state.table.$fieldGetter);"); + ..write("(\$table.$fieldGetter);"); } } @@ -64,15 +64,15 @@ class _FilterWithConverterWriter extends _FilterWriter { ..writeDriftRef("ColumnWithTypeConverterFilters") ..write("<$converterType,$type> get $filterName =>") ..writeDriftRef("ColumnWithTypeConverterFilters") - ..writeln("(\$state.table.$fieldGetter);"); + ..writeln("(\$table.$fieldGetter);"); } } class _ReferencedFilterWriter extends _FilterWriter { /// The full function used to get the referenced table /// - /// E.G `\$state.db.resultSet<$CategoryTable>('categories')` - /// or `\$state.db.categories` + /// E.G `\$db.resultSet<$CategoryTable>('categories')` + /// or `\$db.categories` final String referencedTableField; /// The getter for the column on the referenced table @@ -105,7 +105,8 @@ class _ReferencedFilterWriter extends _FilterWriter { ..writeUriRef( Uri.parse('package:drift/internal/manager.dart'), 'composeWithJoins') ..writeln('(') - ..writeln("\$state: \$state,") + ..writeln("\$db: \$db,") + ..writeln("\$table: \$table,") ..writeln("referencedTable: $referencedTableField,") ..writeln("getCurrentColumn: (f) => f.$fieldGetter,") ..writeln("getReferencedColumn: (f) => f.$referencedColumnGetter,") @@ -150,15 +151,15 @@ class _RegularOrderingWriter extends _OrderingWriter { ..writeDriftRef("ColumnOrderings") ..write(" get $orderingName =>") ..writeDriftRef("ColumnOrderings") - ..write("(\$state.table.$fieldGetter);"); + ..write("(\$table.$fieldGetter);"); } } class _ReferencedOrderingWriter extends _OrderingWriter { /// The full function used to get the referenced table /// - /// E.G `\$state.db.resultSet<$CategoryTable>('categories')` - /// or `\$state.db.categories` + /// E.G `\$db.resultSet<$CategoryTable>('categories')` + /// or `\$db.categories` final String referencedTableField; /// The getter for the column on the referenced table @@ -189,7 +190,8 @@ class _ReferencedOrderingWriter extends _OrderingWriter { ..writeUriRef( Uri.parse('package:drift/internal/manager.dart'), 'composeWithJoins') ..writeln('(') - ..writeln("\$state: \$state,") + ..writeln("\$db: \$db,") + ..writeln("\$table: \$table,") ..writeln("referencedTable: $referencedTableField,") ..writeln("getCurrentColumn: (f) => f.$fieldGetter,") ..writeln("getReferencedColumn: (f) => f.$referencedColumnGetter,") @@ -487,8 +489,8 @@ class _TableWriter { _TableWriter(referencedTable, scope, dbScope, databaseGenericName); final referencedColumnNames = _ColumnWriter(referencedCol.nameInDart); final String referencedTableField = scope.generationOptions.isModular - ? "\$state.db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" - : "\$state.db.${referencedTable.dbGetterName}"; + ? "\$db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" + : "\$db.${referencedTable.dbGetterName}"; c.filters.add(_ReferencedFilterWriter(c.fieldGetter, fieldGetter: c.fieldGetter, @@ -515,8 +517,8 @@ class _TableWriter { _TableWriter(ot, scope, dbScope, databaseGenericName); final referencedColumnNames = _ColumnWriter(oc.nameInDart); final String referencedTableField = scope.generationOptions.isModular - ? "\$state.db.resultSet<${referencedTableNames.tableClassName}>('${ot.schemaName}')" - : "\$state.db.${ot.dbGetterName}"; + ? "\$db.resultSet<${referencedTableNames.tableClassName}>('${ot.schemaName}')" + : "\$db.${ot.dbGetterName}"; final filterName = oc.referenceName ?? "${referencedTableNames.table.dbGetterName}Refs"; From 698741c0923defd085fbc2c611c1d5b43547f420 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 20:22:19 -0400 Subject: [PATCH 37/74] Docs --- docs/pages/docs/Generation options/index.md | 68 +++++++++++---------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/docs/pages/docs/Generation options/index.md b/docs/pages/docs/Generation options/index.md index 0246caa1..ea864d4e 100644 --- a/docs/pages/docs/Generation options/index.md +++ b/docs/pages/docs/Generation options/index.md @@ -6,7 +6,7 @@ data: template: layouts/docs/list path: docs/advanced-features/builder_options/ aliases: - - "options/" + - "options/" --- The `drift_dev` package supports a range of options that control how code @@ -19,6 +19,7 @@ advice on which options to use. To use the options, create a `build.yaml` file in the root of your project (e.g. next to your `pubspec.yaml`): + ```yaml # build.yaml. This file is quite powerful, see https://pub.dev/packages/build_config @@ -34,59 +35,60 @@ targets: At the moment, drift supports these options: -* `write_from_json_string_constructor`: boolean. Adds a `.fromJsonString` factory - constructor to generated data classes. By default, we only write a `.fromJson` - constructor that takes a `Map`. -* `override_hash_and_equals_in_result_sets`: boolean. When drift generates another class - to hold the result of generated select queries, this flag controls whether drift should - override `operator ==` and `hashCode` in those classes. In recent versions, it will also - override `toString` if this option is enabled. -* `skip_verification_code`: Generated tables contain a significant chunk of code to verify integrity +- `write_from_json_string_constructor`: boolean. Adds a `.fromJsonString` factory + constructor to generated data classes. By default, we only write a `.fromJson` + constructor that takes a `Map`. +- `override_hash_and_equals_in_result_sets`: boolean. When drift generates another class + to hold the result of generated select queries, this flag controls whether drift should + override `operator ==` and `hashCode` in those classes. In recent versions, it will also + override `toString` if this option is enabled. +- `skip_verification_code`: Generated tables contain a significant chunk of code to verify integrity of inserted data and report detailed errors when the integrity is violated. If you're only using inserts with SQL, or don't need this functionality, enabling this flag can help to reduce the amount generated code. -* `use_data_class_name_for_companions`: By default, the name for [companion classes]({{ "../Dart API/writes.md#updates-and-deletes" | pageUrl }}) +- `use_data_class_name_for_companions`: By default, the name for [companion classes]({{ "../Dart API/writes.md#updates-and-deletes" | pageUrl }}) is based on the table name (e.g. a `@DataClassName('Users') class UsersTable extends Table` would generate a `UsersTableCompanion`). With this option, the name is based on the data class (so `UsersCompanion` in this case). -* `use_column_name_as_json_key_when_defined_in_moor_file` (defaults to `true`): When serializing columns declared inside a +- `use_column_name_as_json_key_when_defined_in_moor_file` (defaults to `true`): When serializing columns declared inside a `.drift` file from and to json, use their sql name instead of the generated Dart getter name (so a column named `user_name` would also use `user_name` as a json key instead of `userName`). You can always override the json key by using a `JSON KEY` column constraint (e.g. `user_name VARCHAR NOT NULL JSON KEY userName`). -* `use_sql_column_name_as_json_key` (defaults to false): Uses the column name in SQL as the JSON key for serialization, +- `use_sql_column_name_as_json_key` (defaults to false): Uses the column name in SQL as the JSON key for serialization, regardless of whether the table was defined in a drift file or not. -* `generate_connect_constructor` (deprecated): Generates a named `connect()` constructor on database classes +- `generate_connect_constructor` (deprecated): Generates a named `connect()` constructor on database classes that takes a `DatabaseConnection` instead of a `QueryExecutor`. This option was deprecated in drift 2.5 because `DatabaseConnection` now implements `QueryExecutor`. -* `data_class_to_companions` (defaults to `true`): Controls whether drift will write the `toCompanion` method in generated - data classes. -* `mutable_classes` (defaults to `false`): The fields generated in generated data, companion and result set classes are final +- `data_class_to_companions` (defaults to `true`): Controls whether drift will write the `toCompanion` method in generated + data classes. +- `mutable_classes` (defaults to `false`): The fields generated in generated data, companion and result set classes are final by default. You can make them mutable by setting `mutable_classes: true`. -* `raw_result_set_data`: The generator will expose the underlying `QueryRow` for generated result set classes -* `apply_converters_on_variables` (defaults to `true`): Applies type converters to variables in compiled statements. -* `generate_values_in_copy_with` (defaults to `true`): Generates a `Value` instead of `T?` for nullable columns in `copyWith`. This allows to set +- `raw_result_set_data`: The generator will expose the underlying `QueryRow` for generated result set classes +- `apply_converters_on_variables` (defaults to `true`): Applies type converters to variables in compiled statements. +- `generate_values_in_copy_with` (defaults to `true`): Generates a `Value` instead of `T?` for nullable columns in `copyWith`. This allows to set columns back to null (by using `Value(null)`). Passing `null` was ignored before, making it impossible to set columns to `null`. -* `named_parameters`: Generates named parameters for named variables in SQL queries. -* `named_parameters_always_required`: All named parameters (generated if `named_parameters` option is `true`) will be required in Dart. -* `scoped_dart_components` (defaults to `true`): Generates a function parameter for [Dart placeholders]({{ '../SQL API/drift_files.md#dart-components-in-sql' | pageUrl }}) in SQL. +- `named_parameters`: Generates named parameters for named variables in SQL queries. +- `named_parameters_always_required`: All named parameters (generated if `named_parameters` option is `true`) will be required in Dart. +- `scoped_dart_components` (defaults to `true`): Generates a function parameter for [Dart placeholders]({{ '../SQL API/drift_files.md#dart-components-in-sql' | pageUrl }}) in SQL. The function has a parameter for each table that is available in the query, making it easier to get aliases right when using Dart placeholders. -* `store_date_time_values_as_text`: Whether date-time columns should be stored as ISO 8601 string instead of a unix timestamp. +- `store_date_time_values_as_text`: Whether date-time columns should be stored as ISO 8601 string instead of a unix timestamp. For more information on these modes, see [datetime options]({{ '../Dart API/tables.md#datetime-options' | pageUrl }}). -* `case_from_dart_to_sql` (defaults to `snake_case`): Controls how the table and column names are re-cased from the Dart identifiers. - The possible values are `preserve`, `camelCase`, `CONSTANT_CASE`, `snake_case`, `PascalCase`, `lowercase` and `UPPERCASE` (default: `snake_case`). -* `write_to_columns_mixins`: Whether the `toColumns` method should be written as a mixin instead of being added directly to the data class. - This is useful when using [existing row classes]({{ '../custom_row_classes.md' | pageUrl }}), as the mixin is generated for those as well. -* `has_separate_analyzer`: This option is only relevant when using the `drift_dev:not_shared` builder, which needs to use a less efficient +- `case_from_dart_to_sql` (defaults to `snake_case`): Controls how the table and column names are re-cased from the Dart identifiers. + The possible values are `preserve`, `camelCase`, `CONSTANT_CASE`, `snake_case`, `PascalCase`, `lowercase` and `UPPERCASE` (default: `snake_case`). +- `write_to_columns_mixins`: Whether the `toColumns` method should be written as a mixin instead of being added directly to the data class. + This is useful when using [existing row classes]({{ '../custom_row_classes.md' | pageUrl }}), as the mixin is generated for those as well. +- `has_separate_analyzer`: This option is only relevant when using the `drift_dev:not_shared` builder, which needs to use a less efficient analysis implementation than the other builders by default. After also applying `drift_dev:analyzer` to the same build target, this option can be enabled to speed up builds. This option has no effect with the default or the modular builder. -* `fatal_warnings`: When enabled (defaults to `false`), warnings found by `drift_dev` in the build process (like syntax errors in SQL queries or +- `fatal_warnings`: When enabled (defaults to `false`), warnings found by `drift_dev` in the build process (like syntax errors in SQL queries or unresolved references in your Dart tables) will cause the build to fail. -* `preamble`: This option is useful when using drift [as a standalone part builder](#using-drift-classes-in-other-builders) or when running a +- `preamble`: This option is useful when using drift [as a standalone part builder](#using-drift-classes-in-other-builders) or when running a [modular build](#modular-code-generation). In these setups, the `preamble` option defined by the [source_gen package](https://pub.dev/packages/source_gen#preamble) would have no effect, which is why it has been added as an option for the drift builders. +- `generate_manager`: When enabled (defaults to `true`), managers will be generated for each table in the database. These managers help perform simple actions without boilerplate. ## Assumed SQL environment @@ -151,7 +153,7 @@ targets: ### Available extensions -__Note__: This enables extensions in the analyzer for custom queries only. For instance, when the `json1` extension is +**Note**: This enables extensions in the analyzer for custom queries only. For instance, when the `json1` extension is enabled, the [`json`](https://www.sqlite.org/json1.html) functions can be used in drift files. This doesn't necessarily mean that those functions are supported at runtime! Both extensions are available on iOS 11 or later. On Android, they're only available when using a `NativeDatabase`. @@ -176,9 +178,9 @@ We currently support the following extensions: - [json1](https://www.sqlite.org/json1.html): Support static analysis for `json_` functions in moor files - [fts5](https://www.sqlite.org/fts5.html): Support `CREATE VIRTUAL TABLE` statements for `fts5` tables and the `MATCH` operator. Functions like `highlight` or `bm25` are available as well. -- `rtree`: Static analysis support for the [R*Tree](https://www.sqlite.org/rtree.html) extension. +- `rtree`: Static analysis support for the [R\*Tree](https://www.sqlite.org/rtree.html) extension. Enabling this option is safe when using a `NativeDatabase` with `sqlite3_flutter_libs`, - which compiles sqlite3 with the R*Tree extension enabled. + which compiles sqlite3 with the R\*Tree extension enabled. - `moor_ffi`: Enables support for functions that are only available when using a `NativeDatabase`. This contains `pow`, `sqrt` and a variety of trigonometric functions. Details on those functions are available [here]({{ "../Platforms/vm.md#moor-only-functions" | pageUrl }}). - `math`: Assumes that sqlite3 was compiled with [math functions](https://www.sqlite.org/lang_mathfunc.html). From 585e93881ac2d9c80d2de6b0706fc1be9c3a2b00 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 21:05:29 -0400 Subject: [PATCH 38/74] null bug --- drift/lib/src/runtime/manager/manager.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 78844010..57849a9f 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -158,7 +158,9 @@ class TableManagerState< simpleStatement .orderBy(orderingBuilders.map((e) => (_) => e.buildTerm()).toList()); - simpleStatement.limit(limit!, offset: offset); + if (limit != null) { + simpleStatement.limit(limit!, offset: offset); + } return _SimpleResult(simpleStatement); } else { @@ -184,7 +186,9 @@ class TableManagerState< joinedStatement .orderBy(orderingBuilders.map((e) => e.buildTerm()).toList()); - joinedStatement.limit(limit!, offset: offset); + if (limit != null) { + joinedStatement.limit(limit!, offset: offset); + } return _JoinedResult(joinedStatement); } From 59e1e00ae0abf8a212ee32ccd5920ad2c68b9ba1 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 21:23:54 -0400 Subject: [PATCH 39/74] another bug --- drift/lib/src/runtime/manager/manager.dart | 29 ++++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 57849a9f..789dbcbb 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -323,9 +323,8 @@ abstract class BaseTableManager< /// /// See also: [RootTableManager.replace], which does not require [filter] statements and /// supports setting fields back to null. - Future write(DT Function(CU o) f) => $state - .buildUpdateStatement() - .write(f($state._getUpdateCompanionBuilder) as Insertable
); + Future write(Insertable
Function(CU o) f) => + $state.buildUpdateStatement().write(f($state._getUpdateCompanionBuilder)); } /// A table manager that can be used to select rows from a table @@ -437,10 +436,10 @@ abstract class RootTableManager< /// /// If the table doesn't have a `rowid`, you can't rely on the return value. /// Still, the future will always complete with an error if the insert fails. - Future create(D Function(CI o) f, + Future create(Insertable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insert( - f($state._getInsertCompanionBuilder) as Insertable, + f($state._getInsertCompanionBuilder), mode: mode, onConflict: onConflict); } @@ -453,10 +452,10 @@ abstract class RootTableManager< /// exception in that case. Use [createReturningOrNull] when performing an /// insert with an insert mode like [InsertMode.insertOrIgnore] or when using /// a [DoUpdate] with a `where` clause clause. - Future createReturning(D Function(CI o) f, + Future createReturning(Insertable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insertReturning( - f($state._getInsertCompanionBuilder) as Insertable, + f($state._getInsertCompanionBuilder), mode: mode, onConflict: onConflict); } @@ -466,10 +465,10 @@ abstract class RootTableManager< /// When no row was inserted and no exception was thrown, for instance because /// [InsertMode.insertOrIgnore] was used or because the upsert clause had a /// `where` clause that didn't match, `null` is returned instead. - Future createReturningOrNull(D Function(CI o) f, + Future createReturningOrNull(Insertable Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { return $state.db.into($state._tableAsTableInfo).insertReturningOrNull( - f($state._getInsertCompanionBuilder) as Insertable, + f($state._getInsertCompanionBuilder), mode: mode, onConflict: onConflict); } @@ -486,10 +485,10 @@ abstract class RootTableManager< /// checks. /// [onConflict] can be used to create an upsert clause for engines that /// support it. For details and examples, see [InsertStatement.insert]. - Future bulkCreate(Iterable Function(CI o) f, + Future bulkCreate(Iterable> Function(CI o) f, {InsertMode? mode, UpsertClause? onConflict}) { - return $state.db.batch((b) => b.insertAll($state._tableAsTableInfo, - f($state._getInsertCompanionBuilder) as Iterable>, + return $state.db.batch((b) => b.insertAll( + $state._tableAsTableInfo, f($state._getInsertCompanionBuilder), mode: mode, onConflict: onConflict)); } @@ -505,9 +504,7 @@ abstract class RootTableManager< /// ignores such fields without changing them in the database. /// /// Returns true if a row was affected by this operation. - Future replace(D entity) { - return $state.db - .update($state._tableAsTableInfo) - .replace(entity as Insertable); + Future replace(Insertable entity) { + return $state.db.update($state._tableAsTableInfo).replace(entity); } } From c383a9d173d6d9b329298ff2af62c9d45bf6b33b Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 3 Apr 2024 22:48:28 -0400 Subject: [PATCH 40/74] Add example --- drift/lib/src/runtime/manager/filter.dart | 4 +- drift/lib/src/runtime/manager/manager.dart | 22 +- examples/manager/.gitignore | 43 + examples/manager/.metadata | 45 + examples/manager/README.md | 16 + examples/manager/analysis_options.yaml | 28 + examples/manager/android/.gitignore | 13 + examples/manager/android/app/build.gradle | 67 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 44 + .../com/example/manager/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + examples/manager/android/build.gradle | 18 + examples/manager/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + examples/manager/android/settings.gradle | 26 + examples/manager/ios/.gitignore | 34 + .../ios/Flutter/AppFrameworkInfo.plist | 26 + examples/manager/ios/Flutter/Debug.xcconfig | 1 + examples/manager/ios/Flutter/Release.xcconfig | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 616 ++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + examples/manager/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + examples/manager/ios/Runner/Info.plist | 49 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../manager/ios/RunnerTests/RunnerTests.swift | 12 + examples/manager/lib/database.dart | 43 + examples/manager/lib/database.g.dart | 1405 +++++++++++++++++ examples/manager/lib/main.dart | 64 + examples/manager/lib/pages/listings.dart | 152 ++ examples/manager/lib/pages/owners.dart | 72 + examples/manager/lib/pages/product.dart | 112 ++ examples/manager/lib/pages/store.dart | 112 ++ examples/manager/lib/tables.dart | 41 + examples/manager/linux/.gitignore | 1 + examples/manager/linux/CMakeLists.txt | 145 ++ examples/manager/linux/flutter/CMakeLists.txt | 88 ++ .../flutter/generated_plugin_registrant.cc | 15 + .../flutter/generated_plugin_registrant.h | 15 + .../linux/flutter/generated_plugins.cmake | 24 + examples/manager/linux/main.cc | 6 + examples/manager/linux/my_application.cc | 124 ++ examples/manager/linux/my_application.h | 18 + examples/manager/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 14 + .../macos/Runner.xcodeproj/project.pbxproj | 705 +++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../manager/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 ++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + examples/manager/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../manager/macos/Runner/Release.entitlements | 8 + .../macos/RunnerTests/RunnerTests.swift | 12 + examples/manager/pubspec.yaml | 41 + examples/manager/test/widget_test.dart | 30 + examples/manager/web/favicon.png | Bin 0 -> 917 bytes examples/manager/web/icons/Icon-192.png | Bin 0 -> 5292 bytes examples/manager/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../manager/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../manager/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes examples/manager/web/index.html | 59 + examples/manager/web/manifest.json | 35 + examples/manager/windows/.gitignore | 17 + examples/manager/windows/CMakeLists.txt | 108 ++ .../manager/windows/flutter/CMakeLists.txt | 109 ++ .../flutter/generated_plugin_registrant.cc | 14 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 24 + .../manager/windows/runner/CMakeLists.txt | 40 + examples/manager/windows/runner/Runner.rc | 121 ++ .../manager/windows/runner/flutter_window.cpp | 71 + .../manager/windows/runner/flutter_window.h | 33 + examples/manager/windows/runner/main.cpp | 43 + examples/manager/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + examples/manager/windows/runner/utils.cpp | 65 + examples/manager/windows/runner/utils.h | 19 + .../manager/windows/runner/win32_window.cpp | 288 ++++ .../manager/windows/runner/win32_window.h | 102 ++ 136 files changed, 6600 insertions(+), 9 deletions(-) create mode 100644 examples/manager/.gitignore create mode 100644 examples/manager/.metadata create mode 100644 examples/manager/README.md create mode 100644 examples/manager/analysis_options.yaml create mode 100644 examples/manager/android/.gitignore create mode 100644 examples/manager/android/app/build.gradle create mode 100644 examples/manager/android/app/src/debug/AndroidManifest.xml create mode 100644 examples/manager/android/app/src/main/AndroidManifest.xml create mode 100644 examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt create mode 100644 examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 examples/manager/android/app/src/main/res/drawable/launch_background.xml create mode 100644 examples/manager/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 examples/manager/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 examples/manager/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 examples/manager/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 examples/manager/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 examples/manager/android/app/src/main/res/values-night/styles.xml create mode 100644 examples/manager/android/app/src/main/res/values/styles.xml create mode 100644 examples/manager/android/app/src/profile/AndroidManifest.xml create mode 100644 examples/manager/android/build.gradle create mode 100644 examples/manager/android/gradle.properties create mode 100644 examples/manager/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 examples/manager/android/settings.gradle create mode 100644 examples/manager/ios/.gitignore create mode 100644 examples/manager/ios/Flutter/AppFrameworkInfo.plist create mode 100644 examples/manager/ios/Flutter/Debug.xcconfig create mode 100644 examples/manager/ios/Flutter/Release.xcconfig create mode 100644 examples/manager/ios/Runner.xcodeproj/project.pbxproj create mode 100644 examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 examples/manager/ios/Runner/AppDelegate.swift create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 examples/manager/ios/Runner/Base.lproj/Main.storyboard create mode 100644 examples/manager/ios/Runner/Info.plist create mode 100644 examples/manager/ios/Runner/Runner-Bridging-Header.h create mode 100644 examples/manager/ios/RunnerTests/RunnerTests.swift create mode 100644 examples/manager/lib/database.dart create mode 100644 examples/manager/lib/database.g.dart create mode 100644 examples/manager/lib/main.dart create mode 100644 examples/manager/lib/pages/listings.dart create mode 100644 examples/manager/lib/pages/owners.dart create mode 100644 examples/manager/lib/pages/product.dart create mode 100644 examples/manager/lib/pages/store.dart create mode 100644 examples/manager/lib/tables.dart create mode 100644 examples/manager/linux/.gitignore create mode 100644 examples/manager/linux/CMakeLists.txt create mode 100644 examples/manager/linux/flutter/CMakeLists.txt create mode 100644 examples/manager/linux/flutter/generated_plugin_registrant.cc create mode 100644 examples/manager/linux/flutter/generated_plugin_registrant.h create mode 100644 examples/manager/linux/flutter/generated_plugins.cmake create mode 100644 examples/manager/linux/main.cc create mode 100644 examples/manager/linux/my_application.cc create mode 100644 examples/manager/linux/my_application.h create mode 100644 examples/manager/macos/.gitignore create mode 100644 examples/manager/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 examples/manager/macos/Flutter/Flutter-Release.xcconfig create mode 100644 examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 examples/manager/macos/Runner.xcodeproj/project.pbxproj create mode 100644 examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/manager/macos/Runner/AppDelegate.swift create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 examples/manager/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 examples/manager/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 examples/manager/macos/Runner/Configs/Debug.xcconfig create mode 100644 examples/manager/macos/Runner/Configs/Release.xcconfig create mode 100644 examples/manager/macos/Runner/Configs/Warnings.xcconfig create mode 100644 examples/manager/macos/Runner/DebugProfile.entitlements create mode 100644 examples/manager/macos/Runner/Info.plist create mode 100644 examples/manager/macos/Runner/MainFlutterWindow.swift create mode 100644 examples/manager/macos/Runner/Release.entitlements create mode 100644 examples/manager/macos/RunnerTests/RunnerTests.swift create mode 100644 examples/manager/pubspec.yaml create mode 100644 examples/manager/test/widget_test.dart create mode 100644 examples/manager/web/favicon.png create mode 100644 examples/manager/web/icons/Icon-192.png create mode 100644 examples/manager/web/icons/Icon-512.png create mode 100644 examples/manager/web/icons/Icon-maskable-192.png create mode 100644 examples/manager/web/icons/Icon-maskable-512.png create mode 100644 examples/manager/web/index.html create mode 100644 examples/manager/web/manifest.json create mode 100644 examples/manager/windows/.gitignore create mode 100644 examples/manager/windows/CMakeLists.txt create mode 100644 examples/manager/windows/flutter/CMakeLists.txt create mode 100644 examples/manager/windows/flutter/generated_plugin_registrant.cc create mode 100644 examples/manager/windows/flutter/generated_plugin_registrant.h create mode 100644 examples/manager/windows/flutter/generated_plugins.cmake create mode 100644 examples/manager/windows/runner/CMakeLists.txt create mode 100644 examples/manager/windows/runner/Runner.rc create mode 100644 examples/manager/windows/runner/flutter_window.cpp create mode 100644 examples/manager/windows/runner/flutter_window.h create mode 100644 examples/manager/windows/runner/main.cpp create mode 100644 examples/manager/windows/runner/resource.h create mode 100644 examples/manager/windows/runner/resources/app_icon.ico create mode 100644 examples/manager/windows/runner/runner.exe.manifest create mode 100644 examples/manager/windows/runner/utils.cpp create mode 100644 examples/manager/windows/runner/utils.h create mode 100644 examples/manager/windows/runner/win32_window.cpp create mode 100644 examples/manager/windows/runner/win32_window.h diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 7f92e5e4..71b9ba74 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -29,11 +29,11 @@ class ColumnFilters { /// Create a filter that checks if the column is in a list of values. - ComposableFilter isIn(List values) => + ComposableFilter isIn(Iterable values) => ComposableFilter(column.isIn(values)); /// Create a filter that checks if the column is not in a list of values. - ComposableFilter isNotIn(List values) => + ComposableFilter isNotIn(Iterable values) => ComposableFilter(column.isNotIn(values)); /// Shortcut for [equals] diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 789dbcbb..0c98b7b8 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -270,13 +270,6 @@ abstract class BaseTableManager< /// Create a new [BaseTableManager] instance const BaseTableManager(this.$state); - /// Deletes all rows matched by built statement - /// - /// Returns the amount of rows that were deleted by this statement directly - /// (not including additional rows that might be affected through triggers or - /// foreign key constraints). - Future delete() => $state.buildDeleteStatement().go(); - /// Set the distinct flag on the statement to true /// This will ensure that only distinct rows are returned C distict() { @@ -345,6 +338,13 @@ abstract class ProcessedTableManager< /// Create a new [ProcessedTableManager] instance const ProcessedTableManager(super.$state); + /// Deletes all rows matched by built statement + /// + /// Returns the amount of rows that were deleted by this statement directly + /// (not including additional rows that might be affected through triggers or + /// foreign key constraints). + Future delete() => $state.buildDeleteStatement().go(); + /// Executes this statement, like [get], but only returns one /// value. If the query returns no or too many rows, the returned future will /// complete with an error. @@ -412,6 +412,14 @@ abstract class RootTableManager< /// Create a new [RootTableManager] instance const RootTableManager(super.$state); + /// Deletes all rows matched by built statement + /// + /// Returns the amount of rows that were deleted by this statement directly + /// (not including additional rows that might be affected through triggers or + /// foreign key constraints). + Future delete(Insertable entity) => + $state.db.delete($state._tableAsTableInfo).delete(entity); + /// Select all rows from the table C all() => $state._getChildManagerBuilder($state); diff --git a/examples/manager/.gitignore b/examples/manager/.gitignore new file mode 100644 index 00000000..29a3a501 --- /dev/null +++ b/examples/manager/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/examples/manager/.metadata b/examples/manager/.metadata new file mode 100644 index 00000000..aa90aa80 --- /dev/null +++ b/examples/manager/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ba393198430278b6595976de84fe170f553cc728" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + - platform: android + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + - platform: ios + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + - platform: linux + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + - platform: macos + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + - platform: web + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + - platform: windows + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/examples/manager/README.md b/examples/manager/README.md new file mode 100644 index 00000000..9c4cd3f9 --- /dev/null +++ b/examples/manager/README.md @@ -0,0 +1,16 @@ +# manager + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/examples/manager/analysis_options.yaml b/examples/manager/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/examples/manager/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/examples/manager/android/.gitignore b/examples/manager/android/.gitignore new file mode 100644 index 00000000..6f568019 --- /dev/null +++ b/examples/manager/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/examples/manager/android/app/build.gradle b/examples/manager/android/app/build.gradle new file mode 100644 index 00000000..0dd94dc2 --- /dev/null +++ b/examples/manager/android/app/build.gradle @@ -0,0 +1,67 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "com.example.manager" + compileSdk flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.manager" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies {} diff --git a/examples/manager/android/app/src/debug/AndroidManifest.xml b/examples/manager/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/examples/manager/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/examples/manager/android/app/src/main/AndroidManifest.xml b/examples/manager/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..18ab6af0 --- /dev/null +++ b/examples/manager/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt b/examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt new file mode 100644 index 00000000..c062d662 --- /dev/null +++ b/examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.manager + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml b/examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/examples/manager/android/app/src/main/res/drawable/launch_background.xml b/examples/manager/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/examples/manager/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/examples/manager/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/examples/manager/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/examples/manager/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/examples/manager/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/examples/manager/android/app/src/main/res/values-night/styles.xml b/examples/manager/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..06952be7 --- /dev/null +++ b/examples/manager/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/examples/manager/android/app/src/main/res/values/styles.xml b/examples/manager/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..cb1ef880 --- /dev/null +++ b/examples/manager/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/examples/manager/android/app/src/profile/AndroidManifest.xml b/examples/manager/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/examples/manager/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/examples/manager/android/build.gradle b/examples/manager/android/build.gradle new file mode 100644 index 00000000..bc157bd1 --- /dev/null +++ b/examples/manager/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/examples/manager/android/gradle.properties b/examples/manager/android/gradle.properties new file mode 100644 index 00000000..598d13fe --- /dev/null +++ b/examples/manager/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G +android.useAndroidX=true +android.enableJetifier=true diff --git a/examples/manager/android/gradle/wrapper/gradle-wrapper.properties b/examples/manager/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..e1ca574e --- /dev/null +++ b/examples/manager/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/examples/manager/android/settings.gradle b/examples/manager/android/settings.gradle new file mode 100644 index 00000000..1d6d19b7 --- /dev/null +++ b/examples/manager/android/settings.gradle @@ -0,0 +1,26 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/examples/manager/ios/.gitignore b/examples/manager/ios/.gitignore new file mode 100644 index 00000000..7a7f9873 --- /dev/null +++ b/examples/manager/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/examples/manager/ios/Flutter/AppFrameworkInfo.plist b/examples/manager/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..7c569640 --- /dev/null +++ b/examples/manager/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/examples/manager/ios/Flutter/Debug.xcconfig b/examples/manager/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/examples/manager/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/examples/manager/ios/Flutter/Release.xcconfig b/examples/manager/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/examples/manager/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/examples/manager/ios/Runner.xcodeproj/project.pbxproj b/examples/manager/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..8e53c3cd --- /dev/null +++ b/examples/manager/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,616 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..8e3ca5df --- /dev/null +++ b/examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata b/examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/examples/manager/ios/Runner/AppDelegate.swift b/examples/manager/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/examples/manager/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard b/examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/manager/ios/Runner/Base.lproj/Main.storyboard b/examples/manager/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/examples/manager/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/manager/ios/Runner/Info.plist b/examples/manager/ios/Runner/Info.plist new file mode 100644 index 00000000..d2d86816 --- /dev/null +++ b/examples/manager/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Manager + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + manager + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/examples/manager/ios/Runner/Runner-Bridging-Header.h b/examples/manager/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/examples/manager/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/examples/manager/ios/RunnerTests/RunnerTests.swift b/examples/manager/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/examples/manager/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/examples/manager/lib/database.dart b/examples/manager/lib/database.dart new file mode 100644 index 00000000..a5033912 --- /dev/null +++ b/examples/manager/lib/database.dart @@ -0,0 +1,43 @@ +import 'dart:io'; + +import 'package:drift/drift.dart'; +import 'package:drift/internal/manager.dart'; +import 'package:drift/native.dart'; +import 'package:flutter/material.dart' show Color; +import 'package:manager/tables.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart' as p; +import 'package:sqlite3/sqlite3.dart'; +import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart'; +part 'database.g.dart'; + +@DriftDatabase(tables: [Products, Listings, Store, Owner]) +class AppDatabase extends _$AppDatabase { + AppDatabase() : super(connect()); + + @override + int get schemaVersion => 1; +} + +LazyDatabase connect() { + return LazyDatabase(() async { + // put the database file, called db.sqlite here, into the documents folder + // for your app. + final dbFolder = await getApplicationDocumentsDirectory(); + final file = File(p.join(dbFolder.path, 'db1.sqlite')); + + // Also work around limitations on old Android versions + if (Platform.isAndroid) { + await applyWorkaroundToOpenSqlite3OnOldAndroidVersions(); + } + + // Make sqlite3 pick a more suitable location for temporary files - the + // one from the system may be inaccessible due to sandboxing. + final cachebase = (await getTemporaryDirectory()).path; + // We can't access /tmp on Android, which sqlite3 would try by default. + // Explicitly tell it about the correct temporary directory. + sqlite3.tempDirectory = cachebase; + + return NativeDatabase.createInBackground(file); + }); +} diff --git a/examples/manager/lib/database.g.dart b/examples/manager/lib/database.g.dart new file mode 100644 index 00000000..0a6e545a --- /dev/null +++ b/examples/manager/lib/database.g.dart @@ -0,0 +1,1405 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ignore_for_file: type=lint +class $ProductsTable extends Products with TableInfo<$ProductsTable, Product> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $ProductsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + additionalChecks: + GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), + type: DriftSqlType.string, + requiredDuringInsert: true); + static const VerificationMeta _descriptionMeta = + const VerificationMeta('description'); + @override + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, false, + additionalChecks: + GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), + type: DriftSqlType.string, + requiredDuringInsert: true); + static const VerificationMeta _releaseDateMeta = + const VerificationMeta('releaseDate'); + @override + late final GeneratedColumn releaseDate = GeneratedColumn( + 'release_date', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); + static const VerificationMeta _colorMeta = const VerificationMeta('color'); + @override + late final GeneratedColumnWithTypeConverter color = + GeneratedColumn('color', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true) + .withConverter($ProductsTable.$convertercolor); + @override + List get $columns => + [id, name, description, releaseDate, color]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'products'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = 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('description')) { + context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta)); + } else if (isInserting) { + context.missing(_descriptionMeta); + } + if (data.containsKey('release_date')) { + context.handle( + _releaseDateMeta, + releaseDate.isAcceptableOrUnknown( + data['release_date']!, _releaseDateMeta)); + } else if (isInserting) { + context.missing(_releaseDateMeta); + } + context.handle(_colorMeta, const VerificationResult.success()); + return context; + } + + @override + Set get $primaryKey => {id}; + @override + Product map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return Product( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description'])!, + releaseDate: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}release_date'])!, + color: $ProductsTable.$convertercolor.fromSql(attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}color'])!), + ); + } + + @override + $ProductsTable createAlias(String alias) { + return $ProductsTable(attachedDatabase, alias); + } + + static TypeConverter $convertercolor = const ColorConverter(); +} + +class Product extends DataClass implements Insertable { + final int id; + final String name; + final String description; + final DateTime releaseDate; + final Color color; + const Product( + {required this.id, + required this.name, + required this.description, + required this.releaseDate, + required this.color}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['release_date'] = Variable(releaseDate); + { + map['color'] = Variable($ProductsTable.$convertercolor.toSql(color)); + } + return map; + } + + ProductsCompanion toCompanion(bool nullToAbsent) { + return ProductsCompanion( + id: Value(id), + name: Value(name), + description: Value(description), + releaseDate: Value(releaseDate), + color: Value(color), + ); + } + + factory Product.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return Product( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + releaseDate: serializer.fromJson(json['releaseDate']), + color: serializer.fromJson(json['color']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'releaseDate': serializer.toJson(releaseDate), + 'color': serializer.toJson(color), + }; + } + + Product copyWith( + {int? id, + String? name, + String? description, + DateTime? releaseDate, + Color? color}) => + Product( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + releaseDate: releaseDate ?? this.releaseDate, + color: color ?? this.color, + ); + @override + String toString() { + return (StringBuffer('Product(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('releaseDate: $releaseDate, ') + ..write('color: $color') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, description, releaseDate, color); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is Product && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.releaseDate == this.releaseDate && + other.color == this.color); +} + +class ProductsCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value releaseDate; + final Value color; + const ProductsCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.releaseDate = const Value.absent(), + this.color = const Value.absent(), + }); + ProductsCompanion.insert({ + this.id = const Value.absent(), + required String name, + required String description, + required DateTime releaseDate, + required Color color, + }) : name = Value(name), + description = Value(description), + releaseDate = Value(releaseDate), + color = Value(color); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? releaseDate, + Expression? color, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (releaseDate != null) 'release_date': releaseDate, + if (color != null) 'color': color, + }); + } + + ProductsCompanion copyWith( + {Value? id, + Value? name, + Value? description, + Value? releaseDate, + Value? color}) { + return ProductsCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + releaseDate: releaseDate ?? this.releaseDate, + color: color ?? this.color, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (releaseDate.present) { + map['release_date'] = Variable(releaseDate.value); + } + if (color.present) { + map['color'] = + Variable($ProductsTable.$convertercolor.toSql(color.value)); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ProductsCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('releaseDate: $releaseDate, ') + ..write('color: $color') + ..write(')')) + .toString(); + } +} + +class $OwnerTable extends Owner with TableInfo<$OwnerTable, OwnerData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $OwnerTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + additionalChecks: + GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), + type: DriftSqlType.string, + requiredDuringInsert: true); + @override + List get $columns => [id, name]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'owner'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = 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); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + OwnerData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return OwnerData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + ); + } + + @override + $OwnerTable createAlias(String alias) { + return $OwnerTable(attachedDatabase, alias); + } +} + +class OwnerData extends DataClass implements Insertable { + final int id; + final String name; + const OwnerData({required this.id, required this.name}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + return map; + } + + OwnerCompanion toCompanion(bool nullToAbsent) { + return OwnerCompanion( + id: Value(id), + name: Value(name), + ); + } + + factory OwnerData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return OwnerData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + }; + } + + OwnerData copyWith({int? id, String? name}) => OwnerData( + id: id ?? this.id, + name: name ?? this.name, + ); + @override + String toString() { + return (StringBuffer('OwnerData(') + ..write('id: $id, ') + ..write('name: $name') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is OwnerData && other.id == this.id && other.name == this.name); +} + +class OwnerCompanion extends UpdateCompanion { + final Value id; + final Value name; + const OwnerCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + }); + OwnerCompanion.insert({ + this.id = const Value.absent(), + required String name, + }) : name = Value(name); + static Insertable custom({ + Expression? id, + Expression? name, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + }); + } + + OwnerCompanion copyWith({Value? id, Value? name}) { + return OwnerCompanion( + id: id ?? this.id, + name: name ?? this.name, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('OwnerCompanion(') + ..write('id: $id, ') + ..write('name: $name') + ..write(')')) + .toString(); + } +} + +class $StoreTable extends Store with TableInfo<$StoreTable, StoreData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StoreTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + additionalChecks: + GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), + type: DriftSqlType.string, + requiredDuringInsert: true); + static const VerificationMeta _ownerMeta = const VerificationMeta('owner'); + @override + late final GeneratedColumn owner = GeneratedColumn( + 'owner', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES owner (id)')); + @override + List get $columns => [id, name, owner]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = 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('owner')) { + context.handle( + _ownerMeta, owner.isAcceptableOrUnknown(data['owner']!, _ownerMeta)); + } else if (isInserting) { + context.missing(_ownerMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + StoreData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoreData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + owner: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}owner'])!, + ); + } + + @override + $StoreTable createAlias(String alias) { + return $StoreTable(attachedDatabase, alias); + } +} + +class StoreData extends DataClass implements Insertable { + final int id; + final String name; + final int owner; + const StoreData({required this.id, required this.name, required this.owner}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['owner'] = Variable(owner); + return map; + } + + StoreCompanion toCompanion(bool nullToAbsent) { + return StoreCompanion( + id: Value(id), + name: Value(name), + owner: Value(owner), + ); + } + + factory StoreData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoreData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + owner: serializer.fromJson(json['owner']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'owner': serializer.toJson(owner), + }; + } + + StoreData copyWith({int? id, String? name, int? owner}) => StoreData( + id: id ?? this.id, + name: name ?? this.name, + owner: owner ?? this.owner, + ); + @override + String toString() { + return (StringBuffer('StoreData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('owner: $owner') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, owner); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoreData && + other.id == this.id && + other.name == this.name && + other.owner == this.owner); +} + +class StoreCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value owner; + const StoreCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.owner = const Value.absent(), + }); + StoreCompanion.insert({ + this.id = const Value.absent(), + required String name, + required int owner, + }) : name = Value(name), + owner = Value(owner); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? owner, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (owner != null) 'owner': owner, + }); + } + + StoreCompanion copyWith( + {Value? id, Value? name, Value? owner}) { + return StoreCompanion( + id: id ?? this.id, + name: name ?? this.name, + owner: owner ?? this.owner, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (owner.present) { + map['owner'] = Variable(owner.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('owner: $owner') + ..write(')')) + .toString(); + } +} + +class $ListingsTable extends Listings with TableInfo<$ListingsTable, Listing> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $ListingsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _productMeta = + const VerificationMeta('product'); + @override + late final GeneratedColumn product = GeneratedColumn( + 'product', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES products (id)')); + static const VerificationMeta _storeMeta = const VerificationMeta('store'); + @override + late final GeneratedColumn store = GeneratedColumn( + 'store', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES store (id)')); + static const VerificationMeta _priceMeta = const VerificationMeta('price'); + @override + late final GeneratedColumn price = GeneratedColumn( + 'price', aliasedName, false, + type: DriftSqlType.double, requiredDuringInsert: true); + @override + List get $columns => [id, product, store, price]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'listings'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('product')) { + context.handle(_productMeta, + product.isAcceptableOrUnknown(data['product']!, _productMeta)); + } else if (isInserting) { + context.missing(_productMeta); + } + if (data.containsKey('store')) { + context.handle( + _storeMeta, store.isAcceptableOrUnknown(data['store']!, _storeMeta)); + } else if (isInserting) { + context.missing(_storeMeta); + } + if (data.containsKey('price')) { + context.handle( + _priceMeta, price.isAcceptableOrUnknown(data['price']!, _priceMeta)); + } else if (isInserting) { + context.missing(_priceMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + Listing map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return Listing( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + product: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}product'])!, + store: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}store'])!, + price: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}price'])!, + ); + } + + @override + $ListingsTable createAlias(String alias) { + return $ListingsTable(attachedDatabase, alias); + } +} + +class Listing extends DataClass implements Insertable { + final int id; + final int product; + final int store; + final double price; + const Listing( + {required this.id, + required this.product, + required this.store, + required this.price}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['product'] = Variable(product); + map['store'] = Variable(store); + map['price'] = Variable(price); + return map; + } + + ListingsCompanion toCompanion(bool nullToAbsent) { + return ListingsCompanion( + id: Value(id), + product: Value(product), + store: Value(store), + price: Value(price), + ); + } + + factory Listing.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return Listing( + id: serializer.fromJson(json['id']), + product: serializer.fromJson(json['product']), + store: serializer.fromJson(json['store']), + price: serializer.fromJson(json['price']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'product': serializer.toJson(product), + 'store': serializer.toJson(store), + 'price': serializer.toJson(price), + }; + } + + Listing copyWith({int? id, int? product, int? store, double? price}) => + Listing( + id: id ?? this.id, + product: product ?? this.product, + store: store ?? this.store, + price: price ?? this.price, + ); + @override + String toString() { + return (StringBuffer('Listing(') + ..write('id: $id, ') + ..write('product: $product, ') + ..write('store: $store, ') + ..write('price: $price') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, product, store, price); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is Listing && + other.id == this.id && + other.product == this.product && + other.store == this.store && + other.price == this.price); +} + +class ListingsCompanion extends UpdateCompanion { + final Value id; + final Value product; + final Value store; + final Value price; + const ListingsCompanion({ + this.id = const Value.absent(), + this.product = const Value.absent(), + this.store = const Value.absent(), + this.price = const Value.absent(), + }); + ListingsCompanion.insert({ + this.id = const Value.absent(), + required int product, + required int store, + required double price, + }) : product = Value(product), + store = Value(store), + price = Value(price); + static Insertable custom({ + Expression? id, + Expression? product, + Expression? store, + Expression? price, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (product != null) 'product': product, + if (store != null) 'store': store, + if (price != null) 'price': price, + }); + } + + ListingsCompanion copyWith( + {Value? id, + Value? product, + Value? store, + Value? price}) { + return ListingsCompanion( + id: id ?? this.id, + product: product ?? this.product, + store: store ?? this.store, + price: price ?? this.price, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (product.present) { + map['product'] = Variable(product.value); + } + if (store.present) { + map['store'] = Variable(store.value); + } + if (price.present) { + map['price'] = Variable(price.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ListingsCompanion(') + ..write('id: $id, ') + ..write('product: $product, ') + ..write('store: $store, ') + ..write('price: $price') + ..write(')')) + .toString(); + } +} + +abstract class _$AppDatabase extends GeneratedDatabase { + _$AppDatabase(QueryExecutor e) : super(e); + _$AppDatabaseManager get managers => _$AppDatabaseManager(this); + late final $ProductsTable products = $ProductsTable(this); + late final $OwnerTable owner = $OwnerTable(this); + late final $StoreTable store = $StoreTable(this); + late final $ListingsTable listings = $ListingsTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => + [products, owner, store, listings]; +} + +class $$ProductsTableFilterComposer + extends FilterComposer<_$AppDatabase, $ProductsTable> { + $$ProductsTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get name => ColumnFilters($table.name); + ColumnFilters get description => ColumnFilters($table.description); + ColumnFilters get releaseDate => ColumnFilters($table.releaseDate); + ColumnFilters get colorValue => ColumnFilters($table.color); + ColumnWithTypeConverterFilters get color => + ColumnWithTypeConverterFilters($table.color); + ComposableFilter listings( + ComposableFilter Function($$ListingsTableFilterComposer f) f) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.listings, + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.product, + getReferencedComposer: (db, table) => + $$ListingsTableFilterComposer(db, table), + builder: f); + } +} + +class $$ProductsTableOrderingComposer + extends OrderingComposer<_$AppDatabase, $ProductsTable> { + $$ProductsTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); + ColumnOrderings get description => ColumnOrderings($table.description); + ColumnOrderings get releaseDate => ColumnOrderings($table.releaseDate); + ColumnOrderings get color => ColumnOrderings($table.color); +} + +class $$ProductsTableProcessedTableManager extends ProcessedTableManager< + _$AppDatabase, + $ProductsTable, + Product, + $$ProductsTableFilterComposer, + $$ProductsTableOrderingComposer, + $$ProductsTableProcessedTableManager, + $$ProductsTableInsertCompanionBuilder, + $$ProductsTableUpdateCompanionBuilder> { + const $$ProductsTableProcessedTableManager(super.$state); +} + +typedef $$ProductsTableInsertCompanionBuilder = ProductsCompanion Function({ + Value id, + required String name, + required String description, + required DateTime releaseDate, + required Color color, +}); +typedef $$ProductsTableUpdateCompanionBuilder = ProductsCompanion Function({ + Value id, + Value name, + Value description, + Value releaseDate, + Value color, +}); + +class $$ProductsTableTableManager extends RootTableManager< + _$AppDatabase, + $ProductsTable, + Product, + $$ProductsTableFilterComposer, + $$ProductsTableOrderingComposer, + $$ProductsTableProcessedTableManager, + $$ProductsTableInsertCompanionBuilder, + $$ProductsTableUpdateCompanionBuilder> { + $$ProductsTableTableManager(_$AppDatabase db, $ProductsTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$ProductsTableFilterComposer(db, table), + orderingComposer: $$ProductsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ProductsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value description = const Value.absent(), + Value releaseDate = const Value.absent(), + Value color = const Value.absent(), + }) => + ProductsCompanion( + id: id, + name: name, + description: description, + releaseDate: releaseDate, + color: color, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String name, + required String description, + required DateTime releaseDate, + required Color color, + }) => + ProductsCompanion.insert( + id: id, + name: name, + description: description, + releaseDate: releaseDate, + color: color, + ))); +} + +class $$OwnerTableFilterComposer + extends FilterComposer<_$AppDatabase, $OwnerTable> { + $$OwnerTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get name => ColumnFilters($table.name); + ComposableFilter stores( + ComposableFilter Function($$StoreTableFilterComposer f) f) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.store, + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.owner, + getReferencedComposer: (db, table) => + $$StoreTableFilterComposer(db, table), + builder: f); + } +} + +class $$OwnerTableOrderingComposer + extends OrderingComposer<_$AppDatabase, $OwnerTable> { + $$OwnerTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); +} + +class $$OwnerTableProcessedTableManager extends ProcessedTableManager< + _$AppDatabase, + $OwnerTable, + OwnerData, + $$OwnerTableFilterComposer, + $$OwnerTableOrderingComposer, + $$OwnerTableProcessedTableManager, + $$OwnerTableInsertCompanionBuilder, + $$OwnerTableUpdateCompanionBuilder> { + const $$OwnerTableProcessedTableManager(super.$state); +} + +typedef $$OwnerTableInsertCompanionBuilder = OwnerCompanion Function({ + Value id, + required String name, +}); +typedef $$OwnerTableUpdateCompanionBuilder = OwnerCompanion Function({ + Value id, + Value name, +}); + +class $$OwnerTableTableManager extends RootTableManager< + _$AppDatabase, + $OwnerTable, + OwnerData, + $$OwnerTableFilterComposer, + $$OwnerTableOrderingComposer, + $$OwnerTableProcessedTableManager, + $$OwnerTableInsertCompanionBuilder, + $$OwnerTableUpdateCompanionBuilder> { + $$OwnerTableTableManager(_$AppDatabase db, $OwnerTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$OwnerTableFilterComposer(db, table), + orderingComposer: $$OwnerTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$OwnerTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + }) => + OwnerCompanion( + id: id, + name: name, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String name, + }) => + OwnerCompanion.insert( + id: id, + name: name, + ))); +} + +class $$StoreTableFilterComposer + extends FilterComposer<_$AppDatabase, $StoreTable> { + $$StoreTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get name => ColumnFilters($table.name); + ColumnFilters get ownerId => ColumnFilters($table.owner); + ComposableFilter owner( + ComposableFilter Function($$OwnerTableFilterComposer f) f) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.owner, + getCurrentColumn: (f) => f.owner, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$OwnerTableFilterComposer(db, table), + builder: f); + } + + ComposableFilter listings( + ComposableFilter Function($$ListingsTableFilterComposer f) f) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.listings, + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.store, + getReferencedComposer: (db, table) => + $$ListingsTableFilterComposer(db, table), + builder: f); + } +} + +class $$StoreTableOrderingComposer + extends OrderingComposer<_$AppDatabase, $StoreTable> { + $$StoreTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); + ColumnOrderings get ownerId => ColumnOrderings($table.owner); + ComposableOrdering owner( + ComposableOrdering Function($$OwnerTableOrderingComposer o) o) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.owner, + getCurrentColumn: (f) => f.owner, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$OwnerTableOrderingComposer(db, table), + builder: o); + } +} + +class $$StoreTableProcessedTableManager extends ProcessedTableManager< + _$AppDatabase, + $StoreTable, + StoreData, + $$StoreTableFilterComposer, + $$StoreTableOrderingComposer, + $$StoreTableProcessedTableManager, + $$StoreTableInsertCompanionBuilder, + $$StoreTableUpdateCompanionBuilder> { + const $$StoreTableProcessedTableManager(super.$state); +} + +typedef $$StoreTableInsertCompanionBuilder = StoreCompanion Function({ + Value id, + required String name, + required int owner, +}); +typedef $$StoreTableUpdateCompanionBuilder = StoreCompanion Function({ + Value id, + Value name, + Value owner, +}); + +class $$StoreTableTableManager extends RootTableManager< + _$AppDatabase, + $StoreTable, + StoreData, + $$StoreTableFilterComposer, + $$StoreTableOrderingComposer, + $$StoreTableProcessedTableManager, + $$StoreTableInsertCompanionBuilder, + $$StoreTableUpdateCompanionBuilder> { + $$StoreTableTableManager(_$AppDatabase db, $StoreTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$StoreTableFilterComposer(db, table), + orderingComposer: $$StoreTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$StoreTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value owner = const Value.absent(), + }) => + StoreCompanion( + id: id, + name: name, + owner: owner, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String name, + required int owner, + }) => + StoreCompanion.insert( + id: id, + name: name, + owner: owner, + ))); +} + +class $$ListingsTableFilterComposer + extends FilterComposer<_$AppDatabase, $ListingsTable> { + $$ListingsTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get productId => ColumnFilters($table.product); + ComposableFilter product( + ComposableFilter Function($$ProductsTableFilterComposer f) f) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.products, + getCurrentColumn: (f) => f.product, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$ProductsTableFilterComposer(db, table), + builder: f); + } + + ColumnFilters get storeId => ColumnFilters($table.store); + ComposableFilter store( + ComposableFilter Function($$StoreTableFilterComposer f) f) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.store, + getCurrentColumn: (f) => f.store, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$StoreTableFilterComposer(db, table), + builder: f); + } + + ColumnFilters get price => ColumnFilters($table.price); +} + +class $$ListingsTableOrderingComposer + extends OrderingComposer<_$AppDatabase, $ListingsTable> { + $$ListingsTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get productId => ColumnOrderings($table.product); + ComposableOrdering product( + ComposableOrdering Function($$ProductsTableOrderingComposer o) o) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.products, + getCurrentColumn: (f) => f.product, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$ProductsTableOrderingComposer(db, table), + builder: o); + } + + ColumnOrderings get storeId => ColumnOrderings($table.store); + ComposableOrdering store( + ComposableOrdering Function($$StoreTableOrderingComposer o) o) { + return composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.store, + getCurrentColumn: (f) => f.store, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$StoreTableOrderingComposer(db, table), + builder: o); + } + + ColumnOrderings get price => ColumnOrderings($table.price); +} + +class $$ListingsTableProcessedTableManager extends ProcessedTableManager< + _$AppDatabase, + $ListingsTable, + Listing, + $$ListingsTableFilterComposer, + $$ListingsTableOrderingComposer, + $$ListingsTableProcessedTableManager, + $$ListingsTableInsertCompanionBuilder, + $$ListingsTableUpdateCompanionBuilder> { + const $$ListingsTableProcessedTableManager(super.$state); +} + +typedef $$ListingsTableInsertCompanionBuilder = ListingsCompanion Function({ + Value id, + required int product, + required int store, + required double price, +}); +typedef $$ListingsTableUpdateCompanionBuilder = ListingsCompanion Function({ + Value id, + Value product, + Value store, + Value price, +}); + +class $$ListingsTableTableManager extends RootTableManager< + _$AppDatabase, + $ListingsTable, + Listing, + $$ListingsTableFilterComposer, + $$ListingsTableOrderingComposer, + $$ListingsTableProcessedTableManager, + $$ListingsTableInsertCompanionBuilder, + $$ListingsTableUpdateCompanionBuilder> { + $$ListingsTableTableManager(_$AppDatabase db, $ListingsTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$ListingsTableFilterComposer(db, table), + orderingComposer: $$ListingsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ListingsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value product = const Value.absent(), + Value store = const Value.absent(), + Value price = const Value.absent(), + }) => + ListingsCompanion( + id: id, + product: product, + store: store, + price: price, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required int product, + required int store, + required double price, + }) => + ListingsCompanion.insert( + id: id, + product: product, + store: store, + price: price, + ))); +} + +class _$AppDatabaseManager { + final _$AppDatabase _db; + _$AppDatabaseManager(this._db); + $$ProductsTableTableManager get products => + $$ProductsTableTableManager(_db, _db.products); + $$OwnerTableTableManager get owner => + $$OwnerTableTableManager(_db, _db.owner); + $$StoreTableTableManager get store => + $$StoreTableTableManager(_db, _db.store); + $$ListingsTableTableManager get listings => + $$ListingsTableTableManager(_db, _db.listings); +} diff --git a/examples/manager/lib/main.dart b/examples/manager/lib/main.dart new file mode 100644 index 00000000..1a93b016 --- /dev/null +++ b/examples/manager/lib/main.dart @@ -0,0 +1,64 @@ +import 'package:flex_color_picker/flex_color_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:manager/database.dart'; +import 'package:manager/pages/listings.dart'; +import 'package:manager/pages/owners.dart'; +import 'package:manager/pages/product.dart'; +import 'package:manager/pages/store.dart'; + +late final AppDatabase db; +void main() { + db = AppDatabase(); + runApp(ProviderScope(child: const MyApp())); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Manager Demo', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const Home(), + ); + } +} + +class Home extends HookConsumerWidget { + const Home({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final tabs = useTabController(initialLength: 4); + + return Scaffold( + appBar: AppBar( + title: const Text('Manager Demo'), + bottom: TabBar( + controller: tabs, + tabs: const [ + Tab(text: 'Products'), + Tab(text: 'Listings'), + Tab(text: 'Stores'), + Tab(text: 'Owners'), + ], + ), + ), + body: TabBarView( + controller: tabs, + children: const [ + ProductPage(), + ListingPage(), + StorePage(), + OwnersPage(), + ], + ), + ); + } +} diff --git a/examples/manager/lib/pages/listings.dart b/examples/manager/lib/pages/listings.dart new file mode 100644 index 00000000..93e41ef7 --- /dev/null +++ b/examples/manager/lib/pages/listings.dart @@ -0,0 +1,152 @@ +import 'package:flex_color_picker/flex_color_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:manager/database.dart'; +import 'package:manager/main.dart'; + +class ListingPage extends HookConsumerWidget { + const ListingPage({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final stream = useMemoized(() => + db.managers.listings.all().watch().asyncMap((event) async { + final products = await db.managers.products + .filter( + (f) => f.listings((f) => f.id.isIn(event.map((e) => e.id)))) + .get(); + final stores = await db.managers.store + .filter( + (f) => f.listings((f) => f.id.isIn(event.map((e) => e.id)))) + .get(); + return event.map((e) { + final product = + products.firstWhere((element) => element.id == e.product); + final store = stores.firstWhere((element) => element.id == e.store); + return (product, store, e); + }).toList(); + })); + final data = useStream(stream); + + final Widget body; + var items = <(Product, StoreData, Listing)>[]; + if (data.hasData) { + items = data.data!; + body = ListView.builder( + itemCount: items.length, + itemBuilder: (context, index) { + final (product, store, listing) = items[index]; + return ListTile( + title: Text("${product.name} - ${store.name}"), + subtitle: Text("${listing.price}"), + trailing: IconButton( + onPressed: () { + db.managers.products.delete(product); + }, + icon: Icon(Icons.delete)), + leading: CircleAvatar( + backgroundColor: product.color, + ), + ); + }, + ); + } else { + body = const Center(child: Text("No data")); + } + + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + showDialog( + context: context, + builder: (ctx) { + return HookBuilder(builder: (context) { + final priceTextController = useTextEditingController(); + final product = useState(null); + final store = useState(null); + + return SimpleDialog( + title: Text("Add Listing"), + children: [ + TextField( + controller: priceTextController, + decoration: InputDecoration(labelText: "Price"), + ), + ListTile( + title: Text("Product"), + subtitle: product.value == null + ? null + : Text(product.value!.name), + onTap: () { + showDialog( + context: context, + builder: (ctx) { + return HookBuilder(builder: (context) { + final data = useStream( + db.managers.products.all().watch()); + final items = data.data ?? []; + return SimpleDialog( + title: Text("Select Product"), + children: [ + ...items.map((e) => ListTile( + title: Text(e.name), + onTap: () { + product.value = e; + Navigator.of(ctx).pop(); + }, + )) + ], + ); + }); + }); + }, + ), + ListTile( + title: Text("Store"), + subtitle: store.value == null + ? null + : Text(store.value!.name), + onTap: () { + showDialog( + context: context, + builder: (ctx) { + return HookBuilder(builder: (context) { + final data = useStream( + db.managers.store.all().watch()); + final items = data.data ?? []; + return SimpleDialog( + title: Text("Select Store"), + children: [ + ...items.map((e) => ListTile( + title: Text(e.name), + onTap: () { + store.value = e; + Navigator.of(ctx).pop(); + }, + )) + ], + ); + }); + }); + }, + ), + ElevatedButton( + onPressed: () { + db.managers.listings.create((o) => o( + price: double.parse(priceTextController.text), + product: product.value!.id, + store: store.value!.id)); + Navigator.of(ctx).pop(); + }, + child: Text("Add")) + ], + ); + }); + }); + }, + child: Icon(Icons.add), + ), + body: body, + ); + } +} diff --git a/examples/manager/lib/pages/owners.dart b/examples/manager/lib/pages/owners.dart new file mode 100644 index 00000000..b313ba66 --- /dev/null +++ b/examples/manager/lib/pages/owners.dart @@ -0,0 +1,72 @@ +import 'package:flex_color_picker/flex_color_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:manager/database.dart'; +import 'package:manager/main.dart'; +import 'package:manager/tables.dart'; + +class OwnersPage extends HookConsumerWidget { + const OwnersPage({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final stream = useMemoized(() => db.managers.owner.all().watch()); + final data = useStream(stream); + + final Widget body; + var items = []; + if (data.hasData) { + items = data.data!; + body = ListView.builder( + itemCount: items.length, + itemBuilder: (context, index) { + final product = items[index]; + return ListTile( + title: Text(product.name), + trailing: IconButton( + onPressed: () { + db.managers.owner.delete(product); + }, + icon: Icon(Icons.delete)), + ); + }, + ); + } else { + body = const Center(child: Text("No data")); + } + + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + showDialog( + context: context, + builder: (ctx) { + return HookBuilder(builder: (context) { + final nameTextController = useTextEditingController(); + + return SimpleDialog( + title: Text("Add Owner"), + children: [ + TextField( + controller: nameTextController, + decoration: InputDecoration(labelText: "Name"), + ), + ElevatedButton( + onPressed: () { + db.managers.owner.create((o) => o( + name: nameTextController.text, + )); + Navigator.of(ctx).pop(); + }, + child: Text("Add")) + ], + ); + }); + }); + }, + child: Icon(Icons.add), + ), + body: body, + ); + } +} diff --git a/examples/manager/lib/pages/product.dart b/examples/manager/lib/pages/product.dart new file mode 100644 index 00000000..185f7c4d --- /dev/null +++ b/examples/manager/lib/pages/product.dart @@ -0,0 +1,112 @@ +import 'package:flex_color_picker/flex_color_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:manager/database.dart'; +import 'package:manager/main.dart'; + +class ProductPage extends HookConsumerWidget { + const ProductPage({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final stream = useMemoized(() => db.managers.products.all().watch()); + final data = useStream(stream); + + final Widget body; + var items = []; + if (data.hasData) { + items = data.data!; + body = ListView.builder( + itemCount: items.length, + itemBuilder: (context, index) { + final product = items[index]; + return ListTile( + title: Text(product.name), + subtitle: Text("${product.description} ${product.releaseDate}"), + trailing: IconButton( + onPressed: () { + db.managers.products.delete(product); + }, + icon: Icon(Icons.delete)), + leading: CircleAvatar( + backgroundColor: product.color, + ), + ); + }, + ); + } else { + body = const Center(child: Text("No data")); + } + + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + showDialog( + context: context, + builder: (ctx) { + return HookBuilder(builder: (context) { + final nameTextController = useTextEditingController(); + final descriptionTextController = useTextEditingController(); + final color = useState(null); + final releaseDate = useState(null); + + return SimpleDialog( + title: Text("Add Product"), + children: [ + TextField( + controller: nameTextController, + decoration: InputDecoration(labelText: "Name"), + ), + TextField( + controller: descriptionTextController, + decoration: InputDecoration(labelText: "Description"), + ), + ListTile( + leading: CircleAvatar( + backgroundColor: color.value, + ), + title: Text("Color"), + onTap: () { + showColorPickerDialog( + context, color.value ?? Colors.blue) + .then((value) => color.value = value); + }, + ), + ListTile( + leading: Icon(Icons.calendar_month), + title: Text("Date"), + subtitle: releaseDate.value != null + ? Text(releaseDate.value.toString()) + : null, + onTap: () { + showDatePicker( + context: ctx, + initialDate: releaseDate.value ?? DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime(2100), + ).then((value) { + releaseDate.value = value; + }); + }, + ), + ElevatedButton( + onPressed: () { + db.managers.products.create((o) => o( + color: color.value ?? Colors.red, + description: descriptionTextController.text, + name: nameTextController.text, + releaseDate: releaseDate.value!)); + Navigator.of(ctx).pop(); + }, + child: Text("Add")) + ], + ); + }); + }); + }, + child: Icon(Icons.add), + ), + body: body, + ); + } +} diff --git a/examples/manager/lib/pages/store.dart b/examples/manager/lib/pages/store.dart new file mode 100644 index 00000000..07500ec4 --- /dev/null +++ b/examples/manager/lib/pages/store.dart @@ -0,0 +1,112 @@ +import 'package:flex_color_picker/flex_color_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:manager/database.dart'; +import 'package:manager/main.dart'; + +class StorePage extends HookConsumerWidget { + const StorePage({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final stream = useMemoized(() => + db.managers.store.all().watch().asyncMap((event) async { + final owners = await db.managers.owner + .filter((f) => f.stores((f) => f.id.isIn(event.map((e) => e.id)))) + .get(); + return event.map((e) { + final owner = owners.firstWhere((element) => element.id == e.owner); + return (owner, e); + }).toList(); + })); + final data = useStream(stream); + + final Widget body; + var items = <(OwnerData, StoreData)>[]; + if (data.hasData) { + items = data.data!; + body = ListView.builder( + itemCount: items.length, + itemBuilder: (context, index) { + final (owner, store) = items[index]; + return ListTile( + title: Text(store.name), + subtitle: Text(owner.name), + trailing: IconButton( + onPressed: () { + db.managers.store.delete(store); + }, + icon: Icon(Icons.delete)), + ); + }, + ); + } else { + body = const Center(child: Text("No data")); + } + + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + showDialog( + context: context, + builder: (ctx) { + return HookBuilder(builder: (context) { + final nameTextController = useTextEditingController(); + final owner = useState(null); + + return SimpleDialog( + title: Text("Add Store"), + children: [ + TextField( + controller: nameTextController, + decoration: InputDecoration(labelText: "Name"), + ), + ListTile( + title: Text("Owner"), + subtitle: Text(owner.value?.name ?? "Select Owner"), + onTap: () { + showDialog( + context: context, + builder: (ctx) { + return HookBuilder(builder: (context) { + final stream = useMemoized( + () => db.managers.owner.all().watch()); + final data = useStream(stream); + + return SimpleDialog( + title: Text("Select Owner"), + children: [ + if (data.hasData) + for (final o in data.data!) + ListTile( + title: Text(o.name), + onTap: () { + owner.value = o; + Navigator.of(ctx).pop(owner); + }, + ) + ], + ); + }); + }, + ); + }), + ElevatedButton( + onPressed: () { + db.managers.store.create((o) => o( + name: nameTextController.text, + owner: owner.value!.id)); + Navigator.of(ctx).pop(); + }, + child: Text("Add")) + ], + ); + }); + }); + }, + child: Icon(Icons.add), + ), + body: body, + ); + } +} diff --git a/examples/manager/lib/tables.dart b/examples/manager/lib/tables.dart new file mode 100644 index 00000000..fc104715 --- /dev/null +++ b/examples/manager/lib/tables.dart @@ -0,0 +1,41 @@ +import 'package:drift/drift.dart'; +import 'package:flutter/material.dart' show Color; + +class Products extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get name => text().withLength(min: 1, max: 50)(); + TextColumn get description => text().withLength(min: 1, max: 50)(); + DateTimeColumn get releaseDate => dateTime()(); + IntColumn get color => integer().map(const ColorConverter())(); +} + +class Listings extends Table { + IntColumn get id => integer().autoIncrement()(); + @ReferenceName("listings") + IntColumn get product => integer().references(Products, #id)(); + @ReferenceName("listings") + IntColumn get store => integer().references(Store, #id)(); + RealColumn get price => real()(); +} + +class Store extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get name => text().withLength(min: 1, max: 50)(); + @ReferenceName("stores") + IntColumn get owner => integer().references(Owner, #id)(); +} + +class Owner extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get name => text().withLength(min: 1, max: 50)(); +} + +class ColorConverter extends TypeConverter { + const ColorConverter(); + + @override + Color fromSql(int fromDb) => Color(fromDb); + + @override + int toSql(Color value) => value.value; +} diff --git a/examples/manager/linux/.gitignore b/examples/manager/linux/.gitignore new file mode 100644 index 00000000..d3896c98 --- /dev/null +++ b/examples/manager/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/examples/manager/linux/CMakeLists.txt b/examples/manager/linux/CMakeLists.txt new file mode 100644 index 00000000..87384c78 --- /dev/null +++ b/examples/manager/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "manager") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.manager") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/examples/manager/linux/flutter/CMakeLists.txt b/examples/manager/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/examples/manager/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/examples/manager/linux/flutter/generated_plugin_registrant.cc b/examples/manager/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..2c1ec4fe --- /dev/null +++ b/examples/manager/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); + sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); +} diff --git a/examples/manager/linux/flutter/generated_plugin_registrant.h b/examples/manager/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/examples/manager/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/examples/manager/linux/flutter/generated_plugins.cmake b/examples/manager/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..7ea2a801 --- /dev/null +++ b/examples/manager/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + sqlite3_flutter_libs +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/examples/manager/linux/main.cc b/examples/manager/linux/main.cc new file mode 100644 index 00000000..e7c5c543 --- /dev/null +++ b/examples/manager/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/examples/manager/linux/my_application.cc b/examples/manager/linux/my_application.cc new file mode 100644 index 00000000..058b1555 --- /dev/null +++ b/examples/manager/linux/my_application.cc @@ -0,0 +1,124 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "manager"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "manager"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/examples/manager/linux/my_application.h b/examples/manager/linux/my_application.h new file mode 100644 index 00000000..72271d5e --- /dev/null +++ b/examples/manager/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/examples/manager/macos/.gitignore b/examples/manager/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/examples/manager/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/examples/manager/macos/Flutter/Flutter-Debug.xcconfig b/examples/manager/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..c2efd0b6 --- /dev/null +++ b/examples/manager/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/manager/macos/Flutter/Flutter-Release.xcconfig b/examples/manager/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..c2efd0b6 --- /dev/null +++ b/examples/manager/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift b/examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..14fa8bc9 --- /dev/null +++ b/examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import path_provider_foundation +import sqlite3_flutter_libs + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) +} diff --git a/examples/manager/macos/Runner.xcodeproj/project.pbxproj b/examples/manager/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..80acabf8 --- /dev/null +++ b/examples/manager/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* manager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "manager.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* manager.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* manager.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..c72bbcf3 --- /dev/null +++ b/examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata b/examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/manager/macos/Runner/AppDelegate.swift b/examples/manager/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/examples/manager/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/manager/macos/Runner/Configs/AppInfo.xcconfig b/examples/manager/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..8167a552 --- /dev/null +++ b/examples/manager/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = manager + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.manager + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/examples/manager/macos/Runner/Configs/Debug.xcconfig b/examples/manager/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/examples/manager/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/manager/macos/Runner/Configs/Release.xcconfig b/examples/manager/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/examples/manager/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/manager/macos/Runner/Configs/Warnings.xcconfig b/examples/manager/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/examples/manager/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/examples/manager/macos/Runner/DebugProfile.entitlements b/examples/manager/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/examples/manager/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/examples/manager/macos/Runner/Info.plist b/examples/manager/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/examples/manager/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/examples/manager/macos/Runner/MainFlutterWindow.swift b/examples/manager/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/examples/manager/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/examples/manager/macos/Runner/Release.entitlements b/examples/manager/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/examples/manager/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/examples/manager/macos/RunnerTests/RunnerTests.swift b/examples/manager/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..5418c9f5 --- /dev/null +++ b/examples/manager/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/examples/manager/pubspec.yaml b/examples/manager/pubspec.yaml new file mode 100644 index 00000000..9b309869 --- /dev/null +++ b/examples/manager/pubspec.yaml @@ -0,0 +1,41 @@ +name: manager +description: "A new Flutter project." +publish_to: "none" + +version: 1.0.0+1 + +environment: + sdk: ">=3.3.1 <4.0.0" +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.6 + drift: ^2.16.0 + drift_dev: ^2.16.0 + flutter_riverpod: ^2.5.1 + hooks_riverpod: ^2.5.1 + flutter_hooks: ^0.20.5 + riverpod_annotation: ^2.3.5 + sqlite3_flutter_libs: ^0.5.20 + sqlite3: ^2.4.2 + path: ^1.9.0 + path_provider: ^2.1.2 + flex_color_picker: ^3.4.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 + build_runner: ^2.4.9 + riverpod_generator: ^2.4.0 + custom_lint: ^0.6.4 + riverpod_lint: ^2.3.10 + +flutter: + uses-material-design: true + +dependency_overrides: + drift_dev: + path: C:/Users/dicke/drift/drift_dev + drift: + path: C:/Users/dicke/drift/drift diff --git a/examples/manager/test/widget_test.dart b/examples/manager/test/widget_test.dart new file mode 100644 index 00000000..a9ed2a8d --- /dev/null +++ b/examples/manager/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:manager/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/examples/manager/web/favicon.png b/examples/manager/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/examples/manager/web/icons/Icon-192.png b/examples/manager/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/examples/manager/web/icons/Icon-512.png b/examples/manager/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/examples/manager/web/icons/Icon-maskable-192.png b/examples/manager/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/examples/manager/web/icons/Icon-maskable-512.png b/examples/manager/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/examples/manager/web/index.html b/examples/manager/web/index.html new file mode 100644 index 00000000..39bf2a38 --- /dev/null +++ b/examples/manager/web/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + manager + + + + + + + + + + diff --git a/examples/manager/web/manifest.json b/examples/manager/web/manifest.json new file mode 100644 index 00000000..7cd1d06a --- /dev/null +++ b/examples/manager/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "manager", + "short_name": "manager", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/examples/manager/windows/.gitignore b/examples/manager/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/examples/manager/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/examples/manager/windows/CMakeLists.txt b/examples/manager/windows/CMakeLists.txt new file mode 100644 index 00000000..90b7800c --- /dev/null +++ b/examples/manager/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(manager LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "manager") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/examples/manager/windows/flutter/CMakeLists.txt b/examples/manager/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..903f4899 --- /dev/null +++ b/examples/manager/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/examples/manager/windows/flutter/generated_plugin_registrant.cc b/examples/manager/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..988f3c8f --- /dev/null +++ b/examples/manager/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + Sqlite3FlutterLibsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); +} diff --git a/examples/manager/windows/flutter/generated_plugin_registrant.h b/examples/manager/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/examples/manager/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/examples/manager/windows/flutter/generated_plugins.cmake b/examples/manager/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..8abff957 --- /dev/null +++ b/examples/manager/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + sqlite3_flutter_libs +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/examples/manager/windows/runner/CMakeLists.txt b/examples/manager/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..394917c0 --- /dev/null +++ b/examples/manager/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/examples/manager/windows/runner/Runner.rc b/examples/manager/windows/runner/Runner.rc new file mode 100644 index 00000000..1819ae23 --- /dev/null +++ b/examples/manager/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "manager" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "manager" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "manager.exe" "\0" + VALUE "ProductName", "manager" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/examples/manager/windows/runner/flutter_window.cpp b/examples/manager/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..955ee303 --- /dev/null +++ b/examples/manager/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/examples/manager/windows/runner/flutter_window.h b/examples/manager/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/examples/manager/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/examples/manager/windows/runner/main.cpp b/examples/manager/windows/runner/main.cpp new file mode 100644 index 00000000..609d5eac --- /dev/null +++ b/examples/manager/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"manager", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/examples/manager/windows/runner/resource.h b/examples/manager/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/examples/manager/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/examples/manager/windows/runner/resources/app_icon.ico b/examples/manager/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/examples/manager/windows/runner/runner.exe.manifest b/examples/manager/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/examples/manager/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/examples/manager/windows/runner/utils.cpp b/examples/manager/windows/runner/utils.cpp new file mode 100644 index 00000000..b2b08734 --- /dev/null +++ b/examples/manager/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/examples/manager/windows/runner/utils.h b/examples/manager/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/examples/manager/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/examples/manager/windows/runner/win32_window.cpp b/examples/manager/windows/runner/win32_window.cpp new file mode 100644 index 00000000..60608d0f --- /dev/null +++ b/examples/manager/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/examples/manager/windows/runner/win32_window.h b/examples/manager/windows/runner/win32_window.h new file mode 100644 index 00000000..e901dde6 --- /dev/null +++ b/examples/manager/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ From 4373489724b38827c1b189900bee8b4380f09e2b Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Thu, 4 Apr 2024 08:56:30 -0400 Subject: [PATCH 41/74] Move joiner into composer ceuz Non-modular generation can't do imports --- drift/lib/internal/manager.dart | 53 -------------------- drift/lib/src/runtime/manager/composer.dart | 46 +++++++++++++++++ drift_dev/lib/src/writer/manager_writer.dart | 10 +--- 3 files changed, 48 insertions(+), 61 deletions(-) delete mode 100644 drift/lib/internal/manager.dart diff --git a/drift/lib/internal/manager.dart b/drift/lib/internal/manager.dart deleted file mode 100644 index c3aa31b5..00000000 --- a/drift/lib/internal/manager.dart +++ /dev/null @@ -1,53 +0,0 @@ -/// Internal library used by generated managers. -/// -/// This library is not part of drift's public API and should not be imported -/// manually. -library drift.internal.manager; - -import 'package:drift/drift.dart'; -import 'package:drift/src/runtime/manager/manager.dart'; - -/// Utility for creating a composer which contains the joins needed to -/// execute a query on a table that is referenced by a foreign key. -B composeWithJoins, B extends HasJoinBuilders>({ - required DB $db, - required CT $table, - required GeneratedColumn Function(CT) getCurrentColumn, - required RT referencedTable, - required GeneratedColumn Function(RT) getReferencedColumn, - required B Function(QC) builder, - required QC Function(DB db, RT table) getReferencedComposer, -}) { - // The name of the alias will be created using the following logic: - // "currentTableName__currentColumnName__referencedColumnName__referencedTableName" - // This is to ensure that the alias is unique - final currentColumn = getCurrentColumn($table); - final tempReferencedColumn = getReferencedColumn(referencedTable); - final aliasName = - '${currentColumn.tableName}__${currentColumn.name}__${tempReferencedColumn.tableName}__${tempReferencedColumn.name}'; - final aliasedReferencedTable = - $db.alias(referencedTable as TableInfo, aliasName); - final aliasedReferencedColumn = - getReferencedColumn(aliasedReferencedTable as RT); - - // Create a join builder for the referenced table - final joinBuilder = JoinBuilder( - currentTable: $table, - currentColumn: currentColumn, - referencedTable: aliasedReferencedTable, - referencedColumn: aliasedReferencedColumn, - ); - - // Get the query composer for the referenced table, passing in the aliased - // table and all the join builders - final referencedComposer = getReferencedComposer($db, aliasedReferencedTable); - - // Run the user provided builder with the referencedQueryComposer - // This may return a filter or ordering, but we only enforce that it's - // a HasJoinBuilders - final result = builder(referencedComposer); - result.addJoinBuilders({joinBuilder}); - - return result; -} diff --git a/drift/lib/src/runtime/manager/composer.dart b/drift/lib/src/runtime/manager/composer.dart index 08008281..086f0247 100644 --- a/drift/lib/src/runtime/manager/composer.dart +++ b/drift/lib/src/runtime/manager/composer.dart @@ -22,4 +22,50 @@ sealed class Composer { final CT $table; Composer(this.$db, this.$table); + + /// Utility for creating a composer which contains the joins needed to + /// execute a query on a table that is referenced by a foreign key. + B $composeWithJoins, + B extends HasJoinBuilders>({ + required DB $db, + required CT $table, + required GeneratedColumn Function(CT) getCurrentColumn, + required RT referencedTable, + required GeneratedColumn Function(RT) getReferencedColumn, + required B Function(QC) builder, + required QC Function(DB db, RT table) getReferencedComposer, + }) { + // The name of the alias will be created using the following logic: + // "currentTableName__currentColumnName__referencedColumnName__referencedTableName" + // This is to ensure that the alias is unique + final currentColumn = getCurrentColumn($table); + final tempReferencedColumn = getReferencedColumn(referencedTable); + final aliasName = + '${currentColumn.tableName}__${currentColumn.name}__${tempReferencedColumn.tableName}__${tempReferencedColumn.name}'; + final aliasedReferencedTable = + $db.alias(referencedTable as TableInfo, aliasName); + final aliasedReferencedColumn = + getReferencedColumn(aliasedReferencedTable as RT); + + // Create a join builder for the referenced table + final joinBuilder = JoinBuilder( + currentTable: $table, + currentColumn: currentColumn, + referencedTable: aliasedReferencedTable, + referencedColumn: aliasedReferencedColumn, + ); + + // Get the query composer for the referenced table, passing in the aliased + // table and all the join builders + final referencedComposer = + getReferencedComposer($db, aliasedReferencedTable); + + // Run the user provided builder with the referencedQueryComposer + // This may return a filter or ordering, but we only enforce that it's + // a HasJoinBuilders + final result = builder(referencedComposer); + result.addJoinBuilders({joinBuilder}); + + return result; + } } diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 64bd7c1f..32c83f0a 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -101,10 +101,7 @@ class _ReferencedFilterWriter extends _FilterWriter { ..write(" $filterName(") ..writeDriftRef("ComposableFilter") ..writeln(" Function( $referencedFilterComposer f) f) {") - ..write("return ") - ..writeUriRef( - Uri.parse('package:drift/internal/manager.dart'), 'composeWithJoins') - ..writeln('(') + ..write("return \$composeWithJoins(") ..writeln("\$db: \$db,") ..writeln("\$table: \$table,") ..writeln("referencedTable: $referencedTableField,") @@ -186,10 +183,7 @@ class _ReferencedOrderingWriter extends _OrderingWriter { ..write(" $orderingName(") ..writeDriftRef("ComposableOrdering") ..writeln(" Function( $referencedOrderingComposer o) o) {") - ..write("return ") - ..writeUriRef( - Uri.parse('package:drift/internal/manager.dart'), 'composeWithJoins') - ..writeln('(') + ..write("return \$composeWithJoins(") ..writeln("\$db: \$db,") ..writeln("\$table: \$table,") ..writeln("referencedTable: $referencedTableField,") From d553a7f67975da261617d9e87d464c11ecab0140 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 5 Apr 2024 21:26:51 +0200 Subject: [PATCH 42/74] Use latest language version in test --- drift_dev/test/writer/data_class_writer_test.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drift_dev/test/writer/data_class_writer_test.dart b/drift_dev/test/writer/data_class_writer_test.dart index ef89838b..25ad6e82 100644 --- a/drift_dev/test/writer/data_class_writer_test.dart +++ b/drift_dev/test/writer/data_class_writer_test.dart @@ -409,10 +409,7 @@ class _GeneratesConstDataClasses extends Matcher { final parsed = parseFile( path: '/foo.dart', - featureSet: FeatureSet.fromEnableFlags2( - sdkLanguageVersion: Version(2, 12, 0), - flags: const [], - ), + featureSet: FeatureSet.latestLanguageVersion(), resourceProvider: resourceProvider, throwIfDiagnostics: true, ).unit; From 008dfaf7980cb83cc57d100c51a0630a623ac290 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 5 Apr 2024 21:42:51 +0200 Subject: [PATCH 43/74] Avoid superfluous import --- .../drift_files/custom_queries.drift.dart | 222 ++++++++++++ .../modular/many_to_many/json.drift.dart | 169 +++++++++ .../many_to_many/relational.drift.dart | 328 +++++++++++++++++- drift_dev/lib/src/writer/manager_writer.dart | 25 +- .../test/writer/data_class_writer_test.dart | 1 - examples/manager/lib/database.dart | 1 - examples/manager/lib/database.g.dart | 18 +- examples/manager/lib/main.dart | 3 +- examples/manager/lib/pages/listings.dart | 19 +- examples/manager/lib/pages/owners.dart | 12 +- examples/manager/lib/pages/product.dart | 18 +- examples/manager/lib/pages/store.dart | 15 +- examples/manager/pubspec.yaml | 6 - 13 files changed, 769 insertions(+), 68 deletions(-) diff --git a/docs/lib/snippets/drift_files/custom_queries.drift.dart b/docs/lib/snippets/drift_files/custom_queries.drift.dart index 6bcbe9e7..2bb8229a 100644 --- a/docs/lib/snippets/drift_files/custom_queries.drift.dart +++ b/docs/lib/snippets/drift_files/custom_queries.drift.dart @@ -1,9 +1,11 @@ // ignore_for_file: type=lint import 'package:drift/drift.dart' as i0; import 'package:drift_docs/snippets/_shared/todo_tables.drift.dart' as i1; +import 'package:drift/internal/modular.dart' as i2; abstract class $MyDatabase extends i0.GeneratedDatabase { $MyDatabase(i0.QueryExecutor e) : super(e); + $MyDatabaseManager get managers => $MyDatabaseManager(this); late final i1.$CategoriesTable categories = i1.$CategoriesTable(this); late final i1.$TodoItemsTable todoItems = i1.$TodoItemsTable(this); i0.Selectable categoriesWithCount() { @@ -28,6 +30,226 @@ abstract class $MyDatabase extends i0.GeneratedDatabase { [categories, todoItems]; } +class $$CategoriesTableFilterComposer + extends i0.FilterComposer { + $$CategoriesTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get name => i0.ColumnFilters($table.name); + i0.ComposableFilter todoItemsRefs( + i0.ComposableFilter Function($$TodoItemsTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i2.ReadDatabaseContainer($db) + .resultSet('todo_items'), + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.category, + getReferencedComposer: (db, table) => + $$TodoItemsTableFilterComposer(db, table), + builder: f); + } +} + +class $$CategoriesTableOrderingComposer + extends i0.OrderingComposer { + $$CategoriesTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get name => i0.ColumnOrderings($table.name); +} + +class $$CategoriesTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$CategoriesTable, + i1.Category, + $$CategoriesTableFilterComposer, + $$CategoriesTableOrderingComposer, + $$CategoriesTableProcessedTableManager, + $$CategoriesTableInsertCompanionBuilder, + $$CategoriesTableUpdateCompanionBuilder> { + const $$CategoriesTableProcessedTableManager(super.$state); +} + +typedef $$CategoriesTableInsertCompanionBuilder = i1.CategoriesCompanion + Function({ + i0.Value id, + required String name, +}); +typedef $$CategoriesTableUpdateCompanionBuilder = i1.CategoriesCompanion + Function({ + i0.Value id, + i0.Value name, +}); + +class $$CategoriesTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$CategoriesTable, + i1.Category, + $$CategoriesTableFilterComposer, + $$CategoriesTableOrderingComposer, + $$CategoriesTableProcessedTableManager, + $$CategoriesTableInsertCompanionBuilder, + $$CategoriesTableUpdateCompanionBuilder> { + $$CategoriesTableTableManager( + i0.GeneratedDatabase db, i1.$CategoriesTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$CategoriesTableFilterComposer(db, table), + orderingComposer: $$CategoriesTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$CategoriesTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + }) => + i1.CategoriesCompanion( + id: id, + name: name, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String name, + }) => + i1.CategoriesCompanion.insert( + id: id, + name: name, + ))); +} + +class $$TodoItemsTableFilterComposer + extends i0.FilterComposer { + $$TodoItemsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get title => i0.ColumnFilters($table.title); + i0.ColumnFilters get content => i0.ColumnFilters($table.content); + i0.ColumnFilters get categoryId => i0.ColumnFilters($table.category); + i0.ComposableFilter category( + i0.ComposableFilter Function($$CategoriesTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i2.ReadDatabaseContainer($db) + .resultSet('categories'), + getCurrentColumn: (f) => f.category, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$CategoriesTableFilterComposer(db, table), + builder: f); + } + + i0.ColumnFilters get dueDate => i0.ColumnFilters($table.dueDate); +} + +class $$TodoItemsTableOrderingComposer + extends i0.OrderingComposer { + $$TodoItemsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get title => i0.ColumnOrderings($table.title); + i0.ColumnOrderings get content => i0.ColumnOrderings($table.content); + i0.ColumnOrderings get categoryId => i0.ColumnOrderings($table.category); + i0.ComposableOrdering category( + i0.ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i2.ReadDatabaseContainer($db) + .resultSet('categories'), + getCurrentColumn: (f) => f.category, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$CategoriesTableOrderingComposer(db, table), + builder: o); + } + + i0.ColumnOrderings get dueDate => i0.ColumnOrderings($table.dueDate); +} + +class $$TodoItemsTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$TodoItemsTable, + i1.TodoItem, + $$TodoItemsTableFilterComposer, + $$TodoItemsTableOrderingComposer, + $$TodoItemsTableProcessedTableManager, + $$TodoItemsTableInsertCompanionBuilder, + $$TodoItemsTableUpdateCompanionBuilder> { + const $$TodoItemsTableProcessedTableManager(super.$state); +} + +typedef $$TodoItemsTableInsertCompanionBuilder = i1.TodoItemsCompanion + Function({ + i0.Value id, + required String title, + required String content, + i0.Value category, + i0.Value dueDate, +}); +typedef $$TodoItemsTableUpdateCompanionBuilder = i1.TodoItemsCompanion + Function({ + i0.Value id, + i0.Value title, + i0.Value content, + i0.Value category, + i0.Value dueDate, +}); + +class $$TodoItemsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$TodoItemsTable, + i1.TodoItem, + $$TodoItemsTableFilterComposer, + $$TodoItemsTableOrderingComposer, + $$TodoItemsTableProcessedTableManager, + $$TodoItemsTableInsertCompanionBuilder, + $$TodoItemsTableUpdateCompanionBuilder> { + $$TodoItemsTableTableManager( + i0.GeneratedDatabase db, i1.$TodoItemsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$TodoItemsTableFilterComposer(db, table), + orderingComposer: $$TodoItemsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$TodoItemsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value title = const i0.Value.absent(), + i0.Value content = const i0.Value.absent(), + i0.Value category = const i0.Value.absent(), + i0.Value dueDate = const i0.Value.absent(), + }) => + i1.TodoItemsCompanion( + id: id, + title: title, + content: content, + category: category, + dueDate: dueDate, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String title, + required String content, + i0.Value category = const i0.Value.absent(), + i0.Value dueDate = const i0.Value.absent(), + }) => + i1.TodoItemsCompanion.insert( + id: id, + title: title, + content: content, + category: category, + dueDate: dueDate, + ))); +} + +class $MyDatabaseManager { + final $MyDatabase _db; + $MyDatabaseManager(this._db); + $$CategoriesTableTableManager get categories => + $$CategoriesTableTableManager(_db, _db.categories); + $$TodoItemsTableTableManager get todoItems => + $$TodoItemsTableTableManager(_db, _db.todoItems); +} + class CategoriesWithCountResult { final int id; final String name; diff --git a/docs/lib/snippets/modular/many_to_many/json.drift.dart b/docs/lib/snippets/modular/many_to_many/json.drift.dart index a7bdd538..5ffc5cff 100644 --- a/docs/lib/snippets/modular/many_to_many/json.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/json.drift.dart @@ -7,6 +7,7 @@ import 'package:drift_docs/snippets/modular/many_to_many/json.dart' as i3; abstract class $JsonBasedDatabase extends i0.GeneratedDatabase { $JsonBasedDatabase(i0.QueryExecutor e) : super(e); + $JsonBasedDatabaseManager get managers => $JsonBasedDatabaseManager(this); late final i1.$BuyableItemsTable buyableItems = i1.$BuyableItemsTable(this); late final i2.$ShoppingCartsTable shoppingCarts = i2.$ShoppingCartsTable(this); @@ -18,6 +19,174 @@ abstract class $JsonBasedDatabase extends i0.GeneratedDatabase { [buyableItems, shoppingCarts]; } +class $$BuyableItemsTableFilterComposer + extends i0.FilterComposer { + $$BuyableItemsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get description => + i0.ColumnFilters($table.description); + i0.ColumnFilters get price => i0.ColumnFilters($table.price); +} + +class $$BuyableItemsTableOrderingComposer + extends i0.OrderingComposer { + $$BuyableItemsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); + i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); +} + +class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + const $$BuyableItemsTableProcessedTableManager(super.$state); +} + +typedef $$BuyableItemsTableInsertCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + required String description, + required int price, +}); +typedef $$BuyableItemsTableUpdateCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + i0.Value description, + i0.Value price, +}); + +class $$BuyableItemsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + $$BuyableItemsTableTableManager( + i0.GeneratedDatabase db, i1.$BuyableItemsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$BuyableItemsTableFilterComposer(db, table), + orderingComposer: $$BuyableItemsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$BuyableItemsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value price = const i0.Value.absent(), + }) => + i1.BuyableItemsCompanion( + id: id, + description: description, + price: price, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String description, + required int price, + }) => + i1.BuyableItemsCompanion.insert( + id: id, + description: description, + price: price, + ))); +} + +class $$ShoppingCartsTableFilterComposer + extends i0.FilterComposer { + $$ShoppingCartsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get entriesValue => i0.ColumnFilters($table.entries); + i0.ColumnWithTypeConverterFilters + get entries => i0.ColumnWithTypeConverterFilters($table.entries); +} + +class $$ShoppingCartsTableOrderingComposer + extends i0.OrderingComposer { + $$ShoppingCartsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get entries => i0.ColumnOrderings($table.entries); +} + +class $$ShoppingCartsTableProcessedTableManager + extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + const $$ShoppingCartsTableProcessedTableManager(super.$state); +} + +typedef $$ShoppingCartsTableInsertCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, + required i3.ShoppingCartEntries entries, +}); +typedef $$ShoppingCartsTableUpdateCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, + i0.Value entries, +}); + +class $$ShoppingCartsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + $$ShoppingCartsTableTableManager( + i0.GeneratedDatabase db, i2.$ShoppingCartsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$ShoppingCartsTableFilterComposer(db, table), + orderingComposer: $$ShoppingCartsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ShoppingCartsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value entries = + const i0.Value.absent(), + }) => + i2.ShoppingCartsCompanion( + id: id, + entries: entries, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required i3.ShoppingCartEntries entries, + }) => + i2.ShoppingCartsCompanion.insert( + id: id, + entries: entries, + ))); +} + +class $JsonBasedDatabaseManager { + final $JsonBasedDatabase _db; + $JsonBasedDatabaseManager(this._db); + $$BuyableItemsTableTableManager get buyableItems => + $$BuyableItemsTableTableManager(_db, _db.buyableItems); + $$ShoppingCartsTableTableManager get shoppingCarts => + $$ShoppingCartsTableTableManager(_db, _db.shoppingCarts); +} + class $ShoppingCartsTable extends i3.ShoppingCarts with i0.TableInfo<$ShoppingCartsTable, i2.ShoppingCart> { @override diff --git a/docs/lib/snippets/modular/many_to_many/relational.drift.dart b/docs/lib/snippets/modular/many_to_many/relational.drift.dart index 1d1bab75..7be0062c 100644 --- a/docs/lib/snippets/modular/many_to_many/relational.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/relational.drift.dart @@ -4,10 +4,12 @@ import 'package:drift_docs/snippets/modular/many_to_many/shared.drift.dart' as i1; import 'package:drift_docs/snippets/modular/many_to_many/relational.drift.dart' as i2; -import 'package:drift_docs/snippets/modular/many_to_many/relational.dart' as i3; +import 'package:drift/internal/modular.dart' as i3; +import 'package:drift_docs/snippets/modular/many_to_many/relational.dart' as i4; abstract class $RelationalDatabase extends i0.GeneratedDatabase { $RelationalDatabase(i0.QueryExecutor e) : super(e); + $RelationalDatabaseManager get managers => $RelationalDatabaseManager(this); late final i1.$BuyableItemsTable buyableItems = i1.$BuyableItemsTable(this); late final i2.$ShoppingCartsTable shoppingCarts = i2.$ShoppingCartsTable(this); @@ -21,7 +23,327 @@ abstract class $RelationalDatabase extends i0.GeneratedDatabase { [buyableItems, shoppingCarts, shoppingCartEntries]; } -class $ShoppingCartsTable extends i3.ShoppingCarts +class $$BuyableItemsTableFilterComposer + extends i0.FilterComposer { + $$BuyableItemsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get description => + i0.ColumnFilters($table.description); + i0.ColumnFilters get price => i0.ColumnFilters($table.price); + i0.ComposableFilter shoppingCartEntriesRefs( + i0.ComposableFilter Function($$ShoppingCartEntriesTableFilterComposer f) + f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_cart_entries'), + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.item, + getReferencedComposer: (db, table) => + $$ShoppingCartEntriesTableFilterComposer(db, table), + builder: f); + } +} + +class $$BuyableItemsTableOrderingComposer + extends i0.OrderingComposer { + $$BuyableItemsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); + i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); +} + +class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + const $$BuyableItemsTableProcessedTableManager(super.$state); +} + +typedef $$BuyableItemsTableInsertCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + required String description, + required int price, +}); +typedef $$BuyableItemsTableUpdateCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + i0.Value description, + i0.Value price, +}); + +class $$BuyableItemsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + $$BuyableItemsTableTableManager( + i0.GeneratedDatabase db, i1.$BuyableItemsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$BuyableItemsTableFilterComposer(db, table), + orderingComposer: $$BuyableItemsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$BuyableItemsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value price = const i0.Value.absent(), + }) => + i1.BuyableItemsCompanion( + id: id, + description: description, + price: price, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String description, + required int price, + }) => + i1.BuyableItemsCompanion.insert( + id: id, + description: description, + price: price, + ))); +} + +class $$ShoppingCartsTableFilterComposer + extends i0.FilterComposer { + $$ShoppingCartsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ComposableFilter shoppingCartEntriesRefs( + i0.ComposableFilter Function($$ShoppingCartEntriesTableFilterComposer f) + f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_cart_entries'), + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.shoppingCart, + getReferencedComposer: (db, table) => + $$ShoppingCartEntriesTableFilterComposer(db, table), + builder: f); + } +} + +class $$ShoppingCartsTableOrderingComposer + extends i0.OrderingComposer { + $$ShoppingCartsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); +} + +class $$ShoppingCartsTableProcessedTableManager + extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + const $$ShoppingCartsTableProcessedTableManager(super.$state); +} + +typedef $$ShoppingCartsTableInsertCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, +}); +typedef $$ShoppingCartsTableUpdateCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, +}); + +class $$ShoppingCartsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + $$ShoppingCartsTableTableManager( + i0.GeneratedDatabase db, i2.$ShoppingCartsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$ShoppingCartsTableFilterComposer(db, table), + orderingComposer: $$ShoppingCartsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ShoppingCartsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + }) => + i2.ShoppingCartsCompanion( + id: id, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + }) => + i2.ShoppingCartsCompanion.insert( + id: id, + ))); +} + +class $$ShoppingCartEntriesTableFilterComposer extends i0 + .FilterComposer { + $$ShoppingCartEntriesTableFilterComposer(super.db, super.table); + i0.ColumnFilters get shoppingCartId => + i0.ColumnFilters($table.shoppingCart); + i0.ComposableFilter shoppingCart( + i0.ComposableFilter Function($$ShoppingCartsTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_carts'), + getCurrentColumn: (f) => f.shoppingCart, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$ShoppingCartsTableFilterComposer(db, table), + builder: f); + } + + i0.ColumnFilters get itemId => i0.ColumnFilters($table.item); + i0.ComposableFilter item( + i0.ComposableFilter Function($$BuyableItemsTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('buyable_items'), + getCurrentColumn: (f) => f.item, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$BuyableItemsTableFilterComposer(db, table), + builder: f); + } +} + +class $$ShoppingCartEntriesTableOrderingComposer extends i0 + .OrderingComposer { + $$ShoppingCartEntriesTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get shoppingCartId => + i0.ColumnOrderings($table.shoppingCart); + i0.ComposableOrdering shoppingCart( + i0.ComposableOrdering Function($$ShoppingCartsTableOrderingComposer o) + o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_carts'), + getCurrentColumn: (f) => f.shoppingCart, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$ShoppingCartsTableOrderingComposer(db, table), + builder: o); + } + + i0.ColumnOrderings get itemId => i0.ColumnOrderings($table.item); + i0.ComposableOrdering item( + i0.ComposableOrdering Function($$BuyableItemsTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('buyable_items'), + getCurrentColumn: (f) => f.item, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$BuyableItemsTableOrderingComposer(db, table), + builder: o); + } +} + +class $$ShoppingCartEntriesTableProcessedTableManager + extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartEntriesTable, + i2.ShoppingCartEntry, + $$ShoppingCartEntriesTableFilterComposer, + $$ShoppingCartEntriesTableOrderingComposer, + $$ShoppingCartEntriesTableProcessedTableManager, + $$ShoppingCartEntriesTableInsertCompanionBuilder, + $$ShoppingCartEntriesTableUpdateCompanionBuilder> { + const $$ShoppingCartEntriesTableProcessedTableManager(super.$state); +} + +typedef $$ShoppingCartEntriesTableInsertCompanionBuilder + = i2.ShoppingCartEntriesCompanion Function({ + required int shoppingCart, + required int item, +}); +typedef $$ShoppingCartEntriesTableUpdateCompanionBuilder + = i2.ShoppingCartEntriesCompanion Function({ + i0.Value shoppingCart, + i0.Value item, +}); + +class $$ShoppingCartEntriesTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartEntriesTable, + i2.ShoppingCartEntry, + $$ShoppingCartEntriesTableFilterComposer, + $$ShoppingCartEntriesTableOrderingComposer, + $$ShoppingCartEntriesTableProcessedTableManager, + $$ShoppingCartEntriesTableInsertCompanionBuilder, + $$ShoppingCartEntriesTableUpdateCompanionBuilder> { + $$ShoppingCartEntriesTableTableManager( + i0.GeneratedDatabase db, i2.$ShoppingCartEntriesTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: + $$ShoppingCartEntriesTableFilterComposer(db, table), + orderingComposer: + $$ShoppingCartEntriesTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ShoppingCartEntriesTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value shoppingCart = const i0.Value.absent(), + i0.Value item = const i0.Value.absent(), + }) => + i2.ShoppingCartEntriesCompanion( + shoppingCart: shoppingCart, + item: item, + ), + getInsertCompanionBuilder: ({ + required int shoppingCart, + required int item, + }) => + i2.ShoppingCartEntriesCompanion.insert( + shoppingCart: shoppingCart, + item: item, + ))); +} + +class $RelationalDatabaseManager { + final $RelationalDatabase _db; + $RelationalDatabaseManager(this._db); + $$BuyableItemsTableTableManager get buyableItems => + $$BuyableItemsTableTableManager(_db, _db.buyableItems); + $$ShoppingCartsTableTableManager get shoppingCarts => + $$ShoppingCartsTableTableManager(_db, _db.shoppingCarts); + $$ShoppingCartEntriesTableTableManager get shoppingCartEntries => + $$ShoppingCartEntriesTableTableManager(_db, _db.shoppingCartEntries); +} + +class $ShoppingCartsTable extends i4.ShoppingCarts with i0.TableInfo<$ShoppingCartsTable, i2.ShoppingCart> { @override final i0.GeneratedDatabase attachedDatabase; @@ -163,7 +485,7 @@ class ShoppingCartsCompanion extends i0.UpdateCompanion { } } -class $ShoppingCartEntriesTable extends i3.ShoppingCartEntries +class $ShoppingCartEntriesTable extends i4.ShoppingCartEntries with i0.TableInfo<$ShoppingCartEntriesTable, i2.ShoppingCartEntry> { @override final i0.GeneratedDatabase attachedDatabase; diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 32c83f0a..fb689fb4 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -411,6 +411,17 @@ class _TableWriter { _writeRootTable(leaf); } + String _referenceTable(DriftTable table) { + if (scope.generationOptions.isModular) { + final extension = scope.refUri( + ModularAccessorWriter.modularSupport, 'ReadDatabaseContainer'); + final type = scope.dartCode(scope.entityInfoType(table)); + return "$extension(\$db).resultSet<$type>('${table.schemaName}')"; + } else { + return '\$db.${table.dbGetterName}'; + } + } + /// Add filters and orderings for the columns of this table void addFiltersAndOrderings(List tables) { // Utility function to get the referenced table and column @@ -482,9 +493,7 @@ class _TableWriter { final referencedTableNames = _TableWriter(referencedTable, scope, dbScope, databaseGenericName); final referencedColumnNames = _ColumnWriter(referencedCol.nameInDart); - final String referencedTableField = scope.generationOptions.isModular - ? "\$db.resultSet<${referencedTableNames.tableClassName}>('${referencedTable.schemaName}')" - : "\$db.${referencedTable.dbGetterName}"; + final referencedTableField = _referenceTable(referencedTable); c.filters.add(_ReferencedFilterWriter(c.fieldGetter, fieldGetter: c.fieldGetter, @@ -510,9 +519,7 @@ class _TableWriter { final referencedTableNames = _TableWriter(ot, scope, dbScope, databaseGenericName); final referencedColumnNames = _ColumnWriter(oc.nameInDart); - final String referencedTableField = scope.generationOptions.isModular - ? "\$db.resultSet<${referencedTableNames.tableClassName}>('${ot.schemaName}')" - : "\$db.${ot.dbGetterName}"; + final referencedTableField = _referenceTable(ot); final filterName = oc.referenceName ?? "${referencedTableNames.table.dbGetterName}Refs"; @@ -588,12 +595,6 @@ class ManagerWriter { void write() { final leaf = _scope.leaf(); - // When generating with modular generation, we need to add the imports - // for the internal `resultSet` helper - if (_scope.generationOptions.isModular) { - leaf.refUri(ModularAccessorWriter.modularSupport, ''); - } - // Write the manager class for each table final tableWriters = <_TableWriter>[]; for (var table in _addedTables) { diff --git a/drift_dev/test/writer/data_class_writer_test.dart b/drift_dev/test/writer/data_class_writer_test.dart index 25ad6e82..93f71703 100644 --- a/drift_dev/test/writer/data_class_writer_test.dart +++ b/drift_dev/test/writer/data_class_writer_test.dart @@ -5,7 +5,6 @@ import 'package:analyzer/file_system/memory_file_system.dart'; import 'package:build/build.dart'; import 'package:build_test/build_test.dart'; import 'package:collection/collection.dart'; -import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; import '../utils.dart'; diff --git a/examples/manager/lib/database.dart b/examples/manager/lib/database.dart index a5033912..7ad1aabb 100644 --- a/examples/manager/lib/database.dart +++ b/examples/manager/lib/database.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:drift/drift.dart'; -import 'package:drift/internal/manager.dart'; import 'package:drift/native.dart'; import 'package:flutter/material.dart' show Color; import 'package:manager/tables.dart'; diff --git a/examples/manager/lib/database.g.dart b/examples/manager/lib/database.g.dart index 0a6e545a..40b0c010 100644 --- a/examples/manager/lib/database.g.dart +++ b/examples/manager/lib/database.g.dart @@ -960,7 +960,7 @@ class $$ProductsTableFilterComposer ColumnWithTypeConverterFilters($table.color); ComposableFilter listings( ComposableFilter Function($$ListingsTableFilterComposer f) f) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.listings, @@ -1063,7 +1063,7 @@ class $$OwnerTableFilterComposer ColumnFilters get name => ColumnFilters($table.name); ComposableFilter stores( ComposableFilter Function($$StoreTableFilterComposer f) f) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.store, @@ -1146,7 +1146,7 @@ class $$StoreTableFilterComposer ColumnFilters get ownerId => ColumnFilters($table.owner); ComposableFilter owner( ComposableFilter Function($$OwnerTableFilterComposer f) f) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.owner, @@ -1159,7 +1159,7 @@ class $$StoreTableFilterComposer ComposableFilter listings( ComposableFilter Function($$ListingsTableFilterComposer f) f) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.listings, @@ -1179,7 +1179,7 @@ class $$StoreTableOrderingComposer ColumnOrderings get ownerId => ColumnOrderings($table.owner); ComposableOrdering owner( ComposableOrdering Function($$OwnerTableOrderingComposer o) o) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.owner, @@ -1260,7 +1260,7 @@ class $$ListingsTableFilterComposer ColumnFilters get productId => ColumnFilters($table.product); ComposableFilter product( ComposableFilter Function($$ProductsTableFilterComposer f) f) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.products, @@ -1274,7 +1274,7 @@ class $$ListingsTableFilterComposer ColumnFilters get storeId => ColumnFilters($table.store); ComposableFilter store( ComposableFilter Function($$StoreTableFilterComposer f) f) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.store, @@ -1295,7 +1295,7 @@ class $$ListingsTableOrderingComposer ColumnOrderings get productId => ColumnOrderings($table.product); ComposableOrdering product( ComposableOrdering Function($$ProductsTableOrderingComposer o) o) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.products, @@ -1309,7 +1309,7 @@ class $$ListingsTableOrderingComposer ColumnOrderings get storeId => ColumnOrderings($table.store); ComposableOrdering store( ComposableOrdering Function($$StoreTableOrderingComposer o) o) { - return composeWithJoins( + return $composeWithJoins( $db: $db, $table: $table, referencedTable: $db.store, diff --git a/examples/manager/lib/main.dart b/examples/manager/lib/main.dart index 1a93b016..45cf5402 100644 --- a/examples/manager/lib/main.dart +++ b/examples/manager/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -11,7 +10,7 @@ import 'package:manager/pages/store.dart'; late final AppDatabase db; void main() { db = AppDatabase(); - runApp(ProviderScope(child: const MyApp())); + runApp(const ProviderScope(child: MyApp())); } class MyApp extends StatelessWidget { diff --git a/examples/manager/lib/pages/listings.dart b/examples/manager/lib/pages/listings.dart index 93e41ef7..9a7c4b78 100644 --- a/examples/manager/lib/pages/listings.dart +++ b/examples/manager/lib/pages/listings.dart @@ -1,4 +1,3 @@ -import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -43,7 +42,7 @@ class ListingPage extends HookConsumerWidget { onPressed: () { db.managers.products.delete(product); }, - icon: Icon(Icons.delete)), + icon: const Icon(Icons.delete)), leading: CircleAvatar( backgroundColor: product.color, ), @@ -66,14 +65,14 @@ class ListingPage extends HookConsumerWidget { final store = useState(null); return SimpleDialog( - title: Text("Add Listing"), + title: const Text("Add Listing"), children: [ TextField( controller: priceTextController, - decoration: InputDecoration(labelText: "Price"), + decoration: const InputDecoration(labelText: "Price"), ), ListTile( - title: Text("Product"), + title: const Text("Product"), subtitle: product.value == null ? null : Text(product.value!.name), @@ -86,7 +85,7 @@ class ListingPage extends HookConsumerWidget { db.managers.products.all().watch()); final items = data.data ?? []; return SimpleDialog( - title: Text("Select Product"), + title: const Text("Select Product"), children: [ ...items.map((e) => ListTile( title: Text(e.name), @@ -102,7 +101,7 @@ class ListingPage extends HookConsumerWidget { }, ), ListTile( - title: Text("Store"), + title: const Text("Store"), subtitle: store.value == null ? null : Text(store.value!.name), @@ -115,7 +114,7 @@ class ListingPage extends HookConsumerWidget { db.managers.store.all().watch()); final items = data.data ?? []; return SimpleDialog( - title: Text("Select Store"), + title: const Text("Select Store"), children: [ ...items.map((e) => ListTile( title: Text(e.name), @@ -138,13 +137,13 @@ class ListingPage extends HookConsumerWidget { store: store.value!.id)); Navigator.of(ctx).pop(); }, - child: Text("Add")) + child: const Text("Add")) ], ); }); }); }, - child: Icon(Icons.add), + child: const Icon(Icons.add), ), body: body, ); diff --git a/examples/manager/lib/pages/owners.dart b/examples/manager/lib/pages/owners.dart index b313ba66..bf2646c6 100644 --- a/examples/manager/lib/pages/owners.dart +++ b/examples/manager/lib/pages/owners.dart @@ -1,10 +1,8 @@ -import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:manager/database.dart'; import 'package:manager/main.dart'; -import 'package:manager/tables.dart'; class OwnersPage extends HookConsumerWidget { const OwnersPage({super.key}); @@ -27,7 +25,7 @@ class OwnersPage extends HookConsumerWidget { onPressed: () { db.managers.owner.delete(product); }, - icon: Icon(Icons.delete)), + icon: const Icon(Icons.delete)), ); }, ); @@ -45,11 +43,11 @@ class OwnersPage extends HookConsumerWidget { final nameTextController = useTextEditingController(); return SimpleDialog( - title: Text("Add Owner"), + title: const Text("Add Owner"), children: [ TextField( controller: nameTextController, - decoration: InputDecoration(labelText: "Name"), + decoration: const InputDecoration(labelText: "Name"), ), ElevatedButton( onPressed: () { @@ -58,13 +56,13 @@ class OwnersPage extends HookConsumerWidget { )); Navigator.of(ctx).pop(); }, - child: Text("Add")) + child: const Text("Add")) ], ); }); }); }, - child: Icon(Icons.add), + child: const Icon(Icons.add), ), body: body, ); diff --git a/examples/manager/lib/pages/product.dart b/examples/manager/lib/pages/product.dart index 185f7c4d..5850350a 100644 --- a/examples/manager/lib/pages/product.dart +++ b/examples/manager/lib/pages/product.dart @@ -27,7 +27,7 @@ class ProductPage extends HookConsumerWidget { onPressed: () { db.managers.products.delete(product); }, - icon: Icon(Icons.delete)), + icon: const Icon(Icons.delete)), leading: CircleAvatar( backgroundColor: product.color, ), @@ -51,21 +51,21 @@ class ProductPage extends HookConsumerWidget { final releaseDate = useState(null); return SimpleDialog( - title: Text("Add Product"), + title: const Text("Add Product"), children: [ TextField( controller: nameTextController, - decoration: InputDecoration(labelText: "Name"), + decoration: const InputDecoration(labelText: "Name"), ), TextField( controller: descriptionTextController, - decoration: InputDecoration(labelText: "Description"), + decoration: const InputDecoration(labelText: "Description"), ), ListTile( leading: CircleAvatar( backgroundColor: color.value, ), - title: Text("Color"), + title: const Text("Color"), onTap: () { showColorPickerDialog( context, color.value ?? Colors.blue) @@ -73,8 +73,8 @@ class ProductPage extends HookConsumerWidget { }, ), ListTile( - leading: Icon(Icons.calendar_month), - title: Text("Date"), + leading: const Icon(Icons.calendar_month), + title: const Text("Date"), subtitle: releaseDate.value != null ? Text(releaseDate.value.toString()) : null, @@ -98,13 +98,13 @@ class ProductPage extends HookConsumerWidget { releaseDate: releaseDate.value!)); Navigator.of(ctx).pop(); }, - child: Text("Add")) + child: const Text("Add")) ], ); }); }); }, - child: Icon(Icons.add), + child: const Icon(Icons.add), ), body: body, ); diff --git a/examples/manager/lib/pages/store.dart b/examples/manager/lib/pages/store.dart index 07500ec4..749c6682 100644 --- a/examples/manager/lib/pages/store.dart +++ b/examples/manager/lib/pages/store.dart @@ -1,4 +1,3 @@ -import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -36,7 +35,7 @@ class StorePage extends HookConsumerWidget { onPressed: () { db.managers.store.delete(store); }, - icon: Icon(Icons.delete)), + icon: const Icon(Icons.delete)), ); }, ); @@ -55,14 +54,14 @@ class StorePage extends HookConsumerWidget { final owner = useState(null); return SimpleDialog( - title: Text("Add Store"), + title: const Text("Add Store"), children: [ TextField( controller: nameTextController, - decoration: InputDecoration(labelText: "Name"), + decoration: const InputDecoration(labelText: "Name"), ), ListTile( - title: Text("Owner"), + title: const Text("Owner"), subtitle: Text(owner.value?.name ?? "Select Owner"), onTap: () { showDialog( @@ -74,7 +73,7 @@ class StorePage extends HookConsumerWidget { final data = useStream(stream); return SimpleDialog( - title: Text("Select Owner"), + title: const Text("Select Owner"), children: [ if (data.hasData) for (final o in data.data!) @@ -98,13 +97,13 @@ class StorePage extends HookConsumerWidget { owner: owner.value!.id)); Navigator.of(ctx).pop(); }, - child: Text("Add")) + child: const Text("Add")) ], ); }); }); }, - child: Icon(Icons.add), + child: const Icon(Icons.add), ), body: body, ); diff --git a/examples/manager/pubspec.yaml b/examples/manager/pubspec.yaml index 9b309869..2088e914 100644 --- a/examples/manager/pubspec.yaml +++ b/examples/manager/pubspec.yaml @@ -33,9 +33,3 @@ dev_dependencies: flutter: uses-material-design: true - -dependency_overrides: - drift_dev: - path: C:/Users/dicke/drift/drift_dev - drift: - path: C:/Users/dicke/drift/drift From c723b4e81ef810c436f6260c1ef01c7640249119 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 5 Apr 2024 21:52:18 +0200 Subject: [PATCH 44/74] Don't generate manager code for old snapshots --- .../cli/commands/schema/generate_utils.dart | 1 + .../migrations_example/lib/database.g.dart | 329 +++++++++++++++++- .../test/generated/schema_v1.dart | 5 +- .../test/generated/schema_v10.dart | 15 +- .../test/generated/schema_v2.dart | 5 +- .../test/generated/schema_v3.dart | 10 +- .../test/generated/schema_v4.dart | 10 +- .../test/generated/schema_v5.dart | 10 +- .../test/generated/schema_v6.dart | 10 +- .../test/generated/schema_v7.dart | 15 +- .../test/generated/schema_v8.dart | 15 +- .../test/generated/schema_v9.dart | 15 +- 12 files changed, 395 insertions(+), 45 deletions(-) diff --git a/drift_dev/lib/src/cli/commands/schema/generate_utils.dart b/drift_dev/lib/src/cli/commands/schema/generate_utils.dart index ccbdf120..f9786d3e 100644 --- a/drift_dev/lib/src/cli/commands/schema/generate_utils.dart +++ b/drift_dev/lib/src/cli/commands/schema/generate_utils.dart @@ -93,6 +93,7 @@ class GenerateUtilsCommand extends Command { final options = DriftOptions.fromJson({ ...cli.project.moorOptions.toJson(), ...schema.options, + 'generate_manager': false, }); final writer = Writer( diff --git a/examples/migrations_example/lib/database.g.dart b/examples/migrations_example/lib/database.g.dart index f0656955..1ad77e32 100644 --- a/examples/migrations_example/lib/database.g.dart +++ b/examples/migrations_example/lib/database.g.dart @@ -282,7 +282,7 @@ class Groups extends Table with TableInfo { 'deleted', aliasedName, true, type: DriftSqlType.bool, requiredDuringInsert: false, - $customConstraints: 'DEFAULT FALSE', + $customConstraints: 'NULL DEFAULT FALSE', defaultValue: const CustomExpression('FALSE')); static const VerificationMeta _ownerMeta = const VerificationMeta('owner'); late final GeneratedColumn owner = GeneratedColumn( @@ -884,6 +884,7 @@ class GroupCount extends ViewInfo abstract class _$Database extends GeneratedDatabase { _$Database(QueryExecutor e) : super(e); + _$DatabaseManager get managers => _$DatabaseManager(this); late final $UsersTable users = $UsersTable(this); late final Groups groups = Groups(this); late final Notes notes = Notes(this); @@ -900,3 +901,329 @@ abstract class _$Database extends GeneratedDatabase { DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); } + +class $$UsersTableFilterComposer + extends FilterComposer<_$Database, $UsersTable> { + $$UsersTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get name => ColumnFilters($table.name); + ColumnFilters get birthday => ColumnFilters($table.birthday); + ColumnFilters get nextUserId => ColumnFilters($table.nextUser); + ComposableFilter nextUser( + ComposableFilter Function($$UsersTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.users, + getCurrentColumn: (f) => f.nextUser, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$UsersTableFilterComposer(db, table), + builder: f); + } + + ComposableFilter usersRefs( + ComposableFilter Function($$UsersTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.users, + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.nextUser, + getReferencedComposer: (db, table) => + $$UsersTableFilterComposer(db, table), + builder: f); + } + + ComposableFilter groupsRefs( + ComposableFilter Function($GroupsFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.groups, + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.owner, + getReferencedComposer: (db, table) => $GroupsFilterComposer(db, table), + builder: f); + } +} + +class $$UsersTableOrderingComposer + extends OrderingComposer<_$Database, $UsersTable> { + $$UsersTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); + ColumnOrderings get birthday => ColumnOrderings($table.birthday); + ColumnOrderings get nextUserId => ColumnOrderings($table.nextUser); + ComposableOrdering nextUser( + ComposableOrdering Function($$UsersTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.users, + getCurrentColumn: (f) => f.nextUser, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$UsersTableOrderingComposer(db, table), + builder: o); + } +} + +class $$UsersTableProcessedTableManager extends ProcessedTableManager< + _$Database, + $UsersTable, + User, + $$UsersTableFilterComposer, + $$UsersTableOrderingComposer, + $$UsersTableProcessedTableManager, + $$UsersTableInsertCompanionBuilder, + $$UsersTableUpdateCompanionBuilder> { + const $$UsersTableProcessedTableManager(super.$state); +} + +typedef $$UsersTableInsertCompanionBuilder = UsersCompanion Function({ + Value id, + Value name, + Value birthday, + Value nextUser, +}); +typedef $$UsersTableUpdateCompanionBuilder = UsersCompanion Function({ + Value id, + Value name, + Value birthday, + Value nextUser, +}); + +class $$UsersTableTableManager extends RootTableManager< + _$Database, + $UsersTable, + User, + $$UsersTableFilterComposer, + $$UsersTableOrderingComposer, + $$UsersTableProcessedTableManager, + $$UsersTableInsertCompanionBuilder, + $$UsersTableUpdateCompanionBuilder> { + $$UsersTableTableManager(_$Database db, $UsersTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$UsersTableFilterComposer(db, table), + orderingComposer: $$UsersTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$UsersTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value birthday = const Value.absent(), + Value nextUser = const Value.absent(), + }) => + UsersCompanion( + id: id, + name: name, + birthday: birthday, + nextUser: nextUser, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value birthday = const Value.absent(), + Value nextUser = const Value.absent(), + }) => + UsersCompanion.insert( + id: id, + name: name, + birthday: birthday, + nextUser: nextUser, + ))); +} + +class $GroupsFilterComposer extends FilterComposer<_$Database, Groups> { + $GroupsFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get title => ColumnFilters($table.title); + ColumnFilters get deleted => ColumnFilters($table.deleted); + ColumnFilters get ownerId => ColumnFilters($table.owner); + ComposableFilter owner( + ComposableFilter Function($$UsersTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.users, + getCurrentColumn: (f) => f.owner, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$UsersTableFilterComposer(db, table), + builder: f); + } +} + +class $GroupsOrderingComposer extends OrderingComposer<_$Database, Groups> { + $GroupsOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get deleted => ColumnOrderings($table.deleted); + ColumnOrderings get ownerId => ColumnOrderings($table.owner); + ComposableOrdering owner( + ComposableOrdering Function($$UsersTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.users, + getCurrentColumn: (f) => f.owner, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$UsersTableOrderingComposer(db, table), + builder: o); + } +} + +class $GroupsProcessedTableManager extends ProcessedTableManager< + _$Database, + Groups, + Group, + $GroupsFilterComposer, + $GroupsOrderingComposer, + $GroupsProcessedTableManager, + $GroupsInsertCompanionBuilder, + $GroupsUpdateCompanionBuilder> { + const $GroupsProcessedTableManager(super.$state); +} + +typedef $GroupsInsertCompanionBuilder = GroupsCompanion Function({ + Value id, + required String title, + Value deleted, + required int owner, +}); +typedef $GroupsUpdateCompanionBuilder = GroupsCompanion Function({ + Value id, + Value title, + Value deleted, + Value owner, +}); + +class $GroupsTableManager extends RootTableManager< + _$Database, + Groups, + Group, + $GroupsFilterComposer, + $GroupsOrderingComposer, + $GroupsProcessedTableManager, + $GroupsInsertCompanionBuilder, + $GroupsUpdateCompanionBuilder> { + $GroupsTableManager(_$Database db, Groups table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $GroupsFilterComposer(db, table), + orderingComposer: $GroupsOrderingComposer(db, table), + getChildManagerBuilder: (p0) => $GroupsProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value title = const Value.absent(), + Value deleted = const Value.absent(), + Value owner = const Value.absent(), + }) => + GroupsCompanion( + id: id, + title: title, + deleted: deleted, + owner: owner, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String title, + Value deleted = const Value.absent(), + required int owner, + }) => + GroupsCompanion.insert( + id: id, + title: title, + deleted: deleted, + owner: owner, + ))); +} + +class $NotesFilterComposer extends FilterComposer<_$Database, Notes> { + $NotesFilterComposer(super.db, super.table); + ColumnFilters get title => ColumnFilters($table.title); + ColumnFilters get content => ColumnFilters($table.content); + ColumnFilters get searchTerms => ColumnFilters($table.searchTerms); +} + +class $NotesOrderingComposer extends OrderingComposer<_$Database, Notes> { + $NotesOrderingComposer(super.db, super.table); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get content => ColumnOrderings($table.content); + ColumnOrderings get searchTerms => ColumnOrderings($table.searchTerms); +} + +class $NotesProcessedTableManager extends ProcessedTableManager< + _$Database, + Notes, + Note, + $NotesFilterComposer, + $NotesOrderingComposer, + $NotesProcessedTableManager, + $NotesInsertCompanionBuilder, + $NotesUpdateCompanionBuilder> { + const $NotesProcessedTableManager(super.$state); +} + +typedef $NotesInsertCompanionBuilder = NotesCompanion Function({ + required String title, + required String content, + required String searchTerms, +}); +typedef $NotesUpdateCompanionBuilder = NotesCompanion Function({ + Value title, + Value content, + Value searchTerms, +}); + +class $NotesTableManager extends RootTableManager< + _$Database, + Notes, + Note, + $NotesFilterComposer, + $NotesOrderingComposer, + $NotesProcessedTableManager, + $NotesInsertCompanionBuilder, + $NotesUpdateCompanionBuilder> { + $NotesTableManager(_$Database db, Notes table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $NotesFilterComposer(db, table), + orderingComposer: $NotesOrderingComposer(db, table), + getChildManagerBuilder: (p0) => $NotesProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value title = const Value.absent(), + Value content = const Value.absent(), + Value searchTerms = const Value.absent(), + }) => + NotesCompanion( + title: title, + content: content, + searchTerms: searchTerms, + ), + getInsertCompanionBuilder: ({ + required String title, + required String content, + required String searchTerms, + }) => + NotesCompanion.insert( + title: title, + content: content, + searchTerms: searchTerms, + ))); +} + +class _$DatabaseManager { + final _$Database _db; + _$DatabaseManager(this._db); + $$UsersTableTableManager get users => + $$UsersTableTableManager(_db, _db.users); + $GroupsTableManager get groups => $GroupsTableManager(_db, _db.groups); + $NotesTableManager get notes => $NotesTableManager(_db, _db.notes); +} diff --git a/examples/migrations_example/test/generated/schema_v1.dart b/examples/migrations_example/test/generated/schema_v1.dart index 8a904d9e..9e433bc7 100644 --- a/examples/migrations_example/test/generated/schema_v1.dart +++ b/examples/migrations_example/test/generated/schema_v1.dart @@ -18,9 +18,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override diff --git a/examples/migrations_example/test/generated/schema_v10.dart b/examples/migrations_example/test/generated/schema_v10.dart index 2ad771ef..1dca648f 100644 --- a/examples/migrations_example/test/generated/schema_v10.dart +++ b/examples/migrations_example/test/generated/schema_v10.dart @@ -32,9 +32,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name, birthday, nextUser]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -257,9 +258,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override @@ -475,9 +477,10 @@ class Notes extends Table @override List get $columns => [title, content, searchTerms]; @override - String get aliasedName => _alias ?? 'notes'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'notes'; + String get actualTableName => $name; + static const String $name = 'notes'; @override Set get $primaryKey => const {}; @override diff --git a/examples/migrations_example/test/generated/schema_v2.dart b/examples/migrations_example/test/generated/schema_v2.dart index 6999962e..95213483 100644 --- a/examples/migrations_example/test/generated/schema_v2.dart +++ b/examples/migrations_example/test/generated/schema_v2.dart @@ -21,9 +21,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override diff --git a/examples/migrations_example/test/generated/schema_v3.dart b/examples/migrations_example/test/generated/schema_v3.dart index 25b4e041..65fb64a9 100644 --- a/examples/migrations_example/test/generated/schema_v3.dart +++ b/examples/migrations_example/test/generated/schema_v3.dart @@ -21,9 +21,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -179,9 +180,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override diff --git a/examples/migrations_example/test/generated/schema_v4.dart b/examples/migrations_example/test/generated/schema_v4.dart index a3a124da..f6366291 100644 --- a/examples/migrations_example/test/generated/schema_v4.dart +++ b/examples/migrations_example/test/generated/schema_v4.dart @@ -23,9 +23,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -181,9 +182,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override diff --git a/examples/migrations_example/test/generated/schema_v5.dart b/examples/migrations_example/test/generated/schema_v5.dart index f1a2c19e..e8b57f49 100644 --- a/examples/migrations_example/test/generated/schema_v5.dart +++ b/examples/migrations_example/test/generated/schema_v5.dart @@ -29,9 +29,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name, nextUser]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -218,9 +219,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override diff --git a/examples/migrations_example/test/generated/schema_v6.dart b/examples/migrations_example/test/generated/schema_v6.dart index 5b1a8615..ea4bf018 100644 --- a/examples/migrations_example/test/generated/schema_v6.dart +++ b/examples/migrations_example/test/generated/schema_v6.dart @@ -32,9 +32,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name, birthday, nextUser]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -250,9 +251,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override diff --git a/examples/migrations_example/test/generated/schema_v7.dart b/examples/migrations_example/test/generated/schema_v7.dart index a78587c0..cbb66aa1 100644 --- a/examples/migrations_example/test/generated/schema_v7.dart +++ b/examples/migrations_example/test/generated/schema_v7.dart @@ -32,9 +32,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name, birthday, nextUser]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -250,9 +251,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override @@ -603,9 +605,10 @@ class Notes extends Table @override List get $columns => [title, content, searchTerms]; @override - String get aliasedName => _alias ?? 'notes'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'notes'; + String get actualTableName => $name; + static const String $name = 'notes'; @override Set get $primaryKey => const {}; @override diff --git a/examples/migrations_example/test/generated/schema_v8.dart b/examples/migrations_example/test/generated/schema_v8.dart index 83e39b74..0a593021 100644 --- a/examples/migrations_example/test/generated/schema_v8.dart +++ b/examples/migrations_example/test/generated/schema_v8.dart @@ -32,9 +32,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name, birthday, nextUser]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -254,9 +255,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override @@ -607,9 +609,10 @@ class Notes extends Table @override List get $columns => [title, content, searchTerms]; @override - String get aliasedName => _alias ?? 'notes'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'notes'; + String get actualTableName => $name; + static const String $name = 'notes'; @override Set get $primaryKey => const {}; @override diff --git a/examples/migrations_example/test/generated/schema_v9.dart b/examples/migrations_example/test/generated/schema_v9.dart index 91fbb247..e819e605 100644 --- a/examples/migrations_example/test/generated/schema_v9.dart +++ b/examples/migrations_example/test/generated/schema_v9.dart @@ -32,9 +32,10 @@ class Users extends Table with TableInfo { @override List get $columns => [id, name, birthday, nextUser]; @override - String get aliasedName => _alias ?? 'users'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'users'; + String get actualTableName => $name; + static const String $name = 'users'; @override Set get $primaryKey => {id}; @override @@ -257,9 +258,10 @@ class Groups extends Table with TableInfo { @override List get $columns => [id, title, deleted, owner]; @override - String get aliasedName => _alias ?? 'groups'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'groups'; + String get actualTableName => $name; + static const String $name = 'groups'; @override Set get $primaryKey => {id}; @override @@ -475,9 +477,10 @@ class Notes extends Table @override List get $columns => [title, content, searchTerms]; @override - String get aliasedName => _alias ?? 'notes'; + String get aliasedName => _alias ?? actualTableName; @override - String get actualTableName => 'notes'; + String get actualTableName => $name; + static const String $name = 'notes'; @override Set get $primaryKey => const {}; @override From 7a8983dcc4cc08e04a1b37d0821567f7608ef428 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sat, 6 Apr 2024 21:20:11 -0400 Subject: [PATCH 45/74] distict count by defualt --- drift/lib/src/runtime/manager/manager.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 0c98b7b8..092f1369 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -301,9 +301,6 @@ abstract class BaseTableManager< ._getChildManagerBuilder($state.copyWith(limit: limit, offset: offset)); } - /// Return the count of rows matched by the built statement - Future count() => $state.count(); - /// Writes all non-null fields from the entity into the columns of all rows /// that match the [filter] clause. Warning: That also means that, when you're /// not setting a where clause explicitly, this method will update all rows in @@ -338,6 +335,12 @@ abstract class ProcessedTableManager< /// Create a new [ProcessedTableManager] instance const ProcessedTableManager(super.$state); + /// Return the count of rows matched by the built statement + /// When counting rows, the query will only count distinct rows by default + Future count([bool distinct = true]) { + return $state.copyWith(distinct: true).count(); + } + /// Deletes all rows matched by built statement /// /// Returns the amount of rows that were deleted by this statement directly From 93681d5b799f1da0bbe109c6afff4cc7587ea34a Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sat, 6 Apr 2024 21:29:36 -0400 Subject: [PATCH 46/74] add exists --- drift/lib/src/runtime/manager/manager.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 092f1369..83f85135 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:drift/drift.dart'; import 'package:meta/meta.dart'; @@ -231,6 +233,22 @@ class TableManagerState< return result.statement.map((row) => row.read(count)!).getSingle(); } + /// Check if any rows exists using the built statement + Future exists() async { + final result = _buildSelectStatement(); + final BaseSelectStatement statement; + switch (result) { + case _SimpleResult(): + statement = result.statement; + case _JoinedResult(): + statement = result.statement; + } + final query = existsQuery(statement); + final existsStatement = db.selectOnly(_tableAsTableInfo) + ..addColumns([query]); + return (await existsStatement.map((p0) => p0.read(query)).getSingle())!; + } + /// Build a delete statement based on the manager state DeleteStatement buildDeleteStatement() { final DeleteStatement deleteStatement; @@ -341,6 +359,9 @@ abstract class ProcessedTableManager< return $state.copyWith(distinct: true).count(); } + /// Checks whether any rows exist + Future exists() => $state.exists(); + /// Deletes all rows matched by built statement /// /// Returns the amount of rows that were deleted by this statement directly From e3a8ccdd6d6f05e7f8f635604965652e481225af Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 7 Apr 2024 07:21:02 -0400 Subject: [PATCH 47/74] update generations, fix custom row --- drift/test/generated/custom_tables.g.dart | 517 ++++++++++++++++ drift/test/generated/todos.g.dart | 565 ++++++++++++++++++ .../regress_2166_test.g.dart | 78 +++ drift/test/test_utils/test_utils.mocks.dart | 2 +- drift_dev/lib/src/writer/manager_writer.dart | 7 +- .../lib/src/writer/tables/table_writer.dart | 1 - 6 files changed, 1167 insertions(+), 3 deletions(-) diff --git a/drift/test/generated/custom_tables.g.dart b/drift/test/generated/custom_tables.g.dart index a116dc90..f8bbe29f 100644 --- a/drift/test/generated/custom_tables.g.dart +++ b/drift/test/generated/custom_tables.g.dart @@ -1657,6 +1657,7 @@ class MyView extends ViewInfo implements HasResultSet { abstract class _$CustomTablesDb extends GeneratedDatabase { _$CustomTablesDb(QueryExecutor e) : super(e); + _$CustomTablesDbManager get managers => _$CustomTablesDbManager(this); late final NoIds noIds = NoIds(this); late final WithDefaults withDefaults = WithDefaults(this); late final WithConstraints withConstraints = WithConstraints(this); @@ -1970,6 +1971,522 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { const DriftDatabaseOptions(storeDateTimeAsText: true); } +class $WithDefaultsFilterComposer + extends FilterComposer<_$CustomTablesDb, WithDefaults> { + $WithDefaultsFilterComposer(super.db, super.table); + ColumnFilters get a => ColumnFilters($table.a); + ColumnFilters get b => ColumnFilters($table.b); +} + +class $WithDefaultsOrderingComposer + extends OrderingComposer<_$CustomTablesDb, WithDefaults> { + $WithDefaultsOrderingComposer(super.db, super.table); + ColumnOrderings get a => ColumnOrderings($table.a); + ColumnOrderings get b => ColumnOrderings($table.b); +} + +class $WithDefaultsProcessedTableManager extends ProcessedTableManager< + _$CustomTablesDb, + WithDefaults, + WithDefault, + $WithDefaultsFilterComposer, + $WithDefaultsOrderingComposer, + $WithDefaultsProcessedTableManager, + $WithDefaultsInsertCompanionBuilder, + $WithDefaultsUpdateCompanionBuilder> { + const $WithDefaultsProcessedTableManager(super.$state); +} + +typedef $WithDefaultsInsertCompanionBuilder = WithDefaultsCompanion Function({ + Value a, + Value b, + Value rowid, +}); +typedef $WithDefaultsUpdateCompanionBuilder = WithDefaultsCompanion Function({ + Value a, + Value b, + Value rowid, +}); + +class $WithDefaultsTableManager extends RootTableManager< + _$CustomTablesDb, + WithDefaults, + WithDefault, + $WithDefaultsFilterComposer, + $WithDefaultsOrderingComposer, + $WithDefaultsProcessedTableManager, + $WithDefaultsInsertCompanionBuilder, + $WithDefaultsUpdateCompanionBuilder> { + $WithDefaultsTableManager(_$CustomTablesDb db, WithDefaults table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $WithDefaultsFilterComposer(db, table), + orderingComposer: $WithDefaultsOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $WithDefaultsProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value a = const Value.absent(), + Value b = const Value.absent(), + Value rowid = const Value.absent(), + }) => + WithDefaultsCompanion( + a: a, + b: b, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + Value a = const Value.absent(), + Value b = const Value.absent(), + Value rowid = const Value.absent(), + }) => + WithDefaultsCompanion.insert( + a: a, + b: b, + rowid: rowid, + ))); +} + +class $WithConstraintsFilterComposer + extends FilterComposer<_$CustomTablesDb, WithConstraints> { + $WithConstraintsFilterComposer(super.db, super.table); + ColumnFilters get a => ColumnFilters($table.a); + ColumnFilters get b => ColumnFilters($table.b); + ColumnFilters get c => ColumnFilters($table.c); +} + +class $WithConstraintsOrderingComposer + extends OrderingComposer<_$CustomTablesDb, WithConstraints> { + $WithConstraintsOrderingComposer(super.db, super.table); + ColumnOrderings get a => ColumnOrderings($table.a); + ColumnOrderings get b => ColumnOrderings($table.b); + ColumnOrderings get c => ColumnOrderings($table.c); +} + +class $WithConstraintsProcessedTableManager extends ProcessedTableManager< + _$CustomTablesDb, + WithConstraints, + WithConstraint, + $WithConstraintsFilterComposer, + $WithConstraintsOrderingComposer, + $WithConstraintsProcessedTableManager, + $WithConstraintsInsertCompanionBuilder, + $WithConstraintsUpdateCompanionBuilder> { + const $WithConstraintsProcessedTableManager(super.$state); +} + +typedef $WithConstraintsInsertCompanionBuilder = WithConstraintsCompanion + Function({ + Value a, + required int b, + Value c, + Value rowid, +}); +typedef $WithConstraintsUpdateCompanionBuilder = WithConstraintsCompanion + Function({ + Value a, + Value b, + Value c, + Value rowid, +}); + +class $WithConstraintsTableManager extends RootTableManager< + _$CustomTablesDb, + WithConstraints, + WithConstraint, + $WithConstraintsFilterComposer, + $WithConstraintsOrderingComposer, + $WithConstraintsProcessedTableManager, + $WithConstraintsInsertCompanionBuilder, + $WithConstraintsUpdateCompanionBuilder> { + $WithConstraintsTableManager(_$CustomTablesDb db, WithConstraints table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $WithConstraintsFilterComposer(db, table), + orderingComposer: $WithConstraintsOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $WithConstraintsProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value a = const Value.absent(), + Value b = const Value.absent(), + Value c = const Value.absent(), + Value rowid = const Value.absent(), + }) => + WithConstraintsCompanion( + a: a, + b: b, + c: c, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + Value a = const Value.absent(), + required int b, + Value c = const Value.absent(), + Value rowid = const Value.absent(), + }) => + WithConstraintsCompanion.insert( + a: a, + b: b, + c: c, + rowid: rowid, + ))); +} + +class $ConfigTableFilterComposer + extends FilterComposer<_$CustomTablesDb, ConfigTable> { + $ConfigTableFilterComposer(super.db, super.table); + ColumnFilters get configKey => ColumnFilters($table.configKey); + ColumnFilters get configValue => ColumnFilters($table.configValue); + ColumnFilters get syncStateValue => ColumnFilters($table.syncState); + ColumnWithTypeConverterFilters get syncState => + ColumnWithTypeConverterFilters($table.syncState); + ColumnFilters get syncStateImplicitValue => + ColumnFilters($table.syncStateImplicit); + ColumnWithTypeConverterFilters get syncStateImplicit => + ColumnWithTypeConverterFilters($table.syncStateImplicit); +} + +class $ConfigTableOrderingComposer + extends OrderingComposer<_$CustomTablesDb, ConfigTable> { + $ConfigTableOrderingComposer(super.db, super.table); + ColumnOrderings get configKey => ColumnOrderings($table.configKey); + ColumnOrderings get configValue => ColumnOrderings($table.configValue); + ColumnOrderings get syncState => ColumnOrderings($table.syncState); + ColumnOrderings get syncStateImplicit => + ColumnOrderings($table.syncStateImplicit); +} + +class $ConfigTableProcessedTableManager extends ProcessedTableManager< + _$CustomTablesDb, + ConfigTable, + Config, + $ConfigTableFilterComposer, + $ConfigTableOrderingComposer, + $ConfigTableProcessedTableManager, + $ConfigTableInsertCompanionBuilder, + $ConfigTableUpdateCompanionBuilder> { + const $ConfigTableProcessedTableManager(super.$state); +} + +typedef $ConfigTableInsertCompanionBuilder = ConfigCompanion Function({ + required String configKey, + Value configValue, + Value syncState, + Value syncStateImplicit, + Value rowid, +}); +typedef $ConfigTableUpdateCompanionBuilder = ConfigCompanion Function({ + Value configKey, + Value configValue, + Value syncState, + Value syncStateImplicit, + Value rowid, +}); + +class $ConfigTableTableManager extends RootTableManager< + _$CustomTablesDb, + ConfigTable, + Config, + $ConfigTableFilterComposer, + $ConfigTableOrderingComposer, + $ConfigTableProcessedTableManager, + $ConfigTableInsertCompanionBuilder, + $ConfigTableUpdateCompanionBuilder> { + $ConfigTableTableManager(_$CustomTablesDb db, ConfigTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $ConfigTableFilterComposer(db, table), + orderingComposer: $ConfigTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $ConfigTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value configKey = const Value.absent(), + Value configValue = const Value.absent(), + Value syncState = const Value.absent(), + Value syncStateImplicit = const Value.absent(), + Value rowid = const Value.absent(), + }) => + ConfigCompanion( + configKey: configKey, + configValue: configValue, + syncState: syncState, + syncStateImplicit: syncStateImplicit, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + required String configKey, + Value configValue = const Value.absent(), + Value syncState = const Value.absent(), + Value syncStateImplicit = const Value.absent(), + Value rowid = const Value.absent(), + }) => + ConfigCompanion.insert( + configKey: configKey, + configValue: configValue, + syncState: syncState, + syncStateImplicit: syncStateImplicit, + rowid: rowid, + ))); +} + +class $MytableFilterComposer extends FilterComposer<_$CustomTablesDb, Mytable> { + $MytableFilterComposer(super.db, super.table); + ColumnFilters get someid => ColumnFilters($table.someid); + ColumnFilters get sometext => ColumnFilters($table.sometext); + ColumnFilters get isInserting => ColumnFilters($table.isInserting); + ColumnFilters get somedate => ColumnFilters($table.somedate); +} + +class $MytableOrderingComposer + extends OrderingComposer<_$CustomTablesDb, Mytable> { + $MytableOrderingComposer(super.db, super.table); + ColumnOrderings get someid => ColumnOrderings($table.someid); + ColumnOrderings get sometext => ColumnOrderings($table.sometext); + ColumnOrderings get isInserting => ColumnOrderings($table.isInserting); + ColumnOrderings get somedate => ColumnOrderings($table.somedate); +} + +class $MytableProcessedTableManager extends ProcessedTableManager< + _$CustomTablesDb, + Mytable, + MytableData, + $MytableFilterComposer, + $MytableOrderingComposer, + $MytableProcessedTableManager, + $MytableInsertCompanionBuilder, + $MytableUpdateCompanionBuilder> { + const $MytableProcessedTableManager(super.$state); +} + +typedef $MytableInsertCompanionBuilder = MytableCompanion Function({ + Value someid, + Value sometext, + Value isInserting, + Value somedate, +}); +typedef $MytableUpdateCompanionBuilder = MytableCompanion Function({ + Value someid, + Value sometext, + Value isInserting, + Value somedate, +}); + +class $MytableTableManager extends RootTableManager< + _$CustomTablesDb, + Mytable, + MytableData, + $MytableFilterComposer, + $MytableOrderingComposer, + $MytableProcessedTableManager, + $MytableInsertCompanionBuilder, + $MytableUpdateCompanionBuilder> { + $MytableTableManager(_$CustomTablesDb db, Mytable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $MytableFilterComposer(db, table), + orderingComposer: $MytableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => $MytableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value someid = const Value.absent(), + Value sometext = const Value.absent(), + Value isInserting = const Value.absent(), + Value somedate = const Value.absent(), + }) => + MytableCompanion( + someid: someid, + sometext: sometext, + isInserting: isInserting, + somedate: somedate, + ), + getInsertCompanionBuilder: ({ + Value someid = const Value.absent(), + Value sometext = const Value.absent(), + Value isInserting = const Value.absent(), + Value somedate = const Value.absent(), + }) => + MytableCompanion.insert( + someid: someid, + sometext: sometext, + isInserting: isInserting, + somedate: somedate, + ))); +} + +class $EmailFilterComposer extends FilterComposer<_$CustomTablesDb, Email> { + $EmailFilterComposer(super.db, super.table); + ColumnFilters get sender => ColumnFilters($table.sender); + ColumnFilters get title => ColumnFilters($table.title); + ColumnFilters get body => ColumnFilters($table.body); +} + +class $EmailOrderingComposer extends OrderingComposer<_$CustomTablesDb, Email> { + $EmailOrderingComposer(super.db, super.table); + ColumnOrderings get sender => ColumnOrderings($table.sender); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get body => ColumnOrderings($table.body); +} + +class $EmailProcessedTableManager extends ProcessedTableManager< + _$CustomTablesDb, + Email, + EMail, + $EmailFilterComposer, + $EmailOrderingComposer, + $EmailProcessedTableManager, + $EmailInsertCompanionBuilder, + $EmailUpdateCompanionBuilder> { + const $EmailProcessedTableManager(super.$state); +} + +typedef $EmailInsertCompanionBuilder = EmailCompanion Function({ + required String sender, + required String title, + required String body, + Value rowid, +}); +typedef $EmailUpdateCompanionBuilder = EmailCompanion Function({ + Value sender, + Value title, + Value body, + Value rowid, +}); + +class $EmailTableManager extends RootTableManager< + _$CustomTablesDb, + Email, + EMail, + $EmailFilterComposer, + $EmailOrderingComposer, + $EmailProcessedTableManager, + $EmailInsertCompanionBuilder, + $EmailUpdateCompanionBuilder> { + $EmailTableManager(_$CustomTablesDb db, Email table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $EmailFilterComposer(db, table), + orderingComposer: $EmailOrderingComposer(db, table), + getChildManagerBuilder: (p0) => $EmailProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value sender = const Value.absent(), + Value title = const Value.absent(), + Value body = const Value.absent(), + Value rowid = const Value.absent(), + }) => + EmailCompanion( + sender: sender, + title: title, + body: body, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + required String sender, + required String title, + required String body, + Value rowid = const Value.absent(), + }) => + EmailCompanion.insert( + sender: sender, + title: title, + body: body, + rowid: rowid, + ))); +} + +class $WeirdTableFilterComposer + extends FilterComposer<_$CustomTablesDb, WeirdTable> { + $WeirdTableFilterComposer(super.db, super.table); + ColumnFilters get sqlClass => ColumnFilters($table.sqlClass); + ColumnFilters get textColumn => ColumnFilters($table.textColumn); +} + +class $WeirdTableOrderingComposer + extends OrderingComposer<_$CustomTablesDb, WeirdTable> { + $WeirdTableOrderingComposer(super.db, super.table); + ColumnOrderings get sqlClass => ColumnOrderings($table.sqlClass); + ColumnOrderings get textColumn => ColumnOrderings($table.textColumn); +} + +class $WeirdTableProcessedTableManager extends ProcessedTableManager< + _$CustomTablesDb, + WeirdTable, + WeirdData, + $WeirdTableFilterComposer, + $WeirdTableOrderingComposer, + $WeirdTableProcessedTableManager, + $WeirdTableInsertCompanionBuilder, + $WeirdTableUpdateCompanionBuilder> { + const $WeirdTableProcessedTableManager(super.$state); +} + +typedef $WeirdTableInsertCompanionBuilder = WeirdTableCompanion Function({ + required int sqlClass, + required String textColumn, + Value rowid, +}); +typedef $WeirdTableUpdateCompanionBuilder = WeirdTableCompanion Function({ + Value sqlClass, + Value textColumn, + Value rowid, +}); + +class $WeirdTableTableManager extends RootTableManager< + _$CustomTablesDb, + WeirdTable, + WeirdData, + $WeirdTableFilterComposer, + $WeirdTableOrderingComposer, + $WeirdTableProcessedTableManager, + $WeirdTableInsertCompanionBuilder, + $WeirdTableUpdateCompanionBuilder> { + $WeirdTableTableManager(_$CustomTablesDb db, WeirdTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $WeirdTableFilterComposer(db, table), + orderingComposer: $WeirdTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $WeirdTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value sqlClass = const Value.absent(), + Value textColumn = const Value.absent(), + Value rowid = const Value.absent(), + }) => + WeirdTableCompanion( + sqlClass: sqlClass, + textColumn: textColumn, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + required int sqlClass, + required String textColumn, + Value rowid = const Value.absent(), + }) => + WeirdTableCompanion.insert( + sqlClass: sqlClass, + textColumn: textColumn, + rowid: rowid, + ))); +} + +class _$CustomTablesDbManager { + final _$CustomTablesDb _db; + _$CustomTablesDbManager(this._db); + $WithDefaultsTableManager get withDefaults => + $WithDefaultsTableManager(_db, _db.withDefaults); + $WithConstraintsTableManager get withConstraints => + $WithConstraintsTableManager(_db, _db.withConstraints); + $ConfigTableTableManager get config => + $ConfigTableTableManager(_db, _db.config); + $MytableTableManager get mytable => $MytableTableManager(_db, _db.mytable); + $EmailTableManager get email => $EmailTableManager(_db, _db.email); + $WeirdTableTableManager get weirdTable => + $WeirdTableTableManager(_db, _db.weirdTable); +} + typedef ReadMultiple$clause = OrderBy Function(ConfigTable config); typedef ReadDynamic$predicate = Expression Function(ConfigTable config); typedef TypeConverterVar$pred = Expression Function(ConfigTable config); diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 4685760b..2849a4c6 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -1864,6 +1864,7 @@ class $TodoWithCategoryViewView abstract class _$TodoDb extends GeneratedDatabase { _$TodoDb(QueryExecutor e) : super(e); + _$TodoDbManager get managers => _$TodoDbManager(this); late final $CategoriesTable categories = $CategoriesTable(this); late final $TodosTableTable todosTable = $TodosTableTable(this); late final $UsersTable users = $UsersTable(this); @@ -1962,6 +1963,570 @@ abstract class _$TodoDb extends GeneratedDatabase { ]; } +class $$CategoriesTableFilterComposer + extends FilterComposer<_$TodoDb, $CategoriesTable> { + $$CategoriesTableFilterComposer(super.db, super.table); + ColumnFilters get idValue => ColumnFilters($table.id); + ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters($table.id); + ColumnFilters get description => ColumnFilters($table.description); + ColumnFilters get priorityValue => ColumnFilters($table.priority); + ColumnWithTypeConverterFilters get priority => + ColumnWithTypeConverterFilters($table.priority); + ColumnFilters get descriptionInUpperCase => + ColumnFilters($table.descriptionInUpperCase); + ComposableFilter todosTableRefs( + ComposableFilter Function($$TodosTableTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.todosTable, + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.category, + getReferencedComposer: (db, table) => + $$TodosTableTableFilterComposer(db, table), + builder: f); + } +} + +class $$CategoriesTableOrderingComposer + extends OrderingComposer<_$TodoDb, $CategoriesTable> { + $$CategoriesTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get description => ColumnOrderings($table.description); + ColumnOrderings get priority => ColumnOrderings($table.priority); + ColumnOrderings get descriptionInUpperCase => + ColumnOrderings($table.descriptionInUpperCase); +} + +class $$CategoriesTableProcessedTableManager extends ProcessedTableManager< + _$TodoDb, + $CategoriesTable, + Category, + $$CategoriesTableFilterComposer, + $$CategoriesTableOrderingComposer, + $$CategoriesTableProcessedTableManager, + $$CategoriesTableInsertCompanionBuilder, + $$CategoriesTableUpdateCompanionBuilder> { + const $$CategoriesTableProcessedTableManager(super.$state); +} + +typedef $$CategoriesTableInsertCompanionBuilder = CategoriesCompanion Function({ + Value id, + required String description, + Value priority, +}); +typedef $$CategoriesTableUpdateCompanionBuilder = CategoriesCompanion Function({ + Value id, + Value description, + Value priority, +}); + +class $$CategoriesTableTableManager extends RootTableManager< + _$TodoDb, + $CategoriesTable, + Category, + $$CategoriesTableFilterComposer, + $$CategoriesTableOrderingComposer, + $$CategoriesTableProcessedTableManager, + $$CategoriesTableInsertCompanionBuilder, + $$CategoriesTableUpdateCompanionBuilder> { + $$CategoriesTableTableManager(_$TodoDb db, $CategoriesTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$CategoriesTableFilterComposer(db, table), + orderingComposer: $$CategoriesTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$CategoriesTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value description = const Value.absent(), + Value priority = const Value.absent(), + }) => + CategoriesCompanion( + id: id, + description: description, + priority: priority, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String description, + Value priority = const Value.absent(), + }) => + CategoriesCompanion.insert( + id: id, + description: description, + priority: priority, + ))); +} + +class $$TodosTableTableFilterComposer + extends FilterComposer<_$TodoDb, $TodosTableTable> { + $$TodosTableTableFilterComposer(super.db, super.table); + ColumnFilters get idValue => ColumnFilters($table.id); + ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters($table.id); + ColumnFilters get title => ColumnFilters($table.title); + ColumnFilters get content => ColumnFilters($table.content); + ColumnFilters get targetDate => ColumnFilters($table.targetDate); + ColumnFilters get categoryId => ColumnFilters($table.category); + ComposableFilter category( + ComposableFilter Function($$CategoriesTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.categories, + getCurrentColumn: (f) => f.category, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$CategoriesTableFilterComposer(db, table), + builder: f); + } + + ColumnFilters get statusValue => ColumnFilters($table.status); + ColumnWithTypeConverterFilters get status => + ColumnWithTypeConverterFilters($table.status); +} + +class $$TodosTableTableOrderingComposer + extends OrderingComposer<_$TodoDb, $TodosTableTable> { + $$TodosTableTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get content => ColumnOrderings($table.content); + ColumnOrderings get targetDate => ColumnOrderings($table.targetDate); + ColumnOrderings get categoryId => ColumnOrderings($table.category); + ComposableOrdering category( + ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.categories, + getCurrentColumn: (f) => f.category, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$CategoriesTableOrderingComposer(db, table), + builder: o); + } + + ColumnOrderings get status => ColumnOrderings($table.status); +} + +class $$TodosTableTableProcessedTableManager extends ProcessedTableManager< + _$TodoDb, + $TodosTableTable, + TodoEntry, + $$TodosTableTableFilterComposer, + $$TodosTableTableOrderingComposer, + $$TodosTableTableProcessedTableManager, + $$TodosTableTableInsertCompanionBuilder, + $$TodosTableTableUpdateCompanionBuilder> { + const $$TodosTableTableProcessedTableManager(super.$state); +} + +typedef $$TodosTableTableInsertCompanionBuilder = TodosTableCompanion Function({ + Value id, + Value title, + required String content, + Value targetDate, + Value category, + Value status, +}); +typedef $$TodosTableTableUpdateCompanionBuilder = TodosTableCompanion Function({ + Value id, + Value title, + Value content, + Value targetDate, + Value category, + Value status, +}); + +class $$TodosTableTableTableManager extends RootTableManager< + _$TodoDb, + $TodosTableTable, + TodoEntry, + $$TodosTableTableFilterComposer, + $$TodosTableTableOrderingComposer, + $$TodosTableTableProcessedTableManager, + $$TodosTableTableInsertCompanionBuilder, + $$TodosTableTableUpdateCompanionBuilder> { + $$TodosTableTableTableManager(_$TodoDb db, $TodosTableTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$TodosTableTableFilterComposer(db, table), + orderingComposer: $$TodosTableTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$TodosTableTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value title = const Value.absent(), + Value content = const Value.absent(), + Value targetDate = const Value.absent(), + Value category = const Value.absent(), + Value status = const Value.absent(), + }) => + TodosTableCompanion( + id: id, + title: title, + content: content, + targetDate: targetDate, + category: category, + status: status, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + Value title = const Value.absent(), + required String content, + Value targetDate = const Value.absent(), + Value category = const Value.absent(), + Value status = const Value.absent(), + }) => + TodosTableCompanion.insert( + id: id, + title: title, + content: content, + targetDate: targetDate, + category: category, + status: status, + ))); +} + +class $$UsersTableFilterComposer extends FilterComposer<_$TodoDb, $UsersTable> { + $$UsersTableFilterComposer(super.db, super.table); + ColumnFilters get idValue => ColumnFilters($table.id); + ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters($table.id); + ColumnFilters get name => ColumnFilters($table.name); + ColumnFilters get isAwesome => ColumnFilters($table.isAwesome); + ColumnFilters get profilePicture => + ColumnFilters($table.profilePicture); + ColumnFilters get creationTime => + ColumnFilters($table.creationTime); +} + +class $$UsersTableOrderingComposer + extends OrderingComposer<_$TodoDb, $UsersTable> { + $$UsersTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); + ColumnOrderings get isAwesome => ColumnOrderings($table.isAwesome); + ColumnOrderings get profilePicture => ColumnOrderings($table.profilePicture); + ColumnOrderings get creationTime => ColumnOrderings($table.creationTime); +} + +class $$UsersTableProcessedTableManager extends ProcessedTableManager< + _$TodoDb, + $UsersTable, + User, + $$UsersTableFilterComposer, + $$UsersTableOrderingComposer, + $$UsersTableProcessedTableManager, + $$UsersTableInsertCompanionBuilder, + $$UsersTableUpdateCompanionBuilder> { + const $$UsersTableProcessedTableManager(super.$state); +} + +typedef $$UsersTableInsertCompanionBuilder = UsersCompanion Function({ + Value id, + required String name, + Value isAwesome, + required Uint8List profilePicture, + Value creationTime, +}); +typedef $$UsersTableUpdateCompanionBuilder = UsersCompanion Function({ + Value id, + Value name, + Value isAwesome, + Value profilePicture, + Value creationTime, +}); + +class $$UsersTableTableManager extends RootTableManager< + _$TodoDb, + $UsersTable, + User, + $$UsersTableFilterComposer, + $$UsersTableOrderingComposer, + $$UsersTableProcessedTableManager, + $$UsersTableInsertCompanionBuilder, + $$UsersTableUpdateCompanionBuilder> { + $$UsersTableTableManager(_$TodoDb db, $UsersTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$UsersTableFilterComposer(db, table), + orderingComposer: $$UsersTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$UsersTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value isAwesome = const Value.absent(), + Value profilePicture = const Value.absent(), + Value creationTime = const Value.absent(), + }) => + UsersCompanion( + id: id, + name: name, + isAwesome: isAwesome, + profilePicture: profilePicture, + creationTime: creationTime, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String name, + Value isAwesome = const Value.absent(), + required Uint8List profilePicture, + Value creationTime = const Value.absent(), + }) => + UsersCompanion.insert( + id: id, + name: name, + isAwesome: isAwesome, + profilePicture: profilePicture, + creationTime: creationTime, + ))); +} + +class $$SharedTodosTableFilterComposer + extends FilterComposer<_$TodoDb, $SharedTodosTable> { + $$SharedTodosTableFilterComposer(super.db, super.table); + ColumnFilters get todo => ColumnFilters($table.todo); + ColumnFilters get user => ColumnFilters($table.user); +} + +class $$SharedTodosTableOrderingComposer + extends OrderingComposer<_$TodoDb, $SharedTodosTable> { + $$SharedTodosTableOrderingComposer(super.db, super.table); + ColumnOrderings get todo => ColumnOrderings($table.todo); + ColumnOrderings get user => ColumnOrderings($table.user); +} + +class $$SharedTodosTableProcessedTableManager extends ProcessedTableManager< + _$TodoDb, + $SharedTodosTable, + SharedTodo, + $$SharedTodosTableFilterComposer, + $$SharedTodosTableOrderingComposer, + $$SharedTodosTableProcessedTableManager, + $$SharedTodosTableInsertCompanionBuilder, + $$SharedTodosTableUpdateCompanionBuilder> { + const $$SharedTodosTableProcessedTableManager(super.$state); +} + +typedef $$SharedTodosTableInsertCompanionBuilder = SharedTodosCompanion + Function({ + required int todo, + required int user, + Value rowid, +}); +typedef $$SharedTodosTableUpdateCompanionBuilder = SharedTodosCompanion + Function({ + Value todo, + Value user, + Value rowid, +}); + +class $$SharedTodosTableTableManager extends RootTableManager< + _$TodoDb, + $SharedTodosTable, + SharedTodo, + $$SharedTodosTableFilterComposer, + $$SharedTodosTableOrderingComposer, + $$SharedTodosTableProcessedTableManager, + $$SharedTodosTableInsertCompanionBuilder, + $$SharedTodosTableUpdateCompanionBuilder> { + $$SharedTodosTableTableManager(_$TodoDb db, $SharedTodosTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$SharedTodosTableFilterComposer(db, table), + orderingComposer: $$SharedTodosTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$SharedTodosTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value todo = const Value.absent(), + Value user = const Value.absent(), + Value rowid = const Value.absent(), + }) => + SharedTodosCompanion( + todo: todo, + user: user, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + required int todo, + required int user, + Value rowid = const Value.absent(), + }) => + SharedTodosCompanion.insert( + todo: todo, + user: user, + rowid: rowid, + ))); +} + +class $$PureDefaultsTableFilterComposer + extends FilterComposer<_$TodoDb, $PureDefaultsTable> { + $$PureDefaultsTableFilterComposer(super.db, super.table); + ColumnFilters get txtValue => ColumnFilters($table.txt); + ColumnWithTypeConverterFilters get txt => + ColumnWithTypeConverterFilters($table.txt); +} + +class $$PureDefaultsTableOrderingComposer + extends OrderingComposer<_$TodoDb, $PureDefaultsTable> { + $$PureDefaultsTableOrderingComposer(super.db, super.table); + ColumnOrderings get txt => ColumnOrderings($table.txt); +} + +class $$PureDefaultsTableProcessedTableManager extends ProcessedTableManager< + _$TodoDb, + $PureDefaultsTable, + PureDefault, + $$PureDefaultsTableFilterComposer, + $$PureDefaultsTableOrderingComposer, + $$PureDefaultsTableProcessedTableManager, + $$PureDefaultsTableInsertCompanionBuilder, + $$PureDefaultsTableUpdateCompanionBuilder> { + const $$PureDefaultsTableProcessedTableManager(super.$state); +} + +typedef $$PureDefaultsTableInsertCompanionBuilder = PureDefaultsCompanion + Function({ + Value txt, + Value rowid, +}); +typedef $$PureDefaultsTableUpdateCompanionBuilder = PureDefaultsCompanion + Function({ + Value txt, + Value rowid, +}); + +class $$PureDefaultsTableTableManager extends RootTableManager< + _$TodoDb, + $PureDefaultsTable, + PureDefault, + $$PureDefaultsTableFilterComposer, + $$PureDefaultsTableOrderingComposer, + $$PureDefaultsTableProcessedTableManager, + $$PureDefaultsTableInsertCompanionBuilder, + $$PureDefaultsTableUpdateCompanionBuilder> { + $$PureDefaultsTableTableManager(_$TodoDb db, $PureDefaultsTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$PureDefaultsTableFilterComposer(db, table), + orderingComposer: $$PureDefaultsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$PureDefaultsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value txt = const Value.absent(), + Value rowid = const Value.absent(), + }) => + PureDefaultsCompanion( + txt: txt, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + Value txt = const Value.absent(), + Value rowid = const Value.absent(), + }) => + PureDefaultsCompanion.insert( + txt: txt, + rowid: rowid, + ))); +} + +class $$WithCustomTypeTableFilterComposer + extends FilterComposer<_$TodoDb, $WithCustomTypeTable> { + $$WithCustomTypeTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); +} + +class $$WithCustomTypeTableOrderingComposer + extends OrderingComposer<_$TodoDb, $WithCustomTypeTable> { + $$WithCustomTypeTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); +} + +class $$WithCustomTypeTableProcessedTableManager extends ProcessedTableManager< + _$TodoDb, + $WithCustomTypeTable, + WithCustomTypeData, + $$WithCustomTypeTableFilterComposer, + $$WithCustomTypeTableOrderingComposer, + $$WithCustomTypeTableProcessedTableManager, + $$WithCustomTypeTableInsertCompanionBuilder, + $$WithCustomTypeTableUpdateCompanionBuilder> { + const $$WithCustomTypeTableProcessedTableManager(super.$state); +} + +typedef $$WithCustomTypeTableInsertCompanionBuilder = WithCustomTypeCompanion + Function({ + required UuidValue id, + Value rowid, +}); +typedef $$WithCustomTypeTableUpdateCompanionBuilder = WithCustomTypeCompanion + Function({ + Value id, + Value rowid, +}); + +class $$WithCustomTypeTableTableManager extends RootTableManager< + _$TodoDb, + $WithCustomTypeTable, + WithCustomTypeData, + $$WithCustomTypeTableFilterComposer, + $$WithCustomTypeTableOrderingComposer, + $$WithCustomTypeTableProcessedTableManager, + $$WithCustomTypeTableInsertCompanionBuilder, + $$WithCustomTypeTableUpdateCompanionBuilder> { + $$WithCustomTypeTableTableManager(_$TodoDb db, $WithCustomTypeTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$WithCustomTypeTableFilterComposer(db, table), + orderingComposer: $$WithCustomTypeTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$WithCustomTypeTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value rowid = const Value.absent(), + }) => + WithCustomTypeCompanion( + id: id, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + required UuidValue id, + Value rowid = const Value.absent(), + }) => + WithCustomTypeCompanion.insert( + id: id, + rowid: rowid, + ))); +} + +class _$TodoDbManager { + final _$TodoDb _db; + _$TodoDbManager(this._db); + $$CategoriesTableTableManager get categories => + $$CategoriesTableTableManager(_db, _db.categories); + $$TodosTableTableTableManager get todosTable => + $$TodosTableTableTableManager(_db, _db.todosTable); + $$UsersTableTableManager get users => + $$UsersTableTableManager(_db, _db.users); + $$SharedTodosTableTableManager get sharedTodos => + $$SharedTodosTableTableManager(_db, _db.sharedTodos); + $$PureDefaultsTableTableManager get pureDefaults => + $$PureDefaultsTableTableManager(_db, _db.pureDefaults); + $$WithCustomTypeTableTableManager get withCustomType => + $$WithCustomTypeTableTableManager(_db, _db.withCustomType); +} + class AllTodosWithCategoryResult extends CustomResultSet { final RowId id; final String? title; diff --git a/drift/test/integration_tests/regress_2166_test.g.dart b/drift/test/integration_tests/regress_2166_test.g.dart index 6f16c66d..516af6da 100644 --- a/drift/test/integration_tests/regress_2166_test.g.dart +++ b/drift/test/integration_tests/regress_2166_test.g.dart @@ -184,6 +184,7 @@ class _SomeTableCompanion extends UpdateCompanion<_SomeTableData> { abstract class _$_SomeDb extends GeneratedDatabase { _$_SomeDb(QueryExecutor e) : super(e); + _$_SomeDbManager get managers => _$_SomeDbManager(this); late final $_SomeTableTable someTable = $_SomeTableTable(this); @override Iterable> get allTables => @@ -191,3 +192,80 @@ abstract class _$_SomeDb extends GeneratedDatabase { @override List get allSchemaEntities => [someTable]; } + +class $$_SomeTableTableFilterComposer + extends FilterComposer<_$_SomeDb, $_SomeTableTable> { + $$_SomeTableTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get name => ColumnFilters($table.name); +} + +class $$_SomeTableTableOrderingComposer + extends OrderingComposer<_$_SomeDb, $_SomeTableTable> { + $$_SomeTableTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); +} + +class $$_SomeTableTableProcessedTableManager extends ProcessedTableManager< + _$_SomeDb, + $_SomeTableTable, + _SomeTableData, + $$_SomeTableTableFilterComposer, + $$_SomeTableTableOrderingComposer, + $$_SomeTableTableProcessedTableManager, + $$_SomeTableTableInsertCompanionBuilder, + $$_SomeTableTableUpdateCompanionBuilder> { + const $$_SomeTableTableProcessedTableManager(super.$state); +} + +typedef $$_SomeTableTableInsertCompanionBuilder = _SomeTableCompanion Function({ + Value id, + Value name, +}); +typedef $$_SomeTableTableUpdateCompanionBuilder = _SomeTableCompanion Function({ + Value id, + Value name, +}); + +class $$_SomeTableTableTableManager extends RootTableManager< + _$_SomeDb, + $_SomeTableTable, + _SomeTableData, + $$_SomeTableTableFilterComposer, + $$_SomeTableTableOrderingComposer, + $$_SomeTableTableProcessedTableManager, + $$_SomeTableTableInsertCompanionBuilder, + $$_SomeTableTableUpdateCompanionBuilder> { + $$_SomeTableTableTableManager(_$_SomeDb db, $_SomeTableTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$_SomeTableTableFilterComposer(db, table), + orderingComposer: $$_SomeTableTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$_SomeTableTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + }) => + _SomeTableCompanion( + id: id, + name: name, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + }) => + _SomeTableCompanion.insert( + id: id, + name: name, + ))); +} + +class _$_SomeDbManager { + final _$_SomeDb _db; + _$_SomeDbManager(this._db); + $$_SomeTableTableTableManager get someTable => + $$_SomeTableTableTableManager(_db, _db.someTable); +} diff --git a/drift/test/test_utils/test_utils.mocks.dart b/drift/test/test_utils/test_utils.mocks.dart index 03d207d3..0d052c05 100644 --- a/drift/test/test_utils/test_utils.mocks.dart +++ b/drift/test/test_utils/test_utils.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.3 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in drift/test/test_utils/test_utils.dart. // Do not manually edit this file. diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index fb689fb4..0eae5d0c 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -1,6 +1,7 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/modules.dart'; +import 'package:drift_dev/src/writer/tables/update_companion_writer.dart'; import 'package:drift_dev/src/writer/writer.dart'; abstract class _FilterWriter { @@ -330,6 +331,7 @@ class _TableWriter { /// Use [isUpdate] to determine if the builder is for an update or insert companion (String, String) _companionBuilder(String typedefName, {required bool isUpdate}) { + ; final companionClassName = scope.dartCode(scope.companionType(table)); final companionBuilderTypeDef = @@ -344,7 +346,7 @@ class _TableWriter { companionBuilderBody = StringBuffer('=> $companionClassName.insert('); } - for (final column in table.columns) { + for (final column in UpdateCompanionWriter(table, scope).columns) { final value = scope.drift('Value'); final param = column.nameInDart; final typeName = scope.dartCode(scope.dartType(column)); @@ -595,6 +597,9 @@ class ManagerWriter { void write() { final leaf = _scope.leaf(); + // Remove tables that use custom row classes + _addedTables.removeWhere((t) => t.existingRowClass != null); + // Write the manager class for each table final tableWriters = <_TableWriter>[]; for (var table in _addedTables) { diff --git a/drift_dev/lib/src/writer/tables/table_writer.dart b/drift_dev/lib/src/writer/tables/table_writer.dart index 2ea4b6c4..a1a213fb 100644 --- a/drift_dev/lib/src/writer/tables/table_writer.dart +++ b/drift_dev/lib/src/writer/tables/table_writer.dart @@ -45,7 +45,6 @@ abstract class TableOrViewWriter { final typeName = emitter.dartCode(emitter.writer.converterType(converter)); final code = emitter.dartCode(converter.expression); - buffer.write('static $typeName ${converter.fieldName} = $code;'); // Generate wrappers for non-nullable type converters that are applied to From c79316d80384369947df6f00d631772247283c02 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 7 Apr 2024 07:22:31 -0400 Subject: [PATCH 48/74] lint --- drift_dev/lib/src/writer/manager_writer.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 0eae5d0c..9cefcd44 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -331,7 +331,6 @@ class _TableWriter { /// Use [isUpdate] to determine if the builder is for an update or insert companion (String, String) _companionBuilder(String typedefName, {required bool isUpdate}) { - ; final companionClassName = scope.dartCode(scope.companionType(table)); final companionBuilderTypeDef = From 1b2e8b7c754b9bc6c8967286a89725b591a2c6b2 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 7 Apr 2024 08:38:10 -0400 Subject: [PATCH 49/74] add warning on clash --- drift_dev/lib/src/writer/manager_writer.dart | 60 ++++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 9cefcd44..8c3ae219 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -197,7 +197,7 @@ class _ReferencedOrderingWriter extends _OrderingWriter { } } -class _ColumnWriter { +class _ColumnManagerWriter { /// The getter for the field /// /// E.G `id` in `table.id` @@ -210,12 +210,12 @@ class _ColumnWriter { final List<_OrderingWriter> orderings; /// A class used for writing filters and orderings for columns - _ColumnWriter(this.fieldGetter) + _ColumnManagerWriter(this.fieldGetter) : filters = [], orderings = []; } -class _TableWriter { +class _TableManagerWriter { /// The current table final DriftTable table; @@ -268,18 +268,23 @@ class _TableWriter { /// E.G. `i5.$Category` String get rowClassName => dbScope.dartCode(dbScope.writer.rowType(table)); + /// Whether this table has a custom row class + /// We use this row to determine if we should generate a manager for this table + bool get hasCustomRowClass => table.existingRowClass != null; + /// The name of the database class /// /// E.G. `i5.$GeneratedDatabase` final String databaseGenericName; /// Writers for the columns of this table - final List<_ColumnWriter> columns; + final List<_ColumnManagerWriter> columns; /// Filters for back references final List<_ReferencedFilterWriter> backRefFilters; - _TableWriter(this.table, this.scope, this.dbScope, this.databaseGenericName) + _TableManagerWriter( + this.table, this.scope, this.dbScope, this.databaseGenericName) : backRefFilters = [], columns = []; @@ -455,7 +460,7 @@ class _TableWriter { /// First add the filters and orderings for the columns /// of the current table for (var column in table.columns) { - final c = _ColumnWriter(column.nameInDart); + final c = _ColumnManagerWriter(column.nameInDart); // The type that this column is (int, string, etc) final innerColumnType = @@ -491,9 +496,10 @@ class _TableWriter { /// for the referenced table if (referenced != null) { final (referencedTable, referencedCol) = referenced; - final referencedTableNames = - _TableWriter(referencedTable, scope, dbScope, databaseGenericName); - final referencedColumnNames = _ColumnWriter(referencedCol.nameInDart); + final referencedTableNames = _TableManagerWriter( + referencedTable, scope, dbScope, databaseGenericName); + final referencedColumnNames = + _ColumnManagerWriter(referencedCol.nameInDart); final referencedTableField = _referenceTable(referencedTable); c.filters.add(_ReferencedFilterWriter(c.fieldGetter, @@ -518,8 +524,8 @@ class _TableWriter { if (reference != null && reference.$1.entityInfoName == table.entityInfoName) { final referencedTableNames = - _TableWriter(ot, scope, dbScope, databaseGenericName); - final referencedColumnNames = _ColumnWriter(oc.nameInDart); + _TableManagerWriter(ot, scope, dbScope, databaseGenericName); + final referencedColumnNames = _ColumnManagerWriter(oc.nameInDart); final referencedTableField = _referenceTable(ot); final filterName = oc.referenceName ?? @@ -535,7 +541,6 @@ class _TableWriter { } // Remove the filters and orderings that have duplicates - // TODO: Add warnings for duplicate filters and orderings final duplicatedFilterNames = duplicates(columns .map((e) => e.filters.map((e) => e.filterName)) .expand((e) => e) @@ -545,15 +550,20 @@ class _TableWriter { .map((e) => e.orderings.map((e) => e.orderingName)) .expand((e) => e) .toList()); - // Remove the duplicates - for (var c in columns) { - c.filters + if (duplicatedFilterNames.isNotEmpty || + duplicatedOrderingNames.isNotEmpty) { + print( + "The code generator encountered an issue while attempting to create filters/orderings for $tableClassName manager. The following filters/orderings were not created ${(duplicatedFilterNames + duplicatedOrderingNames).toSet()}. Use the @ReferenceName() annotation to resolve this issue."); + // Remove the duplicates + for (var c in columns) { + c.filters + .removeWhere((e) => duplicatedFilterNames.contains(e.filterName)); + c.orderings.removeWhere( + (e) => duplicatedOrderingNames.contains(e.orderingName)); + } + backRefFilters .removeWhere((e) => duplicatedFilterNames.contains(e.filterName)); - c.orderings - .removeWhere((e) => duplicatedOrderingNames.contains(e.orderingName)); } - backRefFilters - .removeWhere((e) => duplicatedFilterNames.contains(e.filterName)); } } @@ -596,17 +606,17 @@ class ManagerWriter { void write() { final leaf = _scope.leaf(); - // Remove tables that use custom row classes - _addedTables.removeWhere((t) => t.existingRowClass != null); - - // Write the manager class for each table - final tableWriters = <_TableWriter>[]; + // create the manager class for each table + final tableWriters = <_TableManagerWriter>[]; for (var table in _addedTables) { tableWriters.add( - _TableWriter(table, _scope, _dbScope, databaseGenericName) + _TableManagerWriter(table, _scope, _dbScope, databaseGenericName) ..addFiltersAndOrderings(_addedTables)); } + // Remove ones that have custom row classes + tableWriters.removeWhere((t) => !t.hasCustomRowClass); + // Write each tables manager to the leaf and append the getter to the main manager final tableManagerGetters = StringBuffer(); for (var table in tableWriters) { From 6a693a9016080eb0e95c21e26e27366be43c6ded Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Thu, 11 Apr 2024 11:15:13 -0400 Subject: [PATCH 50/74] rebuild, format, and dart fix --- .../drift_files/custom_queries.drift.dart | 222 ------------ .../modular/many_to_many/json.drift.dart | 169 --------- .../many_to_many/relational.drift.dart | 328 +----------------- drift/example/main.g.dart | 209 +++++++++++ drift/pubspec.yaml | 3 + drift/test/manager/filter_composer_test.dart | 54 +++ drift_dev/lib/src/writer/manager_writer.dart | 2 +- examples/manager/lib/pages/product.dart | 3 +- examples/manager/pubspec.yaml | 6 + .../migrations_example/lib/database.g.dart | 327 ----------------- extras/benchmarks/bin/benchmarks.dart | 2 +- .../src/moor/cache_prepared_statements.dart | 2 +- 12 files changed, 280 insertions(+), 1047 deletions(-) create mode 100644 drift/test/manager/filter_composer_test.dart diff --git a/docs/lib/snippets/drift_files/custom_queries.drift.dart b/docs/lib/snippets/drift_files/custom_queries.drift.dart index 2bb8229a..6bcbe9e7 100644 --- a/docs/lib/snippets/drift_files/custom_queries.drift.dart +++ b/docs/lib/snippets/drift_files/custom_queries.drift.dart @@ -1,11 +1,9 @@ // ignore_for_file: type=lint import 'package:drift/drift.dart' as i0; import 'package:drift_docs/snippets/_shared/todo_tables.drift.dart' as i1; -import 'package:drift/internal/modular.dart' as i2; abstract class $MyDatabase extends i0.GeneratedDatabase { $MyDatabase(i0.QueryExecutor e) : super(e); - $MyDatabaseManager get managers => $MyDatabaseManager(this); late final i1.$CategoriesTable categories = i1.$CategoriesTable(this); late final i1.$TodoItemsTable todoItems = i1.$TodoItemsTable(this); i0.Selectable categoriesWithCount() { @@ -30,226 +28,6 @@ abstract class $MyDatabase extends i0.GeneratedDatabase { [categories, todoItems]; } -class $$CategoriesTableFilterComposer - extends i0.FilterComposer { - $$CategoriesTableFilterComposer(super.db, super.table); - i0.ColumnFilters get id => i0.ColumnFilters($table.id); - i0.ColumnFilters get name => i0.ColumnFilters($table.name); - i0.ComposableFilter todoItemsRefs( - i0.ComposableFilter Function($$TodoItemsTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i2.ReadDatabaseContainer($db) - .resultSet('todo_items'), - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.category, - getReferencedComposer: (db, table) => - $$TodoItemsTableFilterComposer(db, table), - builder: f); - } -} - -class $$CategoriesTableOrderingComposer - extends i0.OrderingComposer { - $$CategoriesTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get name => i0.ColumnOrderings($table.name); -} - -class $$CategoriesTableProcessedTableManager extends i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$CategoriesTable, - i1.Category, - $$CategoriesTableFilterComposer, - $$CategoriesTableOrderingComposer, - $$CategoriesTableProcessedTableManager, - $$CategoriesTableInsertCompanionBuilder, - $$CategoriesTableUpdateCompanionBuilder> { - const $$CategoriesTableProcessedTableManager(super.$state); -} - -typedef $$CategoriesTableInsertCompanionBuilder = i1.CategoriesCompanion - Function({ - i0.Value id, - required String name, -}); -typedef $$CategoriesTableUpdateCompanionBuilder = i1.CategoriesCompanion - Function({ - i0.Value id, - i0.Value name, -}); - -class $$CategoriesTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$CategoriesTable, - i1.Category, - $$CategoriesTableFilterComposer, - $$CategoriesTableOrderingComposer, - $$CategoriesTableProcessedTableManager, - $$CategoriesTableInsertCompanionBuilder, - $$CategoriesTableUpdateCompanionBuilder> { - $$CategoriesTableTableManager( - i0.GeneratedDatabase db, i1.$CategoriesTable table) - : super(i0.TableManagerState( - db: db, - table: table, - filteringComposer: $$CategoriesTableFilterComposer(db, table), - orderingComposer: $$CategoriesTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$CategoriesTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - }) => - i1.CategoriesCompanion( - id: id, - name: name, - ), - getInsertCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - required String name, - }) => - i1.CategoriesCompanion.insert( - id: id, - name: name, - ))); -} - -class $$TodoItemsTableFilterComposer - extends i0.FilterComposer { - $$TodoItemsTableFilterComposer(super.db, super.table); - i0.ColumnFilters get id => i0.ColumnFilters($table.id); - i0.ColumnFilters get title => i0.ColumnFilters($table.title); - i0.ColumnFilters get content => i0.ColumnFilters($table.content); - i0.ColumnFilters get categoryId => i0.ColumnFilters($table.category); - i0.ComposableFilter category( - i0.ComposableFilter Function($$CategoriesTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i2.ReadDatabaseContainer($db) - .resultSet('categories'), - getCurrentColumn: (f) => f.category, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$CategoriesTableFilterComposer(db, table), - builder: f); - } - - i0.ColumnFilters get dueDate => i0.ColumnFilters($table.dueDate); -} - -class $$TodoItemsTableOrderingComposer - extends i0.OrderingComposer { - $$TodoItemsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get title => i0.ColumnOrderings($table.title); - i0.ColumnOrderings get content => i0.ColumnOrderings($table.content); - i0.ColumnOrderings get categoryId => i0.ColumnOrderings($table.category); - i0.ComposableOrdering category( - i0.ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i2.ReadDatabaseContainer($db) - .resultSet('categories'), - getCurrentColumn: (f) => f.category, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$CategoriesTableOrderingComposer(db, table), - builder: o); - } - - i0.ColumnOrderings get dueDate => i0.ColumnOrderings($table.dueDate); -} - -class $$TodoItemsTableProcessedTableManager extends i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$TodoItemsTable, - i1.TodoItem, - $$TodoItemsTableFilterComposer, - $$TodoItemsTableOrderingComposer, - $$TodoItemsTableProcessedTableManager, - $$TodoItemsTableInsertCompanionBuilder, - $$TodoItemsTableUpdateCompanionBuilder> { - const $$TodoItemsTableProcessedTableManager(super.$state); -} - -typedef $$TodoItemsTableInsertCompanionBuilder = i1.TodoItemsCompanion - Function({ - i0.Value id, - required String title, - required String content, - i0.Value category, - i0.Value dueDate, -}); -typedef $$TodoItemsTableUpdateCompanionBuilder = i1.TodoItemsCompanion - Function({ - i0.Value id, - i0.Value title, - i0.Value content, - i0.Value category, - i0.Value dueDate, -}); - -class $$TodoItemsTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$TodoItemsTable, - i1.TodoItem, - $$TodoItemsTableFilterComposer, - $$TodoItemsTableOrderingComposer, - $$TodoItemsTableProcessedTableManager, - $$TodoItemsTableInsertCompanionBuilder, - $$TodoItemsTableUpdateCompanionBuilder> { - $$TodoItemsTableTableManager( - i0.GeneratedDatabase db, i1.$TodoItemsTable table) - : super(i0.TableManagerState( - db: db, - table: table, - filteringComposer: $$TodoItemsTableFilterComposer(db, table), - orderingComposer: $$TodoItemsTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$TodoItemsTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - i0.Value title = const i0.Value.absent(), - i0.Value content = const i0.Value.absent(), - i0.Value category = const i0.Value.absent(), - i0.Value dueDate = const i0.Value.absent(), - }) => - i1.TodoItemsCompanion( - id: id, - title: title, - content: content, - category: category, - dueDate: dueDate, - ), - getInsertCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - required String title, - required String content, - i0.Value category = const i0.Value.absent(), - i0.Value dueDate = const i0.Value.absent(), - }) => - i1.TodoItemsCompanion.insert( - id: id, - title: title, - content: content, - category: category, - dueDate: dueDate, - ))); -} - -class $MyDatabaseManager { - final $MyDatabase _db; - $MyDatabaseManager(this._db); - $$CategoriesTableTableManager get categories => - $$CategoriesTableTableManager(_db, _db.categories); - $$TodoItemsTableTableManager get todoItems => - $$TodoItemsTableTableManager(_db, _db.todoItems); -} - class CategoriesWithCountResult { final int id; final String name; diff --git a/docs/lib/snippets/modular/many_to_many/json.drift.dart b/docs/lib/snippets/modular/many_to_many/json.drift.dart index 5ffc5cff..a7bdd538 100644 --- a/docs/lib/snippets/modular/many_to_many/json.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/json.drift.dart @@ -7,7 +7,6 @@ import 'package:drift_docs/snippets/modular/many_to_many/json.dart' as i3; abstract class $JsonBasedDatabase extends i0.GeneratedDatabase { $JsonBasedDatabase(i0.QueryExecutor e) : super(e); - $JsonBasedDatabaseManager get managers => $JsonBasedDatabaseManager(this); late final i1.$BuyableItemsTable buyableItems = i1.$BuyableItemsTable(this); late final i2.$ShoppingCartsTable shoppingCarts = i2.$ShoppingCartsTable(this); @@ -19,174 +18,6 @@ abstract class $JsonBasedDatabase extends i0.GeneratedDatabase { [buyableItems, shoppingCarts]; } -class $$BuyableItemsTableFilterComposer - extends i0.FilterComposer { - $$BuyableItemsTableFilterComposer(super.db, super.table); - i0.ColumnFilters get id => i0.ColumnFilters($table.id); - i0.ColumnFilters get description => - i0.ColumnFilters($table.description); - i0.ColumnFilters get price => i0.ColumnFilters($table.price); -} - -class $$BuyableItemsTableOrderingComposer - extends i0.OrderingComposer { - $$BuyableItemsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); - i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); -} - -class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$BuyableItemsTable, - i1.BuyableItem, - $$BuyableItemsTableFilterComposer, - $$BuyableItemsTableOrderingComposer, - $$BuyableItemsTableProcessedTableManager, - $$BuyableItemsTableInsertCompanionBuilder, - $$BuyableItemsTableUpdateCompanionBuilder> { - const $$BuyableItemsTableProcessedTableManager(super.$state); -} - -typedef $$BuyableItemsTableInsertCompanionBuilder = i1.BuyableItemsCompanion - Function({ - i0.Value id, - required String description, - required int price, -}); -typedef $$BuyableItemsTableUpdateCompanionBuilder = i1.BuyableItemsCompanion - Function({ - i0.Value id, - i0.Value description, - i0.Value price, -}); - -class $$BuyableItemsTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$BuyableItemsTable, - i1.BuyableItem, - $$BuyableItemsTableFilterComposer, - $$BuyableItemsTableOrderingComposer, - $$BuyableItemsTableProcessedTableManager, - $$BuyableItemsTableInsertCompanionBuilder, - $$BuyableItemsTableUpdateCompanionBuilder> { - $$BuyableItemsTableTableManager( - i0.GeneratedDatabase db, i1.$BuyableItemsTable table) - : super(i0.TableManagerState( - db: db, - table: table, - filteringComposer: $$BuyableItemsTableFilterComposer(db, table), - orderingComposer: $$BuyableItemsTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$BuyableItemsTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value price = const i0.Value.absent(), - }) => - i1.BuyableItemsCompanion( - id: id, - description: description, - price: price, - ), - getInsertCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - required String description, - required int price, - }) => - i1.BuyableItemsCompanion.insert( - id: id, - description: description, - price: price, - ))); -} - -class $$ShoppingCartsTableFilterComposer - extends i0.FilterComposer { - $$ShoppingCartsTableFilterComposer(super.db, super.table); - i0.ColumnFilters get id => i0.ColumnFilters($table.id); - i0.ColumnFilters get entriesValue => i0.ColumnFilters($table.entries); - i0.ColumnWithTypeConverterFilters - get entries => i0.ColumnWithTypeConverterFilters($table.entries); -} - -class $$ShoppingCartsTableOrderingComposer - extends i0.OrderingComposer { - $$ShoppingCartsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get entries => i0.ColumnOrderings($table.entries); -} - -class $$ShoppingCartsTableProcessedTableManager - extends i0.ProcessedTableManager< - i0.GeneratedDatabase, - i2.$ShoppingCartsTable, - i2.ShoppingCart, - $$ShoppingCartsTableFilterComposer, - $$ShoppingCartsTableOrderingComposer, - $$ShoppingCartsTableProcessedTableManager, - $$ShoppingCartsTableInsertCompanionBuilder, - $$ShoppingCartsTableUpdateCompanionBuilder> { - const $$ShoppingCartsTableProcessedTableManager(super.$state); -} - -typedef $$ShoppingCartsTableInsertCompanionBuilder = i2.ShoppingCartsCompanion - Function({ - i0.Value id, - required i3.ShoppingCartEntries entries, -}); -typedef $$ShoppingCartsTableUpdateCompanionBuilder = i2.ShoppingCartsCompanion - Function({ - i0.Value id, - i0.Value entries, -}); - -class $$ShoppingCartsTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i2.$ShoppingCartsTable, - i2.ShoppingCart, - $$ShoppingCartsTableFilterComposer, - $$ShoppingCartsTableOrderingComposer, - $$ShoppingCartsTableProcessedTableManager, - $$ShoppingCartsTableInsertCompanionBuilder, - $$ShoppingCartsTableUpdateCompanionBuilder> { - $$ShoppingCartsTableTableManager( - i0.GeneratedDatabase db, i2.$ShoppingCartsTable table) - : super(i0.TableManagerState( - db: db, - table: table, - filteringComposer: $$ShoppingCartsTableFilterComposer(db, table), - orderingComposer: $$ShoppingCartsTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$ShoppingCartsTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - i0.Value entries = - const i0.Value.absent(), - }) => - i2.ShoppingCartsCompanion( - id: id, - entries: entries, - ), - getInsertCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - required i3.ShoppingCartEntries entries, - }) => - i2.ShoppingCartsCompanion.insert( - id: id, - entries: entries, - ))); -} - -class $JsonBasedDatabaseManager { - final $JsonBasedDatabase _db; - $JsonBasedDatabaseManager(this._db); - $$BuyableItemsTableTableManager get buyableItems => - $$BuyableItemsTableTableManager(_db, _db.buyableItems); - $$ShoppingCartsTableTableManager get shoppingCarts => - $$ShoppingCartsTableTableManager(_db, _db.shoppingCarts); -} - class $ShoppingCartsTable extends i3.ShoppingCarts with i0.TableInfo<$ShoppingCartsTable, i2.ShoppingCart> { @override diff --git a/docs/lib/snippets/modular/many_to_many/relational.drift.dart b/docs/lib/snippets/modular/many_to_many/relational.drift.dart index 7be0062c..1d1bab75 100644 --- a/docs/lib/snippets/modular/many_to_many/relational.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/relational.drift.dart @@ -4,12 +4,10 @@ import 'package:drift_docs/snippets/modular/many_to_many/shared.drift.dart' as i1; import 'package:drift_docs/snippets/modular/many_to_many/relational.drift.dart' as i2; -import 'package:drift/internal/modular.dart' as i3; -import 'package:drift_docs/snippets/modular/many_to_many/relational.dart' as i4; +import 'package:drift_docs/snippets/modular/many_to_many/relational.dart' as i3; abstract class $RelationalDatabase extends i0.GeneratedDatabase { $RelationalDatabase(i0.QueryExecutor e) : super(e); - $RelationalDatabaseManager get managers => $RelationalDatabaseManager(this); late final i1.$BuyableItemsTable buyableItems = i1.$BuyableItemsTable(this); late final i2.$ShoppingCartsTable shoppingCarts = i2.$ShoppingCartsTable(this); @@ -23,327 +21,7 @@ abstract class $RelationalDatabase extends i0.GeneratedDatabase { [buyableItems, shoppingCarts, shoppingCartEntries]; } -class $$BuyableItemsTableFilterComposer - extends i0.FilterComposer { - $$BuyableItemsTableFilterComposer(super.db, super.table); - i0.ColumnFilters get id => i0.ColumnFilters($table.id); - i0.ColumnFilters get description => - i0.ColumnFilters($table.description); - i0.ColumnFilters get price => i0.ColumnFilters($table.price); - i0.ComposableFilter shoppingCartEntriesRefs( - i0.ComposableFilter Function($$ShoppingCartEntriesTableFilterComposer f) - f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i3.ReadDatabaseContainer($db) - .resultSet('shopping_cart_entries'), - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.item, - getReferencedComposer: (db, table) => - $$ShoppingCartEntriesTableFilterComposer(db, table), - builder: f); - } -} - -class $$BuyableItemsTableOrderingComposer - extends i0.OrderingComposer { - $$BuyableItemsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); - i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); -} - -class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$BuyableItemsTable, - i1.BuyableItem, - $$BuyableItemsTableFilterComposer, - $$BuyableItemsTableOrderingComposer, - $$BuyableItemsTableProcessedTableManager, - $$BuyableItemsTableInsertCompanionBuilder, - $$BuyableItemsTableUpdateCompanionBuilder> { - const $$BuyableItemsTableProcessedTableManager(super.$state); -} - -typedef $$BuyableItemsTableInsertCompanionBuilder = i1.BuyableItemsCompanion - Function({ - i0.Value id, - required String description, - required int price, -}); -typedef $$BuyableItemsTableUpdateCompanionBuilder = i1.BuyableItemsCompanion - Function({ - i0.Value id, - i0.Value description, - i0.Value price, -}); - -class $$BuyableItemsTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$BuyableItemsTable, - i1.BuyableItem, - $$BuyableItemsTableFilterComposer, - $$BuyableItemsTableOrderingComposer, - $$BuyableItemsTableProcessedTableManager, - $$BuyableItemsTableInsertCompanionBuilder, - $$BuyableItemsTableUpdateCompanionBuilder> { - $$BuyableItemsTableTableManager( - i0.GeneratedDatabase db, i1.$BuyableItemsTable table) - : super(i0.TableManagerState( - db: db, - table: table, - filteringComposer: $$BuyableItemsTableFilterComposer(db, table), - orderingComposer: $$BuyableItemsTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$BuyableItemsTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value price = const i0.Value.absent(), - }) => - i1.BuyableItemsCompanion( - id: id, - description: description, - price: price, - ), - getInsertCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - required String description, - required int price, - }) => - i1.BuyableItemsCompanion.insert( - id: id, - description: description, - price: price, - ))); -} - -class $$ShoppingCartsTableFilterComposer - extends i0.FilterComposer { - $$ShoppingCartsTableFilterComposer(super.db, super.table); - i0.ColumnFilters get id => i0.ColumnFilters($table.id); - i0.ComposableFilter shoppingCartEntriesRefs( - i0.ComposableFilter Function($$ShoppingCartEntriesTableFilterComposer f) - f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i3.ReadDatabaseContainer($db) - .resultSet('shopping_cart_entries'), - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.shoppingCart, - getReferencedComposer: (db, table) => - $$ShoppingCartEntriesTableFilterComposer(db, table), - builder: f); - } -} - -class $$ShoppingCartsTableOrderingComposer - extends i0.OrderingComposer { - $$ShoppingCartsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); -} - -class $$ShoppingCartsTableProcessedTableManager - extends i0.ProcessedTableManager< - i0.GeneratedDatabase, - i2.$ShoppingCartsTable, - i2.ShoppingCart, - $$ShoppingCartsTableFilterComposer, - $$ShoppingCartsTableOrderingComposer, - $$ShoppingCartsTableProcessedTableManager, - $$ShoppingCartsTableInsertCompanionBuilder, - $$ShoppingCartsTableUpdateCompanionBuilder> { - const $$ShoppingCartsTableProcessedTableManager(super.$state); -} - -typedef $$ShoppingCartsTableInsertCompanionBuilder = i2.ShoppingCartsCompanion - Function({ - i0.Value id, -}); -typedef $$ShoppingCartsTableUpdateCompanionBuilder = i2.ShoppingCartsCompanion - Function({ - i0.Value id, -}); - -class $$ShoppingCartsTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i2.$ShoppingCartsTable, - i2.ShoppingCart, - $$ShoppingCartsTableFilterComposer, - $$ShoppingCartsTableOrderingComposer, - $$ShoppingCartsTableProcessedTableManager, - $$ShoppingCartsTableInsertCompanionBuilder, - $$ShoppingCartsTableUpdateCompanionBuilder> { - $$ShoppingCartsTableTableManager( - i0.GeneratedDatabase db, i2.$ShoppingCartsTable table) - : super(i0.TableManagerState( - db: db, - table: table, - filteringComposer: $$ShoppingCartsTableFilterComposer(db, table), - orderingComposer: $$ShoppingCartsTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$ShoppingCartsTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - }) => - i2.ShoppingCartsCompanion( - id: id, - ), - getInsertCompanionBuilder: ({ - i0.Value id = const i0.Value.absent(), - }) => - i2.ShoppingCartsCompanion.insert( - id: id, - ))); -} - -class $$ShoppingCartEntriesTableFilterComposer extends i0 - .FilterComposer { - $$ShoppingCartEntriesTableFilterComposer(super.db, super.table); - i0.ColumnFilters get shoppingCartId => - i0.ColumnFilters($table.shoppingCart); - i0.ComposableFilter shoppingCart( - i0.ComposableFilter Function($$ShoppingCartsTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i3.ReadDatabaseContainer($db) - .resultSet('shopping_carts'), - getCurrentColumn: (f) => f.shoppingCart, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$ShoppingCartsTableFilterComposer(db, table), - builder: f); - } - - i0.ColumnFilters get itemId => i0.ColumnFilters($table.item); - i0.ComposableFilter item( - i0.ComposableFilter Function($$BuyableItemsTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i3.ReadDatabaseContainer($db) - .resultSet('buyable_items'), - getCurrentColumn: (f) => f.item, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$BuyableItemsTableFilterComposer(db, table), - builder: f); - } -} - -class $$ShoppingCartEntriesTableOrderingComposer extends i0 - .OrderingComposer { - $$ShoppingCartEntriesTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get shoppingCartId => - i0.ColumnOrderings($table.shoppingCart); - i0.ComposableOrdering shoppingCart( - i0.ComposableOrdering Function($$ShoppingCartsTableOrderingComposer o) - o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i3.ReadDatabaseContainer($db) - .resultSet('shopping_carts'), - getCurrentColumn: (f) => f.shoppingCart, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$ShoppingCartsTableOrderingComposer(db, table), - builder: o); - } - - i0.ColumnOrderings get itemId => i0.ColumnOrderings($table.item); - i0.ComposableOrdering item( - i0.ComposableOrdering Function($$BuyableItemsTableOrderingComposer o) o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: i3.ReadDatabaseContainer($db) - .resultSet('buyable_items'), - getCurrentColumn: (f) => f.item, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$BuyableItemsTableOrderingComposer(db, table), - builder: o); - } -} - -class $$ShoppingCartEntriesTableProcessedTableManager - extends i0.ProcessedTableManager< - i0.GeneratedDatabase, - i2.$ShoppingCartEntriesTable, - i2.ShoppingCartEntry, - $$ShoppingCartEntriesTableFilterComposer, - $$ShoppingCartEntriesTableOrderingComposer, - $$ShoppingCartEntriesTableProcessedTableManager, - $$ShoppingCartEntriesTableInsertCompanionBuilder, - $$ShoppingCartEntriesTableUpdateCompanionBuilder> { - const $$ShoppingCartEntriesTableProcessedTableManager(super.$state); -} - -typedef $$ShoppingCartEntriesTableInsertCompanionBuilder - = i2.ShoppingCartEntriesCompanion Function({ - required int shoppingCart, - required int item, -}); -typedef $$ShoppingCartEntriesTableUpdateCompanionBuilder - = i2.ShoppingCartEntriesCompanion Function({ - i0.Value shoppingCart, - i0.Value item, -}); - -class $$ShoppingCartEntriesTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i2.$ShoppingCartEntriesTable, - i2.ShoppingCartEntry, - $$ShoppingCartEntriesTableFilterComposer, - $$ShoppingCartEntriesTableOrderingComposer, - $$ShoppingCartEntriesTableProcessedTableManager, - $$ShoppingCartEntriesTableInsertCompanionBuilder, - $$ShoppingCartEntriesTableUpdateCompanionBuilder> { - $$ShoppingCartEntriesTableTableManager( - i0.GeneratedDatabase db, i2.$ShoppingCartEntriesTable table) - : super(i0.TableManagerState( - db: db, - table: table, - filteringComposer: - $$ShoppingCartEntriesTableFilterComposer(db, table), - orderingComposer: - $$ShoppingCartEntriesTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$ShoppingCartEntriesTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - i0.Value shoppingCart = const i0.Value.absent(), - i0.Value item = const i0.Value.absent(), - }) => - i2.ShoppingCartEntriesCompanion( - shoppingCart: shoppingCart, - item: item, - ), - getInsertCompanionBuilder: ({ - required int shoppingCart, - required int item, - }) => - i2.ShoppingCartEntriesCompanion.insert( - shoppingCart: shoppingCart, - item: item, - ))); -} - -class $RelationalDatabaseManager { - final $RelationalDatabase _db; - $RelationalDatabaseManager(this._db); - $$BuyableItemsTableTableManager get buyableItems => - $$BuyableItemsTableTableManager(_db, _db.buyableItems); - $$ShoppingCartsTableTableManager get shoppingCarts => - $$ShoppingCartsTableTableManager(_db, _db.shoppingCarts); - $$ShoppingCartEntriesTableTableManager get shoppingCartEntries => - $$ShoppingCartEntriesTableTableManager(_db, _db.shoppingCartEntries); -} - -class $ShoppingCartsTable extends i4.ShoppingCarts +class $ShoppingCartsTable extends i3.ShoppingCarts with i0.TableInfo<$ShoppingCartsTable, i2.ShoppingCart> { @override final i0.GeneratedDatabase attachedDatabase; @@ -485,7 +163,7 @@ class ShoppingCartsCompanion extends i0.UpdateCompanion { } } -class $ShoppingCartEntriesTable extends i4.ShoppingCartEntries +class $ShoppingCartEntriesTable extends i3.ShoppingCartEntries with i0.TableInfo<$ShoppingCartEntriesTable, i2.ShoppingCartEntry> { @override final i0.GeneratedDatabase attachedDatabase; diff --git a/drift/example/main.g.dart b/drift/example/main.g.dart index adb70e1b..c2ecb319 100644 --- a/drift/example/main.g.dart +++ b/drift/example/main.g.dart @@ -685,6 +685,7 @@ class $TodoItemWithCategoryNameViewView extends ViewInfo< abstract class _$Database extends GeneratedDatabase { _$Database(QueryExecutor e) : super(e); + _$DatabaseManager get managers => _$DatabaseManager(this); late final $TodoCategoriesTable todoCategories = $TodoCategoriesTable(this); late final $TodoItemsTable todoItems = $TodoItemsTable(this); late final $TodoCategoryItemCountView todoCategoryItemCount = @@ -705,3 +706,211 @@ abstract class _$Database extends GeneratedDatabase { itemTitle ]; } + +class $$TodoCategoriesTableFilterComposer + extends FilterComposer<_$Database, $TodoCategoriesTable> { + $$TodoCategoriesTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get name => ColumnFilters($table.name); + ComposableFilter todoItemsRefs( + ComposableFilter Function($$TodoItemsTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.todoItems, + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.categoryId, + getReferencedComposer: (db, table) => + $$TodoItemsTableFilterComposer(db, table), + builder: f); + } +} + +class $$TodoCategoriesTableOrderingComposer + extends OrderingComposer<_$Database, $TodoCategoriesTable> { + $$TodoCategoriesTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); +} + +class $$TodoCategoriesTableProcessedTableManager extends ProcessedTableManager< + _$Database, + $TodoCategoriesTable, + TodoCategory, + $$TodoCategoriesTableFilterComposer, + $$TodoCategoriesTableOrderingComposer, + $$TodoCategoriesTableProcessedTableManager, + $$TodoCategoriesTableInsertCompanionBuilder, + $$TodoCategoriesTableUpdateCompanionBuilder> { + const $$TodoCategoriesTableProcessedTableManager(super.$state); +} + +typedef $$TodoCategoriesTableInsertCompanionBuilder = TodoCategoriesCompanion + Function({ + Value id, + required String name, +}); +typedef $$TodoCategoriesTableUpdateCompanionBuilder = TodoCategoriesCompanion + Function({ + Value id, + Value name, +}); + +class $$TodoCategoriesTableTableManager extends RootTableManager< + _$Database, + $TodoCategoriesTable, + TodoCategory, + $$TodoCategoriesTableFilterComposer, + $$TodoCategoriesTableOrderingComposer, + $$TodoCategoriesTableProcessedTableManager, + $$TodoCategoriesTableInsertCompanionBuilder, + $$TodoCategoriesTableUpdateCompanionBuilder> { + $$TodoCategoriesTableTableManager(_$Database db, $TodoCategoriesTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$TodoCategoriesTableFilterComposer(db, table), + orderingComposer: $$TodoCategoriesTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$TodoCategoriesTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + }) => + TodoCategoriesCompanion( + id: id, + name: name, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String name, + }) => + TodoCategoriesCompanion.insert( + id: id, + name: name, + ))); +} + +class $$TodoItemsTableFilterComposer + extends FilterComposer<_$Database, $TodoItemsTable> { + $$TodoItemsTableFilterComposer(super.db, super.table); + ColumnFilters get id => ColumnFilters($table.id); + ColumnFilters get title => ColumnFilters($table.title); + ColumnFilters get content => ColumnFilters($table.content); + ColumnFilters get categoryIdId => ColumnFilters($table.categoryId); + ComposableFilter categoryId( + ComposableFilter Function($$TodoCategoriesTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.todoCategories, + getCurrentColumn: (f) => f.categoryId, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$TodoCategoriesTableFilterComposer(db, table), + builder: f); + } + + ColumnFilters get generatedText => + ColumnFilters($table.generatedText); +} + +class $$TodoItemsTableOrderingComposer + extends OrderingComposer<_$Database, $TodoItemsTable> { + $$TodoItemsTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get content => ColumnOrderings($table.content); + ColumnOrderings get categoryIdId => ColumnOrderings($table.categoryId); + ComposableOrdering categoryId( + ComposableOrdering Function($$TodoCategoriesTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: $db.todoCategories, + getCurrentColumn: (f) => f.categoryId, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$TodoCategoriesTableOrderingComposer(db, table), + builder: o); + } + + ColumnOrderings get generatedText => ColumnOrderings($table.generatedText); +} + +class $$TodoItemsTableProcessedTableManager extends ProcessedTableManager< + _$Database, + $TodoItemsTable, + TodoItem, + $$TodoItemsTableFilterComposer, + $$TodoItemsTableOrderingComposer, + $$TodoItemsTableProcessedTableManager, + $$TodoItemsTableInsertCompanionBuilder, + $$TodoItemsTableUpdateCompanionBuilder> { + const $$TodoItemsTableProcessedTableManager(super.$state); +} + +typedef $$TodoItemsTableInsertCompanionBuilder = TodoItemsCompanion Function({ + Value id, + required String title, + Value content, + required int categoryId, +}); +typedef $$TodoItemsTableUpdateCompanionBuilder = TodoItemsCompanion Function({ + Value id, + Value title, + Value content, + Value categoryId, +}); + +class $$TodoItemsTableTableManager extends RootTableManager< + _$Database, + $TodoItemsTable, + TodoItem, + $$TodoItemsTableFilterComposer, + $$TodoItemsTableOrderingComposer, + $$TodoItemsTableProcessedTableManager, + $$TodoItemsTableInsertCompanionBuilder, + $$TodoItemsTableUpdateCompanionBuilder> { + $$TodoItemsTableTableManager(_$Database db, $TodoItemsTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: $$TodoItemsTableFilterComposer(db, table), + orderingComposer: $$TodoItemsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$TodoItemsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value title = const Value.absent(), + Value content = const Value.absent(), + Value categoryId = const Value.absent(), + }) => + TodoItemsCompanion( + id: id, + title: title, + content: content, + categoryId: categoryId, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + required String title, + Value content = const Value.absent(), + required int categoryId, + }) => + TodoItemsCompanion.insert( + id: id, + title: title, + content: content, + categoryId: categoryId, + ))); +} + +class _$DatabaseManager { + final _$Database _db; + _$DatabaseManager(this._db); + $$TodoCategoriesTableTableManager get todoCategories => + $$TodoCategoriesTableTableManager(_db, _db.todoCategories); + $$TodoItemsTableTableManager get todoItems => + $$TodoItemsTableTableManager(_db, _db.todoItems); +} diff --git a/drift/pubspec.yaml b/drift/pubspec.yaml index ba3bf91a..fbb300aa 100644 --- a/drift/pubspec.yaml +++ b/drift/pubspec.yaml @@ -40,3 +40,6 @@ dev_dependencies: shelf: ^1.3.0 test_descriptor: ^2.0.1 vm_service: ^13.0.0 +dependency_overrides: + drift_dev: + path: ../drift_dev \ No newline at end of file diff --git a/drift/test/manager/filter_composer_test.dart b/drift/test/manager/filter_composer_test.dart new file mode 100644 index 00000000..9c20b3d9 --- /dev/null +++ b/drift/test/manager/filter_composer_test.dart @@ -0,0 +1,54 @@ +import 'package:drift/drift.dart'; +import 'package:test/test.dart'; + +import '../generated/todos.dart'; +import '../test_utils/matchers.dart'; +import '../test_utils/mocks.dart'; + +void main() { + driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; + + late TodoDb db; + late MockExecutor executor; + + setUp(() { + executor = MockExecutor(); + db = TodoDb(executor); + }); + + test('manager - generates parentheses for OR in AND', () { + final filterComposer = $$CategoriesTableFilterComposer(db, db.categories); + final expr = (filterComposer.idValue(1) | filterComposer.idValue(2)) & + (filterComposer.idValue(3) | filterComposer.idValue(4)); + expect( + expr.expression, + generates( + '("id" = ? OR "id" = ?) AND ("id" = ? OR "id" = ?)', [1, 2, 3, 4])); + }); + + test('manager - equals', () { + final filterComposer = $$CategoriesTableFilterComposer(db, db.categories); + // Numeric + expect(filterComposer.idValue.equals(3).expression, + generates('"id" = ?', [3])); + expect(filterComposer.idValue(3).expression, generates('"id" = ?', [3])); + expect( + filterComposer.idValue.isNull().expression, generates('"id" IS NULL')); + // Text + expect(filterComposer.description.equals("Hi").expression, + generates('"desc" = ?', ["Hi"])); + expect(filterComposer.description("Hi").expression, + generates('"desc" = ?', ["Hi"])); + expect(filterComposer.description.isNull().expression, + generates('"desc" IS NULL')); + }); + + // test('manager - combine query with AND ', () { + // e.expression.hashCode; + + // expect( + // countAll(filter: e.expression), + // generates('WHERE "id" = FILTER (WHERE foo >= ?)', [1, 2]), + // ); + // }); +} diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 8c3ae219..67cf6b60 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -615,7 +615,7 @@ class ManagerWriter { } // Remove ones that have custom row classes - tableWriters.removeWhere((t) => !t.hasCustomRowClass); + tableWriters.removeWhere((t) => t.hasCustomRowClass); // Write each tables manager to the leaf and append the getter to the main manager final tableManagerGetters = StringBuffer(); diff --git a/examples/manager/lib/pages/product.dart b/examples/manager/lib/pages/product.dart index 5850350a..d7cb3a10 100644 --- a/examples/manager/lib/pages/product.dart +++ b/examples/manager/lib/pages/product.dart @@ -59,7 +59,8 @@ class ProductPage extends HookConsumerWidget { ), TextField( controller: descriptionTextController, - decoration: const InputDecoration(labelText: "Description"), + decoration: + const InputDecoration(labelText: "Description"), ), ListTile( leading: CircleAvatar( diff --git a/examples/manager/pubspec.yaml b/examples/manager/pubspec.yaml index 2088e914..f4c8be00 100644 --- a/examples/manager/pubspec.yaml +++ b/examples/manager/pubspec.yaml @@ -33,3 +33,9 @@ dev_dependencies: flutter: uses-material-design: true + +dependency_overrides: + drift_dev: + path: ../../drift_dev + drift: + path: ../../drift \ No newline at end of file diff --git a/examples/migrations_example/lib/database.g.dart b/examples/migrations_example/lib/database.g.dart index 1ad77e32..6e810776 100644 --- a/examples/migrations_example/lib/database.g.dart +++ b/examples/migrations_example/lib/database.g.dart @@ -884,7 +884,6 @@ class GroupCount extends ViewInfo abstract class _$Database extends GeneratedDatabase { _$Database(QueryExecutor e) : super(e); - _$DatabaseManager get managers => _$DatabaseManager(this); late final $UsersTable users = $UsersTable(this); late final Groups groups = Groups(this); late final Notes notes = Notes(this); @@ -901,329 +900,3 @@ abstract class _$Database extends GeneratedDatabase { DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); } - -class $$UsersTableFilterComposer - extends FilterComposer<_$Database, $UsersTable> { - $$UsersTableFilterComposer(super.db, super.table); - ColumnFilters get id => ColumnFilters($table.id); - ColumnFilters get name => ColumnFilters($table.name); - ColumnFilters get birthday => ColumnFilters($table.birthday); - ColumnFilters get nextUserId => ColumnFilters($table.nextUser); - ComposableFilter nextUser( - ComposableFilter Function($$UsersTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.users, - getCurrentColumn: (f) => f.nextUser, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$UsersTableFilterComposer(db, table), - builder: f); - } - - ComposableFilter usersRefs( - ComposableFilter Function($$UsersTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.users, - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.nextUser, - getReferencedComposer: (db, table) => - $$UsersTableFilterComposer(db, table), - builder: f); - } - - ComposableFilter groupsRefs( - ComposableFilter Function($GroupsFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.groups, - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.owner, - getReferencedComposer: (db, table) => $GroupsFilterComposer(db, table), - builder: f); - } -} - -class $$UsersTableOrderingComposer - extends OrderingComposer<_$Database, $UsersTable> { - $$UsersTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get name => ColumnOrderings($table.name); - ColumnOrderings get birthday => ColumnOrderings($table.birthday); - ColumnOrderings get nextUserId => ColumnOrderings($table.nextUser); - ComposableOrdering nextUser( - ComposableOrdering Function($$UsersTableOrderingComposer o) o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.users, - getCurrentColumn: (f) => f.nextUser, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$UsersTableOrderingComposer(db, table), - builder: o); - } -} - -class $$UsersTableProcessedTableManager extends ProcessedTableManager< - _$Database, - $UsersTable, - User, - $$UsersTableFilterComposer, - $$UsersTableOrderingComposer, - $$UsersTableProcessedTableManager, - $$UsersTableInsertCompanionBuilder, - $$UsersTableUpdateCompanionBuilder> { - const $$UsersTableProcessedTableManager(super.$state); -} - -typedef $$UsersTableInsertCompanionBuilder = UsersCompanion Function({ - Value id, - Value name, - Value birthday, - Value nextUser, -}); -typedef $$UsersTableUpdateCompanionBuilder = UsersCompanion Function({ - Value id, - Value name, - Value birthday, - Value nextUser, -}); - -class $$UsersTableTableManager extends RootTableManager< - _$Database, - $UsersTable, - User, - $$UsersTableFilterComposer, - $$UsersTableOrderingComposer, - $$UsersTableProcessedTableManager, - $$UsersTableInsertCompanionBuilder, - $$UsersTableUpdateCompanionBuilder> { - $$UsersTableTableManager(_$Database db, $UsersTable table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: $$UsersTableFilterComposer(db, table), - orderingComposer: $$UsersTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$UsersTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - Value id = const Value.absent(), - Value name = const Value.absent(), - Value birthday = const Value.absent(), - Value nextUser = const Value.absent(), - }) => - UsersCompanion( - id: id, - name: name, - birthday: birthday, - nextUser: nextUser, - ), - getInsertCompanionBuilder: ({ - Value id = const Value.absent(), - Value name = const Value.absent(), - Value birthday = const Value.absent(), - Value nextUser = const Value.absent(), - }) => - UsersCompanion.insert( - id: id, - name: name, - birthday: birthday, - nextUser: nextUser, - ))); -} - -class $GroupsFilterComposer extends FilterComposer<_$Database, Groups> { - $GroupsFilterComposer(super.db, super.table); - ColumnFilters get id => ColumnFilters($table.id); - ColumnFilters get title => ColumnFilters($table.title); - ColumnFilters get deleted => ColumnFilters($table.deleted); - ColumnFilters get ownerId => ColumnFilters($table.owner); - ComposableFilter owner( - ComposableFilter Function($$UsersTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.users, - getCurrentColumn: (f) => f.owner, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$UsersTableFilterComposer(db, table), - builder: f); - } -} - -class $GroupsOrderingComposer extends OrderingComposer<_$Database, Groups> { - $GroupsOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get title => ColumnOrderings($table.title); - ColumnOrderings get deleted => ColumnOrderings($table.deleted); - ColumnOrderings get ownerId => ColumnOrderings($table.owner); - ComposableOrdering owner( - ComposableOrdering Function($$UsersTableOrderingComposer o) o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.users, - getCurrentColumn: (f) => f.owner, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$UsersTableOrderingComposer(db, table), - builder: o); - } -} - -class $GroupsProcessedTableManager extends ProcessedTableManager< - _$Database, - Groups, - Group, - $GroupsFilterComposer, - $GroupsOrderingComposer, - $GroupsProcessedTableManager, - $GroupsInsertCompanionBuilder, - $GroupsUpdateCompanionBuilder> { - const $GroupsProcessedTableManager(super.$state); -} - -typedef $GroupsInsertCompanionBuilder = GroupsCompanion Function({ - Value id, - required String title, - Value deleted, - required int owner, -}); -typedef $GroupsUpdateCompanionBuilder = GroupsCompanion Function({ - Value id, - Value title, - Value deleted, - Value owner, -}); - -class $GroupsTableManager extends RootTableManager< - _$Database, - Groups, - Group, - $GroupsFilterComposer, - $GroupsOrderingComposer, - $GroupsProcessedTableManager, - $GroupsInsertCompanionBuilder, - $GroupsUpdateCompanionBuilder> { - $GroupsTableManager(_$Database db, Groups table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: $GroupsFilterComposer(db, table), - orderingComposer: $GroupsOrderingComposer(db, table), - getChildManagerBuilder: (p0) => $GroupsProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - Value id = const Value.absent(), - Value title = const Value.absent(), - Value deleted = const Value.absent(), - Value owner = const Value.absent(), - }) => - GroupsCompanion( - id: id, - title: title, - deleted: deleted, - owner: owner, - ), - getInsertCompanionBuilder: ({ - Value id = const Value.absent(), - required String title, - Value deleted = const Value.absent(), - required int owner, - }) => - GroupsCompanion.insert( - id: id, - title: title, - deleted: deleted, - owner: owner, - ))); -} - -class $NotesFilterComposer extends FilterComposer<_$Database, Notes> { - $NotesFilterComposer(super.db, super.table); - ColumnFilters get title => ColumnFilters($table.title); - ColumnFilters get content => ColumnFilters($table.content); - ColumnFilters get searchTerms => ColumnFilters($table.searchTerms); -} - -class $NotesOrderingComposer extends OrderingComposer<_$Database, Notes> { - $NotesOrderingComposer(super.db, super.table); - ColumnOrderings get title => ColumnOrderings($table.title); - ColumnOrderings get content => ColumnOrderings($table.content); - ColumnOrderings get searchTerms => ColumnOrderings($table.searchTerms); -} - -class $NotesProcessedTableManager extends ProcessedTableManager< - _$Database, - Notes, - Note, - $NotesFilterComposer, - $NotesOrderingComposer, - $NotesProcessedTableManager, - $NotesInsertCompanionBuilder, - $NotesUpdateCompanionBuilder> { - const $NotesProcessedTableManager(super.$state); -} - -typedef $NotesInsertCompanionBuilder = NotesCompanion Function({ - required String title, - required String content, - required String searchTerms, -}); -typedef $NotesUpdateCompanionBuilder = NotesCompanion Function({ - Value title, - Value content, - Value searchTerms, -}); - -class $NotesTableManager extends RootTableManager< - _$Database, - Notes, - Note, - $NotesFilterComposer, - $NotesOrderingComposer, - $NotesProcessedTableManager, - $NotesInsertCompanionBuilder, - $NotesUpdateCompanionBuilder> { - $NotesTableManager(_$Database db, Notes table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: $NotesFilterComposer(db, table), - orderingComposer: $NotesOrderingComposer(db, table), - getChildManagerBuilder: (p0) => $NotesProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - Value title = const Value.absent(), - Value content = const Value.absent(), - Value searchTerms = const Value.absent(), - }) => - NotesCompanion( - title: title, - content: content, - searchTerms: searchTerms, - ), - getInsertCompanionBuilder: ({ - required String title, - required String content, - required String searchTerms, - }) => - NotesCompanion.insert( - title: title, - content: content, - searchTerms: searchTerms, - ))); -} - -class _$DatabaseManager { - final _$Database _db; - _$DatabaseManager(this._db); - $$UsersTableTableManager get users => - $$UsersTableTableManager(_db, _db.users); - $GroupsTableManager get groups => $GroupsTableManager(_db, _db.groups); - $NotesTableManager get notes => $NotesTableManager(_db, _db.notes); -} diff --git a/extras/benchmarks/bin/benchmarks.dart b/extras/benchmarks/bin/benchmarks.dart index b8a25bcb..e8c4fb6a 100644 --- a/extras/benchmarks/bin/benchmarks.dart +++ b/extras/benchmarks/bin/benchmarks.dart @@ -25,7 +25,7 @@ Future main() async { output.writeAsStringSync(json.encode(tracker.timings)); - // Make sure the process exits. Otherwise, unclosed resources from the + // Make sure the process exits. Otherwise, unclosed resources from the // benchmarks will keep the process alive. exit(0); } diff --git a/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart b/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart index 24edca0d..ddc25e14 100644 --- a/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart +++ b/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart @@ -18,7 +18,7 @@ SELECT * FROM key_values WHERE value IN (SELECT value FROM key_values WHERE valu '''; final fs = []; - + for (var i = 0; i < _numQueries; i++) { fs.add( _db.customSelect(queryToBench, variables: [Variable(uuid.v4())]).get(), From 2f9a57317f2f96d7a601c3bba7d61fbeccbf383e Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Thu, 11 Apr 2024 12:38:54 -0400 Subject: [PATCH 51/74] add some more tests --- drift/lib/src/runtime/manager/manager.dart | 12 +- drift/test/manager/filter_composer_test.dart | 54 ------- drift/test/manager/manager_filter_tests.dart | 16 ++ drift/test/manager/manager_tests.dart | 145 +++++++++++++++++++ 4 files changed, 168 insertions(+), 59 deletions(-) delete mode 100644 drift/test/manager/filter_composer_test.dart create mode 100644 drift/test/manager/manager_filter_tests.dart create mode 100644 drift/test/manager/manager_tests.dart diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 83f85135..cc1b321c 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -230,7 +230,10 @@ class TableManagerState< final count = countAll(); final result = _buildSelectStatement(targetColumns: [count]) as _JoinedResult; - return result.statement.map((row) => row.read(count)!).getSingle(); + return result.statement + .map((row) => row.read(count)!) + .get() + .then((value) => value.firstOrNull ?? 0); } /// Check if any rows exists using the built statement @@ -331,7 +334,7 @@ abstract class BaseTableManager< /// /// See also: [RootTableManager.replace], which does not require [filter] statements and /// supports setting fields back to null. - Future write(Insertable
Function(CU o) f) => + Future update(Insertable
Function(CU o) f) => $state.buildUpdateStatement().write(f($state._getUpdateCompanionBuilder)); } @@ -441,8 +444,7 @@ abstract class RootTableManager< /// Returns the amount of rows that were deleted by this statement directly /// (not including additional rows that might be affected through triggers or /// foreign key constraints). - Future delete(Insertable entity) => - $state.db.delete($state._tableAsTableInfo).delete(entity); + Future delete() => $state.db.delete($state._tableAsTableInfo).go(); /// Select all rows from the table C all() => $state._getChildManagerBuilder($state); @@ -532,7 +534,7 @@ abstract class RootTableManager< /// If [entity] has absent values (set to null on the [DataClass] or /// explicitly to absent on the [UpdateCompanion]), and a default value for /// the field exists, that default value will be used. Otherwise, the field - /// will be reset to null. This behavior is different to [write], which simply + /// will be reset to null. This behavior is different to [update], which simply /// ignores such fields without changing them in the database. /// /// Returns true if a row was affected by this operation. diff --git a/drift/test/manager/filter_composer_test.dart b/drift/test/manager/filter_composer_test.dart deleted file mode 100644 index 9c20b3d9..00000000 --- a/drift/test/manager/filter_composer_test.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:test/test.dart'; - -import '../generated/todos.dart'; -import '../test_utils/matchers.dart'; -import '../test_utils/mocks.dart'; - -void main() { - driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; - - late TodoDb db; - late MockExecutor executor; - - setUp(() { - executor = MockExecutor(); - db = TodoDb(executor); - }); - - test('manager - generates parentheses for OR in AND', () { - final filterComposer = $$CategoriesTableFilterComposer(db, db.categories); - final expr = (filterComposer.idValue(1) | filterComposer.idValue(2)) & - (filterComposer.idValue(3) | filterComposer.idValue(4)); - expect( - expr.expression, - generates( - '("id" = ? OR "id" = ?) AND ("id" = ? OR "id" = ?)', [1, 2, 3, 4])); - }); - - test('manager - equals', () { - final filterComposer = $$CategoriesTableFilterComposer(db, db.categories); - // Numeric - expect(filterComposer.idValue.equals(3).expression, - generates('"id" = ?', [3])); - expect(filterComposer.idValue(3).expression, generates('"id" = ?', [3])); - expect( - filterComposer.idValue.isNull().expression, generates('"id" IS NULL')); - // Text - expect(filterComposer.description.equals("Hi").expression, - generates('"desc" = ?', ["Hi"])); - expect(filterComposer.description("Hi").expression, - generates('"desc" = ?', ["Hi"])); - expect(filterComposer.description.isNull().expression, - generates('"desc" IS NULL')); - }); - - // test('manager - combine query with AND ', () { - // e.expression.hashCode; - - // expect( - // countAll(filter: e.expression), - // generates('WHERE "id" = FILTER (WHERE foo >= ?)', [1, 2]), - // ); - // }); -} diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_tests.dart new file mode 100644 index 00000000..cccf1770 --- /dev/null +++ b/drift/test/manager/manager_filter_tests.dart @@ -0,0 +1,16 @@ +import 'package:drift/drift.dart'; +import 'package:test/test.dart'; + +import '../generated/todos.dart'; +import '../test_utils/test_utils.dart'; + +void main() { + driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; + late TodoDb db; + + setUp(() { + db = TodoDb(testInMemoryDatabase()); + }); + + tearDown(() => db.close()); +} diff --git a/drift/test/manager/manager_tests.dart b/drift/test/manager/manager_tests.dart new file mode 100644 index 00000000..9f3a83c4 --- /dev/null +++ b/drift/test/manager/manager_tests.dart @@ -0,0 +1,145 @@ +import 'package:drift/drift.dart'; +import 'package:test/test.dart'; + +import '../generated/todos.dart'; +import '../test_utils/test_utils.dart'; + +void main() { + driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; + late TodoDb db; + + setUp(() { + db = TodoDb(testInMemoryDatabase()); + }); + + tearDown(() => db.close()); + + test('manager - create', () async { + // Initial count should be 0 + expect(db.managers.categories.all().count(), completion(0)); + + // Creating a row should return the id + final create1 = db.managers.categories.create( + (o) => o(priority: Value(CategoryPriority.high), description: "High")); + expect(create1, completion(1)); + expect(db.managers.categories.all().count(), completion(1)); + + // Creating another row should increment the id + final create2 = db.managers.categories.create( + (o) => o(priority: Value(CategoryPriority.low), description: "Low")); + expect(create2, completion(2)); + expect(db.managers.categories.all().count(), completion(2)); + + // Using an existing id should throw an exception + final create3 = db.managers.categories.create((o) => o( + priority: Value(CategoryPriority.medium), + description: "Medium", + id: Value(RowId(1)))); + expect(create3, throwsException); + + // Using on conflict should not throw an exception + // Only using DoNothing test that onConflict is being passed to the create method + final create4 = db.managers.categories.create( + (o) => o( + priority: Value(CategoryPriority.medium), + description: "Medium", + id: Value(RowId(1))), + onConflict: DoNothing()); + // The is incorrect when using onConflict + expect(create4, completion(2)); + expect(db.managers.categories.all().count(), completion(2)); + + // Likewise, test that mode is passed to the create method + final create5 = db.managers.categories.create( + (o) => o( + priority: Value(CategoryPriority.medium), + description: "Medium", + id: Value(RowId(1))), + mode: InsertMode.insertOrIgnore); + + // The is incorrect when using mode + expect(create5, completion(2)); + expect(db.managers.categories.all().count(), completion(2)); + + // Test the other create methods + final create6 = db.managers.categories.createReturning((o) => + o(priority: Value(CategoryPriority.high), description: "Other High")); + expect(create6, completion(isA())); + expect(db.managers.categories.all().count(), completion(3)); + + // Will return null because the description is not unique + final create7 = db.managers.categories.createReturningOrNull( + (o) => o( + priority: Value(CategoryPriority.high), + description: "High", + ), + mode: InsertMode.insertOrIgnore); + expect(create7, completion(null)); + + // Test batch create + await db.managers.categories.bulkCreate((o) => [ + o(priority: Value(CategoryPriority.high), description: "Super High"), + o(priority: Value(CategoryPriority.low), description: "Super Low"), + o( + priority: Value(CategoryPriority.medium), + description: "Super Medium") + ]); + expect(db.managers.categories.all().count(), completion(6)); + }); + + test('manager - update', () async { + // Create a row + final obj1 = await db.managers.categories.createReturning( + (o) => o(priority: Value(CategoryPriority.low), description: "Low")); + final obj2 = await db.managers.categories.createReturning((o) => + o(priority: Value(CategoryPriority.low), description: "Other Low")); + + // Replace the row + final update1 = + db.managers.categories.replace(obj1.copyWith(description: "Hello")); + expect(update1, completion(true)); + expect( + db.managers.categories + .filter(((f) => f.id(obj1.id))) + .getSingle() + .then((value) => value.description), + completion("Hello")); + + // Update All Rows + final update2 = db.managers.categories + .update((o) => o(priority: Value(CategoryPriority.high))); + expect(update2, completion(2)); + + // Update a single row + final update3 = db.managers.categories + .filter(((f) => f.id(obj2.id))) + .update((o) => o(description: Value("World"))); + expect(update3, completion(1)); + expect( + db.managers.categories + .filter(((f) => f.id(obj2.id))) + .getSingle() + .then((value) => value.description), + completion("World")); + }); + + test('manager - delete', () async { + // Create a row + final obj1 = await db.managers.categories.createReturning( + (o) => o(priority: Value(CategoryPriority.low), description: "Low")); + // ignore: unused_local_variable + final obj2 = await db.managers.categories.createReturning((o) => + o(priority: Value(CategoryPriority.low), description: "Other Low")); + + // Delete a single row + final delete1 = + db.managers.categories.filter(((f) => f.id(obj1.id))).delete(); + expect(delete1, completion(1)); + expect(db.managers.categories.all().count(), completion(1)); + + // Delete all rows + final delete2 = db.managers.categories.delete(); + expect(delete2, completion(1)); + expect(db.managers.categories.all().count(), completion(0)); + }); +} From b6e15c39edad7fac36d74b642822fde810467aac Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Thu, 11 Apr 2024 15:04:24 -0400 Subject: [PATCH 52/74] tests and fixes --- drift/lib/src/runtime/manager/filter.dart | 121 +++-- drift/test/generated/todos.dart | 3 + drift/test/generated/todos.g.dart | 110 ++++- drift/test/manager/manager_filter_tests.dart | 454 +++++++++++++++++++ 4 files changed, 647 insertions(+), 41 deletions(-) diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 71b9ba74..eaf9181c 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -38,18 +38,78 @@ class ColumnFilters { /// Shortcut for [equals] ComposableFilter call(T value) => ComposableFilter(column.equals(value)); +} - /// Nested column for filtering on the count of a column - ColumnFilters get count => ColumnFilters(column.count()); +enum _StringFilterTypes { contains, startsWith, endsWith } + +/// Built in filters for int/double columns +extension StringFilters on ColumnFilters { + /// This function helps handle case insensitivity in like expressions + /// This helps handle all the possible scenarios: + /// 1. If a user hasn't set the database to be case sensitive in like expressions + /// Then we are ok with having the query be performed on upper case values + /// 2. If a user has set the database to be case sensitive in like expressions + /// We still can perform a case insensitive search by default. We have all filters + /// use `{bool caseInsensitive = true}` which will perform a case insensitive search + /// 3. If a user has set the database to be case sensitive in like expressions and wan't + /// to perform a case sensitive search, they can pass `caseInsensitive = false` manually + /// + /// We are using the default of {bool caseInsensitive = true}, so that users who haven't set + /// the database to be case sensitive wont be confues why their like expressions are case insensitive + Expression _buildExpression( + _StringFilterTypes type, String value, bool caseInsensitive) { + final Expression column; + if (caseInsensitive) { + value = value.toUpperCase(); + column = this.column.upper(); + } else { + column = this.column; + } + switch (type) { + case _StringFilterTypes.contains: + return column.like('%$value%'); + case _StringFilterTypes.startsWith: + return column.like('$value%'); + case _StringFilterTypes.endsWith: + return column.like('%$value'); + } + } + + /// Create a filter to check if the this text column contains a substring + /// {@template drift.manager.likeCaseSensitivity} + /// Sqlite performs a case insensitive text search by default + /// + /// If you have set the database to use case sensitive in like expressions, you can + /// pass [caseInsensitive] as false to perform a case sensitive search + /// See https://www.sqlitetutorial.net/sqlite-like/ for more information + /// {@endtemplate} + ComposableFilter contains(T value, {bool caseInsensitive = true}) { + return ComposableFilter( + _buildExpression(_StringFilterTypes.contains, value, caseInsensitive)); + } + + /// Create a filter to check if the this text column starts with a substring + /// {@macro drift.manager.likeCaseSensitivity} + ComposableFilter startsWith(T value, {bool caseInsensitive = true}) { + return ComposableFilter(_buildExpression( + _StringFilterTypes.startsWith, value, caseInsensitive)); + } + + /// Create a filter to check if the this text column ends with a substring + /// {@macro drift.manager.likeCaseSensitivity} + ComposableFilter endsWith(T value, {bool caseInsensitive = true}) { + return ComposableFilter( + _buildExpression(_StringFilterTypes.endsWith, value, caseInsensitive)); + } } /// Built in filters for bool columns extension BoolFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value - ComposableFilter isTrue(bool value) => ComposableFilter(column.equals(true)); + ComposableFilter isTrue() => ComposableFilter(column.equals(true)); /// Create a filter to check if the column is small than a value - ComposableFilter isFalse(bool value) => ComposableFilter(column.equals(true)); + ComposableFilter isFalse() => ComposableFilter(column.equals(false)); } /// Built in filters for int/double columns @@ -71,27 +131,16 @@ extension NumFilters on ColumnFilters { ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between two values + /// This is done inclusively, so the column can be equal to the lower or higher value + /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between two values + /// This is done inclusively, so the column will not be equal to the lower or higher value + /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._invert(); - - /// Nested column for filtering on the average of a column - ColumnFilters get avg => ColumnFilters(column.avg()); - - /// Nested column for filtering on the max item of a column - ColumnFilters get max => ColumnFilters(column.max()); - - /// Nested column for filtering on the min item of a column - ColumnFilters get min => ColumnFilters(column.min()); - - /// Nested column for filtering on the sum of a column - ColumnFilters get sum => ColumnFilters(column.sum()); - - /// Nested column for filtering on the total of a column - ColumnFilters get total => ColumnFilters(column.total()); } /// Built in filters for BigInt columns @@ -113,30 +162,19 @@ extension BigIntFilters on ColumnFilters { ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between two values + /// This is done inclusively, so the column can be equal to the lower or higher value + /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between two values + /// This is done inclusively, so the column will not be equal to the lower or higher value + /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._invert(); - - /// Nested column for filtering on the average of a column - ColumnFilters get avg => ColumnFilters(column.avg()); - - /// Nested column for filtering on the max item of a column - ColumnFilters get max => ColumnFilters(column.max()); - - /// Nested column for filtering on the min item of a column - ColumnFilters get min => ColumnFilters(column.min()); - - /// Nested column for filtering on the sum of a column - ColumnFilters get sum => ColumnFilters(column.sum()); - - /// Nested column for filtering on the total of a column - ColumnFilters get total => ColumnFilters(column.total()); } -/// Built in filters for String columns +/// Built in filters for DateTime columns extension DateFilters on ColumnFilters { /// Create a filter to check if the column is after a [DateTime] ComposableFilter isAfter(T value) => @@ -155,11 +193,14 @@ extension DateFilters on ColumnFilters { ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between 2 [DateTime]s - + /// This is done inclusively, so the column can be equal to the lower or higher value + /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between 2 [DateTime]s + /// This is done inclusively, so the column will not be equal to the lower or higher value + /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._invert(); } @@ -185,6 +226,14 @@ class ColumnWithTypeConverterFilters { /// Shortcut for [equals] ComposableFilter call(CUSTOM value) => ComposableFilter(column.equalsValue(value)); + + /// Create a filter that checks if the column is in a list of values. + ComposableFilter isIn(Iterable values) => + ComposableFilter(column.isInValues(values)); + + /// Create a filter that checks if the column is not in a list of values. + ComposableFilter isNotIn(Iterable values) => + ComposableFilter(column.isNotInValues(values)); } /// This class is wrapper on the expression class diff --git a/drift/test/generated/todos.dart b/drift/test/generated/todos.dart index a7fa13df..0b0a6c28 100644 --- a/drift/test/generated/todos.dart +++ b/drift/test/generated/todos.dart @@ -24,6 +24,9 @@ class TodosTable extends Table with AutoIncrement { @JsonKey('target_date') DateTimeColumn get targetDate => dateTime().nullable().unique()(); + RealColumn get someFloat => real().nullable()(); + Int64Column get someInt64 => int64().nullable()(); + IntColumn get category => integer().references(Categories, #id).nullable()(); TextColumn get status => textEnum().nullable()(); diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 2849a4c6..154b5be7 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -305,6 +305,18 @@ class $TodosTableTable extends TodosTable type: DriftSqlType.dateTime, requiredDuringInsert: false, defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE')); + static const VerificationMeta _someFloatMeta = + const VerificationMeta('someFloat'); + @override + late final GeneratedColumn someFloat = GeneratedColumn( + 'some_float', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + static const VerificationMeta _someInt64Meta = + const VerificationMeta('someInt64'); + @override + late final GeneratedColumn someInt64 = GeneratedColumn( + 'some_int64', aliasedName, true, + type: DriftSqlType.bigInt, requiredDuringInsert: false); static const VerificationMeta _categoryMeta = const VerificationMeta('category'); @override @@ -322,7 +334,7 @@ class $TodosTableTable extends TodosTable .withConverter($TodosTableTable.$converterstatusn); @override List get $columns => - [id, title, content, targetDate, category, status]; + [id, title, content, targetDate, someFloat, someInt64, category, status]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -350,6 +362,14 @@ class $TodosTableTable extends TodosTable targetDate.isAcceptableOrUnknown( data['target_date']!, _targetDateMeta)); } + if (data.containsKey('some_float')) { + context.handle(_someFloatMeta, + someFloat.isAcceptableOrUnknown(data['some_float']!, _someFloatMeta)); + } + if (data.containsKey('some_int64')) { + context.handle(_someInt64Meta, + someInt64.isAcceptableOrUnknown(data['some_int64']!, _someInt64Meta)); + } if (data.containsKey('category')) { context.handle(_categoryMeta, category.isAcceptableOrUnknown(data['category']!, _categoryMeta)); @@ -377,6 +397,10 @@ class $TodosTableTable extends TodosTable .read(DriftSqlType.string, data['${effectivePrefix}content'])!, targetDate: attachedDatabase.typeMapping .read(DriftSqlType.dateTime, data['${effectivePrefix}target_date']), + someFloat: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}some_float']), + someInt64: attachedDatabase.typeMapping + .read(DriftSqlType.bigInt, data['${effectivePrefix}some_int64']), category: attachedDatabase.typeMapping .read(DriftSqlType.int, data['${effectivePrefix}category']), status: $TodosTableTable.$converterstatusn.fromSql(attachedDatabase @@ -403,6 +427,8 @@ class TodoEntry extends DataClass implements Insertable { final String? title; final String content; final DateTime? targetDate; + final double? someFloat; + final BigInt? someInt64; final int? category; final TodoStatus? status; const TodoEntry( @@ -410,6 +436,8 @@ class TodoEntry extends DataClass implements Insertable { this.title, required this.content, this.targetDate, + this.someFloat, + this.someInt64, this.category, this.status}); @override @@ -425,6 +453,12 @@ class TodoEntry extends DataClass implements Insertable { if (!nullToAbsent || targetDate != null) { map['target_date'] = Variable(targetDate); } + if (!nullToAbsent || someFloat != null) { + map['some_float'] = Variable(someFloat); + } + if (!nullToAbsent || someInt64 != null) { + map['some_int64'] = Variable(someInt64); + } if (!nullToAbsent || category != null) { map['category'] = Variable(category); } @@ -444,6 +478,12 @@ class TodoEntry extends DataClass implements Insertable { targetDate: targetDate == null && nullToAbsent ? const Value.absent() : Value(targetDate), + someFloat: someFloat == null && nullToAbsent + ? const Value.absent() + : Value(someFloat), + someInt64: someInt64 == null && nullToAbsent + ? const Value.absent() + : Value(someInt64), category: category == null && nullToAbsent ? const Value.absent() : Value(category), @@ -461,6 +501,8 @@ class TodoEntry extends DataClass implements Insertable { title: serializer.fromJson(json['title']), content: serializer.fromJson(json['content']), targetDate: serializer.fromJson(json['target_date']), + someFloat: serializer.fromJson(json['someFloat']), + someInt64: serializer.fromJson(json['someInt64']), category: serializer.fromJson(json['category']), status: $TodosTableTable.$converterstatusn .fromJson(serializer.fromJson(json['status'])), @@ -479,6 +521,8 @@ class TodoEntry extends DataClass implements Insertable { 'title': serializer.toJson(title), 'content': serializer.toJson(content), 'target_date': serializer.toJson(targetDate), + 'someFloat': serializer.toJson(someFloat), + 'someInt64': serializer.toJson(someInt64), 'category': serializer.toJson(category), 'status': serializer .toJson($TodosTableTable.$converterstatusn.toJson(status)), @@ -490,6 +534,8 @@ class TodoEntry extends DataClass implements Insertable { Value title = const Value.absent(), String? content, Value targetDate = const Value.absent(), + Value someFloat = const Value.absent(), + Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent()}) => TodoEntry( @@ -497,6 +543,8 @@ class TodoEntry extends DataClass implements Insertable { title: title.present ? title.value : this.title, content: content ?? this.content, targetDate: targetDate.present ? targetDate.value : this.targetDate, + someFloat: someFloat.present ? someFloat.value : this.someFloat, + someInt64: someInt64.present ? someInt64.value : this.someInt64, category: category.present ? category.value : this.category, status: status.present ? status.value : this.status, ); @@ -507,6 +555,8 @@ class TodoEntry extends DataClass implements Insertable { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') + ..write('someFloat: $someFloat, ') + ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status') ..write(')')) @@ -514,8 +564,8 @@ class TodoEntry extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, title, content, targetDate, category, status); + int get hashCode => Object.hash( + id, title, content, targetDate, someFloat, someInt64, category, status); @override bool operator ==(Object other) => identical(this, other) || @@ -524,6 +574,8 @@ class TodoEntry extends DataClass implements Insertable { other.title == this.title && other.content == this.content && other.targetDate == this.targetDate && + other.someFloat == this.someFloat && + other.someInt64 == this.someInt64 && other.category == this.category && other.status == this.status); } @@ -533,6 +585,8 @@ class TodosTableCompanion extends UpdateCompanion { final Value title; final Value content; final Value targetDate; + final Value someFloat; + final Value someInt64; final Value category; final Value status; const TodosTableCompanion({ @@ -540,6 +594,8 @@ class TodosTableCompanion extends UpdateCompanion { this.title = const Value.absent(), this.content = const Value.absent(), this.targetDate = const Value.absent(), + this.someFloat = const Value.absent(), + this.someInt64 = const Value.absent(), this.category = const Value.absent(), this.status = const Value.absent(), }); @@ -548,6 +604,8 @@ class TodosTableCompanion extends UpdateCompanion { this.title = const Value.absent(), required String content, this.targetDate = const Value.absent(), + this.someFloat = const Value.absent(), + this.someInt64 = const Value.absent(), this.category = const Value.absent(), this.status = const Value.absent(), }) : content = Value(content); @@ -556,6 +614,8 @@ class TodosTableCompanion extends UpdateCompanion { Expression? title, Expression? content, Expression? targetDate, + Expression? someFloat, + Expression? someInt64, Expression? category, Expression? status, }) { @@ -564,6 +624,8 @@ class TodosTableCompanion extends UpdateCompanion { if (title != null) 'title': title, if (content != null) 'content': content, if (targetDate != null) 'target_date': targetDate, + if (someFloat != null) 'some_float': someFloat, + if (someInt64 != null) 'some_int64': someInt64, if (category != null) 'category': category, if (status != null) 'status': status, }); @@ -574,6 +636,8 @@ class TodosTableCompanion extends UpdateCompanion { Value? title, Value? content, Value? targetDate, + Value? someFloat, + Value? someInt64, Value? category, Value? status}) { return TodosTableCompanion( @@ -581,6 +645,8 @@ class TodosTableCompanion extends UpdateCompanion { title: title ?? this.title, content: content ?? this.content, targetDate: targetDate ?? this.targetDate, + someFloat: someFloat ?? this.someFloat, + someInt64: someInt64 ?? this.someInt64, category: category ?? this.category, status: status ?? this.status, ); @@ -601,6 +667,12 @@ class TodosTableCompanion extends UpdateCompanion { if (targetDate.present) { map['target_date'] = Variable(targetDate.value); } + if (someFloat.present) { + map['some_float'] = Variable(someFloat.value); + } + if (someInt64.present) { + map['some_int64'] = Variable(someInt64.value); + } if (category.present) { map['category'] = Variable(category.value); } @@ -618,6 +690,8 @@ class TodosTableCompanion extends UpdateCompanion { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') + ..write('someFloat: $someFloat, ') + ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status') ..write(')')) @@ -1890,6 +1964,8 @@ abstract class _$TodoDb extends GeneratedDatabase { title: row.readNullable('title'), content: row.read('content'), targetDate: row.readNullable('target_date'), + someFloat: row.readNullable('some_float'), + someInt64: row.readNullable('some_int64'), category: row.readNullable('category'), status: NullAwareTypeConverter.wrapFromSql( $TodosTableTable.$converterstatus, @@ -2070,6 +2146,8 @@ class $$TodosTableTableFilterComposer ColumnFilters get title => ColumnFilters($table.title); ColumnFilters get content => ColumnFilters($table.content); ColumnFilters get targetDate => ColumnFilters($table.targetDate); + ColumnFilters get someFloat => ColumnFilters($table.someFloat); + ColumnFilters get someInt64 => ColumnFilters($table.someInt64); ColumnFilters get categoryId => ColumnFilters($table.category); ComposableFilter category( ComposableFilter Function($$CategoriesTableFilterComposer f) f) { @@ -2096,6 +2174,8 @@ class $$TodosTableTableOrderingComposer ColumnOrderings get title => ColumnOrderings($table.title); ColumnOrderings get content => ColumnOrderings($table.content); ColumnOrderings get targetDate => ColumnOrderings($table.targetDate); + ColumnOrderings get someFloat => ColumnOrderings($table.someFloat); + ColumnOrderings get someInt64 => ColumnOrderings($table.someInt64); ColumnOrderings get categoryId => ColumnOrderings($table.category); ComposableOrdering category( ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { @@ -2130,6 +2210,8 @@ typedef $$TodosTableTableInsertCompanionBuilder = TodosTableCompanion Function({ Value title, required String content, Value targetDate, + Value someFloat, + Value someInt64, Value category, Value status, }); @@ -2138,6 +2220,8 @@ typedef $$TodosTableTableUpdateCompanionBuilder = TodosTableCompanion Function({ Value title, Value content, Value targetDate, + Value someFloat, + Value someInt64, Value category, Value status, }); @@ -2164,6 +2248,8 @@ class $$TodosTableTableTableManager extends RootTableManager< Value title = const Value.absent(), Value content = const Value.absent(), Value targetDate = const Value.absent(), + Value someFloat = const Value.absent(), + Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent(), }) => @@ -2172,6 +2258,8 @@ class $$TodosTableTableTableManager extends RootTableManager< title: title, content: content, targetDate: targetDate, + someFloat: someFloat, + someInt64: someInt64, category: category, status: status, ), @@ -2180,6 +2268,8 @@ class $$TodosTableTableTableManager extends RootTableManager< Value title = const Value.absent(), required String content, Value targetDate = const Value.absent(), + Value someFloat = const Value.absent(), + Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent(), }) => @@ -2188,6 +2278,8 @@ class $$TodosTableTableTableManager extends RootTableManager< title: title, content: content, targetDate: targetDate, + someFloat: someFloat, + someInt64: someInt64, category: category, status: status, ))); @@ -2532,6 +2624,8 @@ class AllTodosWithCategoryResult extends CustomResultSet { final String? title; final String content; final DateTime? targetDate; + final double? someFloat; + final BigInt? someInt64; final int? category; final TodoStatus? status; final RowId catId; @@ -2542,14 +2636,16 @@ class AllTodosWithCategoryResult extends CustomResultSet { this.title, required this.content, this.targetDate, + this.someFloat, + this.someInt64, this.category, this.status, required this.catId, required this.catDesc, }) : super(row); @override - int get hashCode => Object.hash( - id, title, content, targetDate, category, status, catId, catDesc); + int get hashCode => Object.hash(id, title, content, targetDate, someFloat, + someInt64, category, status, catId, catDesc); @override bool operator ==(Object other) => identical(this, other) || @@ -2558,6 +2654,8 @@ class AllTodosWithCategoryResult extends CustomResultSet { other.title == this.title && other.content == this.content && other.targetDate == this.targetDate && + other.someFloat == this.someFloat && + other.someInt64 == this.someInt64 && other.category == this.category && other.status == this.status && other.catId == this.catId && @@ -2569,6 +2667,8 @@ class AllTodosWithCategoryResult extends CustomResultSet { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') + ..write('someFloat: $someFloat, ') + ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status, ') ..write('catId: $catId, ') diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_tests.dart index cccf1770..0a649582 100644 --- a/drift/test/manager/manager_filter_tests.dart +++ b/drift/test/manager/manager_filter_tests.dart @@ -12,5 +12,459 @@ void main() { db = TodoDb(testInMemoryDatabase()); }); + // // Create a dataset for the tests + // schoolId = await db.managers.categories.create((o) => + // o(priority: Value(CategoryPriority.high), description: "School")); + // workId = await db.managers.categories.create( + // (o) => o(priority: Value(CategoryPriority.low), description: "Work")); + // homeId = await db.managers.categories.create((o) => + // o(priority: Value(CategoryPriority.medium), description: "Home")); + // otherId = await db.managers.categories.create( + // (o) => o(priority: Value(CategoryPriority.high), description: "Other")); + + // // School + // await db.managers.todosTable.create((o) => o( + // content: "Get that math homework done", + // title: Value("Math Homework"), + // category: Value(schoolId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1,seconds: 10))))); + // await db.managers.todosTable.create((o) => o( + // content: "Finish that report", + // title: Value("Report"), + // category: Value(workId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2,seconds: 10))))); + // await db.managers.todosTable.create((o) => o( + // content: "Get that english homework done", + // title: Value("English Homework"), + // category: Value(schoolId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1,seconds: 15))))); + // await db.managers.todosTable.create((o) => o( + // content: "Finish that Book report", + // title: Value("Book Report"), + // category: Value(workId), + // status: Value(TodoStatus.done), + // targetDate: Value(DateTime.now().subtract(Duration(days: 2,seconds: 15))))); + + // // Work + // await db.managers.todosTable.create((o) => o( + // content: "File those reports", + // title: Value("File Reports"), + // category: Value(workId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 20))));); + // await db.managers.todosTable.create((o) => o( + // content: "Clean the office", + // title: Value("Clean Office"), + // category: Value(workId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 20))));); + // await db.managers.todosTable.create((o) => o( + // content: "Nail that presentation", + // title: Value("Presentation"), + // category: Value(workId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 25))))); + // await db.managers.todosTable.create((o) => o( + // content: "Take a break", + // title: Value("Break"), + // category: Value(workId), + // status: Value(TodoStatus.done), + // targetDate: Value(DateTime.now().subtract(Duration(days: 2, seconds: 25))))); + + // // Work + // await db.managers.todosTable.create((o) => o( + // content: "Take out the trash", + // title: Value("Trash"), + // category: Value(homeId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 30))));); + // await db.managers.todosTable.create((o) => o( + // content: "Mow the lawn", + // title: Value("Lawn"), + // category: Value(homeId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 30))))); + // await db.managers.todosTable.create((o) => o( + // content: "Fix the sink", + // title: Value("Sink"), + // category: Value(homeId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 35))));); + // await db.managers.todosTable.create((o) => o( + // content: "Paint the walls", + // title: Value("Paint"), + // category: Value(homeId), + // status: Value(TodoStatus.done), + // targetDate: Value(DateTime.now().subtract(Duration(days: 2, seconds: 35))))); + + // // Other + // await db.managers.todosTable.create((o) => o( + // content: "Get groceries", + // title: Value("Groceries"), + // category: Value(otherId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 40))));); + // await db.managers.todosTable.create((o) => o( + // content: "Pick up the kids", + // title: Value("Kids"), + // category: Value(otherId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 40))));); + // await db.managers.todosTable.create((o) => o( + // content: "Take the dog for a walk", + // title: Value("Dog"), + // category: Value(otherId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 45))))); + + // // Items with no category + // await db.managers.todosTable.create((o) => o( + // content: "Get Whiteboard", + // title: Value("Whiteboard"), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 50))));); + // await db.managers.todosTable.create((o) => o( + // content: "Drink Water", + // title: Value("Water"), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 50))))); + // }); + tearDown(() => db.close()); + + test('manager - query generic', () async { + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(5.0), + targetDate: Value(DateTime.now().add(Duration(days: 1))))); + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 2))))); + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(3.0), + targetDate: Value(DateTime.now().add(Duration(days: 3))))); + + // Equals + expect( + db.managers.todosTable.filter((f) => f.someFloat.equals(5.0)).count(), + completion(1)); + expect(db.managers.todosTable.filter((f) => f.someFloat(3.0)).count(), + completion(1)); + // In + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isIn([3.0, 5.0])) + .count(), + completion(2)); + + // Not In + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isNotIn([3.0, 5.0])) + .count(), + completion(0)); + + // Null check + expect(db.managers.todosTable.filter((f) => f.someFloat.isNull()).count(), + completion(1)); + expect( + db.managers.todosTable.filter((f) => f.someFloat.isNotNull()).count(), + completion(2)); + }); + + test('manager - query number', () async { + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(5.0), + targetDate: Value(DateTime.now().add(Duration(days: 1))))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 2))))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(3.0), + targetDate: Value(DateTime.now().add(Duration(days: 3))))); + + // More than + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isBiggerThan(3.0)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isBiggerOrEqualTo(3.0)) + .count(), + completion(2)); + + // Less than + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isSmallerThan(5.0)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isSmallerOrEqualTo(5.0)) + .count(), + completion(2)); + + // Between + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isBetween(3.0, 5.0)) + .count(), + completion(2)); + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isNotBetween(3.0, 5.0)) + .count(), + completion(0)); + }); + + test('manager - query string', () async { + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + )); + await db.managers.todosTable.create((o) => o( + content: "That homework Done", + )); + await db.managers.todosTable.create((o) => o( + content: "that MATH homework", + status: Value(TodoStatus.open), + )); + + // StartsWith + expect( + db.managers.todosTable + .filter((f) => f.content.startsWith("that")) + .count(), + completion(2)); + + // EndsWith + expect( + db.managers.todosTable + .filter((f) => f.content.endsWith("done")) + .count(), + completion(2)); + + // Contains + expect( + db.managers.todosTable + .filter((f) => f.content.contains("math")) + .count(), + completion(2)); + + // Make the database case sensitive + await db.customStatement('PRAGMA case_sensitive_like = ON'); + + // StartsWith + expect( + db.managers.todosTable + .filter((f) => f.content.startsWith("that", caseInsensitive: false)) + .count(), + completion(1)); + + // EndsWith + expect( + db.managers.todosTable + .filter((f) => f.content.endsWith("done", caseInsensitive: false)) + .count(), + completion(1)); + + // Contains + expect( + db.managers.todosTable + .filter((f) => f.content.contains("math", caseInsensitive: false)) + .count(), + completion(1)); + }); + + test('manager - query int64', () async { + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someInt64: Value(BigInt.from(5.0)), + targetDate: Value(DateTime.now().add(Duration(days: 1))))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 2))))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someInt64: Value(BigInt.from(3.0)), + targetDate: Value(DateTime.now().add(Duration(days: 3))))); + + // More than + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isBiggerThan(BigInt.from(3.0))) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isBiggerOrEqualTo(BigInt.from(3.0))) + .count(), + completion(2)); + + // Less than + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isSmallerThan(BigInt.from(5.0))) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isSmallerOrEqualTo(BigInt.from(5.0))) + .count(), + completion(2)); + + // Between + expect( + db.managers.todosTable + .filter((f) => + f.someInt64.isBetween(BigInt.from(3.0), BigInt.from(5.0))) + .count(), + completion(2)); + expect( + db.managers.todosTable + .filter((f) => + f.someInt64.isNotBetween(BigInt.from(3.0), BigInt.from(5.0))) + .count(), + completion(0)); + }); + + test('manager - query bool', () async { + final objId1 = await db.managers.users.create((o) => o( + name: "John Doe", + profilePicture: Uint8List(0), + isAwesome: Value(true), + creationTime: Value(DateTime.now().add(Duration(days: 1))))); + final objId2 = await db.managers.users.create((o) => o( + name: "Jane Doe1", + profilePicture: Uint8List(0), + isAwesome: Value(false), + creationTime: Value(DateTime.now().add(Duration(days: 2))))); + final objId3 = await db.managers.users.create((o) => o( + name: "Jane Doe2", + profilePicture: Uint8List(0), + isAwesome: Value(true), + creationTime: Value(DateTime.now().add(Duration(days: 2))))); + + // False + expect(db.managers.users.filter((f) => f.isAwesome.isFalse()).count(), + completion(1)); + // True + expect(db.managers.users.filter((f) => f.isAwesome.isTrue()).count(), + completion(2)); + }); + + test('manager - query datetime', () async { + final day1 = DateTime.now().add(Duration(days: 1)); + final day2 = DateTime.now().add(Duration(days: 2)); + final day3 = DateTime.now().add(Duration(days: 3)); + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(5.0), + targetDate: Value(day1))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(day2))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(3.0), + targetDate: Value(day3))); + + // More than + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isAfter(day2)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isAfterOrOn(day2)) + .count(), + completion(2)); + + // Less than + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isBefore(day2)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isBeforeOrOn(day2)) + .count(), + completion(2)); + + // Between + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isBetween(day1, day2)) + .count(), + completion(2)); + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isNotBetween(day1, day2)) + .count(), + completion(1)); + }); + + test('manager - query custom column', () async { + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.workInProgress))); + final objId4 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.done))); + + // Equals + expect( + db.managers.todosTable + .filter((f) => f.status.equals(TodoStatus.open)) + .count(), + completion(2)); + expect( + db.managers.todosTable.filter((f) => f.status(TodoStatus.open)).count(), + completion(2)); + + // In + expect( + db.managers.todosTable + .filter((f) => + f.status.isIn([TodoStatus.open, TodoStatus.workInProgress])) + .count(), + completion(3)); + + // Not In + expect( + db.managers.todosTable + .filter((f) => + f.status.isNotIn([TodoStatus.open, TodoStatus.workInProgress])) + .count(), + completion(1)); + }); } From 528b1bc917e5f91aa3f64c6413dc317073016cf1 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Thu, 11 Apr 2024 15:09:42 -0400 Subject: [PATCH 53/74] nits --- drift/test/manager/manager_filter_tests.dart | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_tests.dart index 0a649582..7af64365 100644 --- a/drift/test/manager/manager_filter_tests.dart +++ b/drift/test/manager/manager_filter_tests.dart @@ -180,16 +180,16 @@ void main() { }); test('manager - query number', () async { - final objId1 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), someFloat: Value(5.0), targetDate: Value(DateTime.now().add(Duration(days: 1))))); - final objId2 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), targetDate: Value(DateTime.now().add(Duration(days: 2))))); - final objId3 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), someFloat: Value(3.0), @@ -292,16 +292,16 @@ void main() { }); test('manager - query int64', () async { - final objId1 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), someInt64: Value(BigInt.from(5.0)), targetDate: Value(DateTime.now().add(Duration(days: 1))))); - final objId2 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), targetDate: Value(DateTime.now().add(Duration(days: 2))))); - final objId3 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), someInt64: Value(BigInt.from(3.0)), @@ -347,17 +347,17 @@ void main() { }); test('manager - query bool', () async { - final objId1 = await db.managers.users.create((o) => o( + await db.managers.users.create((o) => o( name: "John Doe", profilePicture: Uint8List(0), isAwesome: Value(true), creationTime: Value(DateTime.now().add(Duration(days: 1))))); - final objId2 = await db.managers.users.create((o) => o( + await db.managers.users.create((o) => o( name: "Jane Doe1", profilePicture: Uint8List(0), isAwesome: Value(false), creationTime: Value(DateTime.now().add(Duration(days: 2))))); - final objId3 = await db.managers.users.create((o) => o( + await db.managers.users.create((o) => o( name: "Jane Doe2", profilePicture: Uint8List(0), isAwesome: Value(true), @@ -375,16 +375,16 @@ void main() { final day1 = DateTime.now().add(Duration(days: 1)); final day2 = DateTime.now().add(Duration(days: 2)); final day3 = DateTime.now().add(Duration(days: 3)); - final objId1 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), someFloat: Value(5.0), targetDate: Value(day1))); - final objId2 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), targetDate: Value(day2))); - final objId3 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open), someFloat: Value(3.0), @@ -428,16 +428,16 @@ void main() { }); test('manager - query custom column', () async { - final objId1 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open))); - final objId2 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.open))); - final objId3 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.workInProgress))); - final objId4 = await db.managers.todosTable.create((o) => o( + await db.managers.todosTable.create((o) => o( content: "Get that math homework done", status: Value(TodoStatus.done))); From 143dd3a819f7f381042ed4244f2a4f8676b627a5 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Thu, 11 Apr 2024 15:19:25 -0400 Subject: [PATCH 54/74] docs --- drift/lib/src/runtime/manager/filter.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index eaf9181c..26e17e2d 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -77,11 +77,12 @@ extension StringFilters on ColumnFilters { /// Create a filter to check if the this text column contains a substring /// {@template drift.manager.likeCaseSensitivity} - /// Sqlite performs a case insensitive text search by default /// - /// If you have set the database to use case sensitive in like expressions, you can - /// pass [caseInsensitive] as false to perform a case sensitive search - /// See https://www.sqlitetutorial.net/sqlite-like/ for more information + /// Setting [caseInsensitive] to false will have no effect unless the database in configured to use + /// case sensitive like expressions. + /// + /// See https://www.sqlitetutorial.net/sqlite-like/ for more information on how + /// to the like expression works. /// {@endtemplate} ComposableFilter contains(T value, {bool caseInsensitive = true}) { return ComposableFilter( From 14335b5d3d8afbb81e1e94a47bb8c4bf65ea5a3a Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 16 Apr 2024 11:38:17 -0400 Subject: [PATCH 55/74] add new table with every type of column for tests --- drift/test/generated/todos.dart | 20 +- drift/test/generated/todos.g.dart | 797 ++++++++++++++++--- drift/test/manager/manager_filter_tests.dart | 430 +++++----- 3 files changed, 930 insertions(+), 317 deletions(-) diff --git a/drift/test/generated/todos.dart b/drift/test/generated/todos.dart index 0b0a6c28..3e4ba9af 100644 --- a/drift/test/generated/todos.dart +++ b/drift/test/generated/todos.dart @@ -24,9 +24,6 @@ class TodosTable extends Table with AutoIncrement { @JsonKey('target_date') DateTimeColumn get targetDate => dateTime().nullable().unique()(); - RealColumn get someFloat => real().nullable()(); - Int64Column get someInt64 => int64().nullable()(); - IntColumn get category => integer().references(Categories, #id).nullable()(); TextColumn get status => textEnum().nullable()(); @@ -90,6 +87,22 @@ class TableWithoutPK extends Table { text().map(const CustomConverter()).clientDefault(_uuid.v4)(); } +class TableWithEveryColumnType extends Table with AutoIncrement { + BoolColumn get aBool => boolean().nullable()(); + DateTimeColumn get aDateTime => dateTime().nullable()(); + TextColumn get aText => text().nullable()(); + IntColumn get anInt => integer().nullable()(); + Int64Column get anInt64 => int64().nullable()(); + RealColumn get aReal => real().nullable()(); + BlobColumn get aBlob => blob().nullable()(); + IntColumn get anIntEnum => intEnum().nullable()(); + TextColumn get aTextWithConverter => text() + .named('insert') + .map(const CustomJsonConverter()) + .nullable() + .nullable()(); +} + class CustomRowClass { final int notReallyAnId; final double anotherName; @@ -251,6 +264,7 @@ const uuidType = DialectAwareSqlType.via( TableWithoutPK, PureDefaults, WithCustomType, + TableWithEveryColumnType ], views: [ CategoryTodoCountView, diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 154b5be7..233e26b1 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -305,18 +305,6 @@ class $TodosTableTable extends TodosTable type: DriftSqlType.dateTime, requiredDuringInsert: false, defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE')); - static const VerificationMeta _someFloatMeta = - const VerificationMeta('someFloat'); - @override - late final GeneratedColumn someFloat = GeneratedColumn( - 'some_float', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - static const VerificationMeta _someInt64Meta = - const VerificationMeta('someInt64'); - @override - late final GeneratedColumn someInt64 = GeneratedColumn( - 'some_int64', aliasedName, true, - type: DriftSqlType.bigInt, requiredDuringInsert: false); static const VerificationMeta _categoryMeta = const VerificationMeta('category'); @override @@ -334,7 +322,7 @@ class $TodosTableTable extends TodosTable .withConverter($TodosTableTable.$converterstatusn); @override List get $columns => - [id, title, content, targetDate, someFloat, someInt64, category, status]; + [id, title, content, targetDate, category, status]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -362,14 +350,6 @@ class $TodosTableTable extends TodosTable targetDate.isAcceptableOrUnknown( data['target_date']!, _targetDateMeta)); } - if (data.containsKey('some_float')) { - context.handle(_someFloatMeta, - someFloat.isAcceptableOrUnknown(data['some_float']!, _someFloatMeta)); - } - if (data.containsKey('some_int64')) { - context.handle(_someInt64Meta, - someInt64.isAcceptableOrUnknown(data['some_int64']!, _someInt64Meta)); - } if (data.containsKey('category')) { context.handle(_categoryMeta, category.isAcceptableOrUnknown(data['category']!, _categoryMeta)); @@ -397,10 +377,6 @@ class $TodosTableTable extends TodosTable .read(DriftSqlType.string, data['${effectivePrefix}content'])!, targetDate: attachedDatabase.typeMapping .read(DriftSqlType.dateTime, data['${effectivePrefix}target_date']), - someFloat: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}some_float']), - someInt64: attachedDatabase.typeMapping - .read(DriftSqlType.bigInt, data['${effectivePrefix}some_int64']), category: attachedDatabase.typeMapping .read(DriftSqlType.int, data['${effectivePrefix}category']), status: $TodosTableTable.$converterstatusn.fromSql(attachedDatabase @@ -427,8 +403,6 @@ class TodoEntry extends DataClass implements Insertable { final String? title; final String content; final DateTime? targetDate; - final double? someFloat; - final BigInt? someInt64; final int? category; final TodoStatus? status; const TodoEntry( @@ -436,8 +410,6 @@ class TodoEntry extends DataClass implements Insertable { this.title, required this.content, this.targetDate, - this.someFloat, - this.someInt64, this.category, this.status}); @override @@ -453,12 +425,6 @@ class TodoEntry extends DataClass implements Insertable { if (!nullToAbsent || targetDate != null) { map['target_date'] = Variable(targetDate); } - if (!nullToAbsent || someFloat != null) { - map['some_float'] = Variable(someFloat); - } - if (!nullToAbsent || someInt64 != null) { - map['some_int64'] = Variable(someInt64); - } if (!nullToAbsent || category != null) { map['category'] = Variable(category); } @@ -478,12 +444,6 @@ class TodoEntry extends DataClass implements Insertable { targetDate: targetDate == null && nullToAbsent ? const Value.absent() : Value(targetDate), - someFloat: someFloat == null && nullToAbsent - ? const Value.absent() - : Value(someFloat), - someInt64: someInt64 == null && nullToAbsent - ? const Value.absent() - : Value(someInt64), category: category == null && nullToAbsent ? const Value.absent() : Value(category), @@ -501,8 +461,6 @@ class TodoEntry extends DataClass implements Insertable { title: serializer.fromJson(json['title']), content: serializer.fromJson(json['content']), targetDate: serializer.fromJson(json['target_date']), - someFloat: serializer.fromJson(json['someFloat']), - someInt64: serializer.fromJson(json['someInt64']), category: serializer.fromJson(json['category']), status: $TodosTableTable.$converterstatusn .fromJson(serializer.fromJson(json['status'])), @@ -521,8 +479,6 @@ class TodoEntry extends DataClass implements Insertable { 'title': serializer.toJson(title), 'content': serializer.toJson(content), 'target_date': serializer.toJson(targetDate), - 'someFloat': serializer.toJson(someFloat), - 'someInt64': serializer.toJson(someInt64), 'category': serializer.toJson(category), 'status': serializer .toJson($TodosTableTable.$converterstatusn.toJson(status)), @@ -534,8 +490,6 @@ class TodoEntry extends DataClass implements Insertable { Value title = const Value.absent(), String? content, Value targetDate = const Value.absent(), - Value someFloat = const Value.absent(), - Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent()}) => TodoEntry( @@ -543,8 +497,6 @@ class TodoEntry extends DataClass implements Insertable { title: title.present ? title.value : this.title, content: content ?? this.content, targetDate: targetDate.present ? targetDate.value : this.targetDate, - someFloat: someFloat.present ? someFloat.value : this.someFloat, - someInt64: someInt64.present ? someInt64.value : this.someInt64, category: category.present ? category.value : this.category, status: status.present ? status.value : this.status, ); @@ -555,8 +507,6 @@ class TodoEntry extends DataClass implements Insertable { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') - ..write('someFloat: $someFloat, ') - ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status') ..write(')')) @@ -564,8 +514,8 @@ class TodoEntry extends DataClass implements Insertable { } @override - int get hashCode => Object.hash( - id, title, content, targetDate, someFloat, someInt64, category, status); + int get hashCode => + Object.hash(id, title, content, targetDate, category, status); @override bool operator ==(Object other) => identical(this, other) || @@ -574,8 +524,6 @@ class TodoEntry extends DataClass implements Insertable { other.title == this.title && other.content == this.content && other.targetDate == this.targetDate && - other.someFloat == this.someFloat && - other.someInt64 == this.someInt64 && other.category == this.category && other.status == this.status); } @@ -585,8 +533,6 @@ class TodosTableCompanion extends UpdateCompanion { final Value title; final Value content; final Value targetDate; - final Value someFloat; - final Value someInt64; final Value category; final Value status; const TodosTableCompanion({ @@ -594,8 +540,6 @@ class TodosTableCompanion extends UpdateCompanion { this.title = const Value.absent(), this.content = const Value.absent(), this.targetDate = const Value.absent(), - this.someFloat = const Value.absent(), - this.someInt64 = const Value.absent(), this.category = const Value.absent(), this.status = const Value.absent(), }); @@ -604,8 +548,6 @@ class TodosTableCompanion extends UpdateCompanion { this.title = const Value.absent(), required String content, this.targetDate = const Value.absent(), - this.someFloat = const Value.absent(), - this.someInt64 = const Value.absent(), this.category = const Value.absent(), this.status = const Value.absent(), }) : content = Value(content); @@ -614,8 +556,6 @@ class TodosTableCompanion extends UpdateCompanion { Expression? title, Expression? content, Expression? targetDate, - Expression? someFloat, - Expression? someInt64, Expression? category, Expression? status, }) { @@ -624,8 +564,6 @@ class TodosTableCompanion extends UpdateCompanion { if (title != null) 'title': title, if (content != null) 'content': content, if (targetDate != null) 'target_date': targetDate, - if (someFloat != null) 'some_float': someFloat, - if (someInt64 != null) 'some_int64': someInt64, if (category != null) 'category': category, if (status != null) 'status': status, }); @@ -636,8 +574,6 @@ class TodosTableCompanion extends UpdateCompanion { Value? title, Value? content, Value? targetDate, - Value? someFloat, - Value? someInt64, Value? category, Value? status}) { return TodosTableCompanion( @@ -645,8 +581,6 @@ class TodosTableCompanion extends UpdateCompanion { title: title ?? this.title, content: content ?? this.content, targetDate: targetDate ?? this.targetDate, - someFloat: someFloat ?? this.someFloat, - someInt64: someInt64 ?? this.someInt64, category: category ?? this.category, status: status ?? this.status, ); @@ -667,12 +601,6 @@ class TodosTableCompanion extends UpdateCompanion { if (targetDate.present) { map['target_date'] = Variable(targetDate.value); } - if (someFloat.present) { - map['some_float'] = Variable(someFloat.value); - } - if (someInt64.present) { - map['some_int64'] = Variable(someInt64.value); - } if (category.present) { map['category'] = Variable(category.value); } @@ -690,8 +618,6 @@ class TodosTableCompanion extends UpdateCompanion { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') - ..write('someFloat: $someFloat, ') - ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status') ..write(')')) @@ -1714,6 +1640,538 @@ class WithCustomTypeCompanion extends UpdateCompanion { } } +class $TableWithEveryColumnTypeTable extends TableWithEveryColumnType + with + TableInfo<$TableWithEveryColumnTypeTable, + TableWithEveryColumnTypeData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TableWithEveryColumnTypeTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumnWithTypeConverter id = GeneratedColumn< + int>('id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')) + .withConverter($TableWithEveryColumnTypeTable.$converterid); + static const VerificationMeta _aBoolMeta = const VerificationMeta('aBool'); + @override + late final GeneratedColumn aBool = GeneratedColumn( + 'a_bool', aliasedName, true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("a_bool" IN (0, 1))')); + static const VerificationMeta _aDateTimeMeta = + const VerificationMeta('aDateTime'); + @override + late final GeneratedColumn aDateTime = GeneratedColumn( + 'a_date_time', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + static const VerificationMeta _aTextMeta = const VerificationMeta('aText'); + @override + late final GeneratedColumn aText = GeneratedColumn( + 'a_text', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + static const VerificationMeta _anIntMeta = const VerificationMeta('anInt'); + @override + late final GeneratedColumn anInt = GeneratedColumn( + 'an_int', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + static const VerificationMeta _anInt64Meta = + const VerificationMeta('anInt64'); + @override + late final GeneratedColumn anInt64 = GeneratedColumn( + 'an_int64', aliasedName, true, + type: DriftSqlType.bigInt, requiredDuringInsert: false); + static const VerificationMeta _aRealMeta = const VerificationMeta('aReal'); + @override + late final GeneratedColumn aReal = GeneratedColumn( + 'a_real', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + static const VerificationMeta _aBlobMeta = const VerificationMeta('aBlob'); + @override + late final GeneratedColumn aBlob = GeneratedColumn( + 'a_blob', aliasedName, true, + type: DriftSqlType.blob, requiredDuringInsert: false); + static const VerificationMeta _anIntEnumMeta = + const VerificationMeta('anIntEnum'); + @override + late final GeneratedColumnWithTypeConverter anIntEnum = + GeneratedColumn('an_int_enum', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false) + .withConverter( + $TableWithEveryColumnTypeTable.$converteranIntEnumn); + static const VerificationMeta _aTextWithConverterMeta = + const VerificationMeta('aTextWithConverter'); + @override + late final GeneratedColumnWithTypeConverter + aTextWithConverter = GeneratedColumn('insert', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false) + .withConverter( + $TableWithEveryColumnTypeTable.$converteraTextWithConvertern); + @override + List get $columns => [ + id, + aBool, + aDateTime, + aText, + anInt, + anInt64, + aReal, + aBlob, + anIntEnum, + aTextWithConverter + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'table_with_every_column_type'; + @override + VerificationContext validateIntegrity( + Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + context.handle(_idMeta, const VerificationResult.success()); + if (data.containsKey('a_bool')) { + context.handle( + _aBoolMeta, aBool.isAcceptableOrUnknown(data['a_bool']!, _aBoolMeta)); + } + if (data.containsKey('a_date_time')) { + context.handle( + _aDateTimeMeta, + aDateTime.isAcceptableOrUnknown( + data['a_date_time']!, _aDateTimeMeta)); + } + if (data.containsKey('a_text')) { + context.handle( + _aTextMeta, aText.isAcceptableOrUnknown(data['a_text']!, _aTextMeta)); + } + if (data.containsKey('an_int')) { + context.handle( + _anIntMeta, anInt.isAcceptableOrUnknown(data['an_int']!, _anIntMeta)); + } + if (data.containsKey('an_int64')) { + context.handle(_anInt64Meta, + anInt64.isAcceptableOrUnknown(data['an_int64']!, _anInt64Meta)); + } + if (data.containsKey('a_real')) { + context.handle( + _aRealMeta, aReal.isAcceptableOrUnknown(data['a_real']!, _aRealMeta)); + } + if (data.containsKey('a_blob')) { + context.handle( + _aBlobMeta, aBlob.isAcceptableOrUnknown(data['a_blob']!, _aBlobMeta)); + } + context.handle(_anIntEnumMeta, const VerificationResult.success()); + context.handle(_aTextWithConverterMeta, const VerificationResult.success()); + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TableWithEveryColumnTypeData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TableWithEveryColumnTypeData( + id: $TableWithEveryColumnTypeTable.$converterid.fromSql(attachedDatabase + .typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!), + aBool: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}a_bool']), + aDateTime: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}a_date_time']), + aText: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}a_text']), + anInt: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}an_int']), + anInt64: attachedDatabase.typeMapping + .read(DriftSqlType.bigInt, data['${effectivePrefix}an_int64']), + aReal: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}a_real']), + aBlob: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}a_blob']), + anIntEnum: $TableWithEveryColumnTypeTable.$converteranIntEnumn.fromSql( + attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}an_int_enum'])), + aTextWithConverter: $TableWithEveryColumnTypeTable + .$converteraTextWithConvertern + .fromSql(attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}insert'])), + ); + } + + @override + $TableWithEveryColumnTypeTable createAlias(String alias) { + return $TableWithEveryColumnTypeTable(attachedDatabase, alias); + } + + static JsonTypeConverter2 $converterid = + TypeConverter.extensionType(); + static JsonTypeConverter2 $converteranIntEnum = + const EnumIndexConverter(TodoStatus.values); + static JsonTypeConverter2 $converteranIntEnumn = + JsonTypeConverter2.asNullable($converteranIntEnum); + static JsonTypeConverter2> + $converteraTextWithConverter = const CustomJsonConverter(); + static JsonTypeConverter2?> + $converteraTextWithConvertern = + JsonTypeConverter2.asNullable($converteraTextWithConverter); +} + +class TableWithEveryColumnTypeData extends DataClass + implements Insertable { + final RowId id; + final bool? aBool; + final DateTime? aDateTime; + final String? aText; + final int? anInt; + final BigInt? anInt64; + final double? aReal; + final Uint8List? aBlob; + final TodoStatus? anIntEnum; + final MyCustomObject? aTextWithConverter; + const TableWithEveryColumnTypeData( + {required this.id, + this.aBool, + this.aDateTime, + this.aText, + this.anInt, + this.anInt64, + this.aReal, + this.aBlob, + this.anIntEnum, + this.aTextWithConverter}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + { + map['id'] = + Variable($TableWithEveryColumnTypeTable.$converterid.toSql(id)); + } + if (!nullToAbsent || aBool != null) { + map['a_bool'] = Variable(aBool); + } + if (!nullToAbsent || aDateTime != null) { + map['a_date_time'] = Variable(aDateTime); + } + if (!nullToAbsent || aText != null) { + map['a_text'] = Variable(aText); + } + if (!nullToAbsent || anInt != null) { + map['an_int'] = Variable(anInt); + } + if (!nullToAbsent || anInt64 != null) { + map['an_int64'] = Variable(anInt64); + } + if (!nullToAbsent || aReal != null) { + map['a_real'] = Variable(aReal); + } + if (!nullToAbsent || aBlob != null) { + map['a_blob'] = Variable(aBlob); + } + if (!nullToAbsent || anIntEnum != null) { + map['an_int_enum'] = Variable( + $TableWithEveryColumnTypeTable.$converteranIntEnumn.toSql(anIntEnum)); + } + if (!nullToAbsent || aTextWithConverter != null) { + map['insert'] = Variable($TableWithEveryColumnTypeTable + .$converteraTextWithConvertern + .toSql(aTextWithConverter)); + } + return map; + } + + TableWithEveryColumnTypeCompanion toCompanion(bool nullToAbsent) { + return TableWithEveryColumnTypeCompanion( + id: Value(id), + aBool: + aBool == null && nullToAbsent ? const Value.absent() : Value(aBool), + aDateTime: aDateTime == null && nullToAbsent + ? const Value.absent() + : Value(aDateTime), + aText: + aText == null && nullToAbsent ? const Value.absent() : Value(aText), + anInt: + anInt == null && nullToAbsent ? const Value.absent() : Value(anInt), + anInt64: anInt64 == null && nullToAbsent + ? const Value.absent() + : Value(anInt64), + aReal: + aReal == null && nullToAbsent ? const Value.absent() : Value(aReal), + aBlob: + aBlob == null && nullToAbsent ? const Value.absent() : Value(aBlob), + anIntEnum: anIntEnum == null && nullToAbsent + ? const Value.absent() + : Value(anIntEnum), + aTextWithConverter: aTextWithConverter == null && nullToAbsent + ? const Value.absent() + : Value(aTextWithConverter), + ); + } + + factory TableWithEveryColumnTypeData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TableWithEveryColumnTypeData( + id: $TableWithEveryColumnTypeTable.$converterid + .fromJson(serializer.fromJson(json['id'])), + aBool: serializer.fromJson(json['aBool']), + aDateTime: serializer.fromJson(json['aDateTime']), + aText: serializer.fromJson(json['aText']), + anInt: serializer.fromJson(json['anInt']), + anInt64: serializer.fromJson(json['anInt64']), + aReal: serializer.fromJson(json['aReal']), + aBlob: serializer.fromJson(json['aBlob']), + anIntEnum: $TableWithEveryColumnTypeTable.$converteranIntEnumn + .fromJson(serializer.fromJson(json['anIntEnum'])), + aTextWithConverter: $TableWithEveryColumnTypeTable + .$converteraTextWithConvertern + .fromJson(serializer + .fromJson?>(json['aTextWithConverter'])), + ); + } + factory TableWithEveryColumnTypeData.fromJsonString(String encodedJson, + {ValueSerializer? serializer}) => + TableWithEveryColumnTypeData.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer + .toJson($TableWithEveryColumnTypeTable.$converterid.toJson(id)), + 'aBool': serializer.toJson(aBool), + 'aDateTime': serializer.toJson(aDateTime), + 'aText': serializer.toJson(aText), + 'anInt': serializer.toJson(anInt), + 'anInt64': serializer.toJson(anInt64), + 'aReal': serializer.toJson(aReal), + 'aBlob': serializer.toJson(aBlob), + 'anIntEnum': serializer.toJson($TableWithEveryColumnTypeTable + .$converteranIntEnumn + .toJson(anIntEnum)), + 'aTextWithConverter': serializer.toJson?>( + $TableWithEveryColumnTypeTable.$converteraTextWithConvertern + .toJson(aTextWithConverter)), + }; + } + + TableWithEveryColumnTypeData copyWith( + {RowId? id, + Value aBool = const Value.absent(), + Value aDateTime = const Value.absent(), + Value aText = const Value.absent(), + Value anInt = const Value.absent(), + Value anInt64 = const Value.absent(), + Value aReal = const Value.absent(), + Value aBlob = const Value.absent(), + Value anIntEnum = const Value.absent(), + Value aTextWithConverter = const Value.absent()}) => + TableWithEveryColumnTypeData( + id: id ?? this.id, + aBool: aBool.present ? aBool.value : this.aBool, + aDateTime: aDateTime.present ? aDateTime.value : this.aDateTime, + aText: aText.present ? aText.value : this.aText, + anInt: anInt.present ? anInt.value : this.anInt, + anInt64: anInt64.present ? anInt64.value : this.anInt64, + aReal: aReal.present ? aReal.value : this.aReal, + aBlob: aBlob.present ? aBlob.value : this.aBlob, + anIntEnum: anIntEnum.present ? anIntEnum.value : this.anIntEnum, + aTextWithConverter: aTextWithConverter.present + ? aTextWithConverter.value + : this.aTextWithConverter, + ); + @override + String toString() { + return (StringBuffer('TableWithEveryColumnTypeData(') + ..write('id: $id, ') + ..write('aBool: $aBool, ') + ..write('aDateTime: $aDateTime, ') + ..write('aText: $aText, ') + ..write('anInt: $anInt, ') + ..write('anInt64: $anInt64, ') + ..write('aReal: $aReal, ') + ..write('aBlob: $aBlob, ') + ..write('anIntEnum: $anIntEnum, ') + ..write('aTextWithConverter: $aTextWithConverter') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, aBool, aDateTime, aText, anInt, anInt64, + aReal, $driftBlobEquality.hash(aBlob), anIntEnum, aTextWithConverter); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TableWithEveryColumnTypeData && + other.id == this.id && + other.aBool == this.aBool && + other.aDateTime == this.aDateTime && + other.aText == this.aText && + other.anInt == this.anInt && + other.anInt64 == this.anInt64 && + other.aReal == this.aReal && + $driftBlobEquality.equals(other.aBlob, this.aBlob) && + other.anIntEnum == this.anIntEnum && + other.aTextWithConverter == this.aTextWithConverter); +} + +class TableWithEveryColumnTypeCompanion + extends UpdateCompanion { + final Value id; + final Value aBool; + final Value aDateTime; + final Value aText; + final Value anInt; + final Value anInt64; + final Value aReal; + final Value aBlob; + final Value anIntEnum; + final Value aTextWithConverter; + const TableWithEveryColumnTypeCompanion({ + this.id = const Value.absent(), + this.aBool = const Value.absent(), + this.aDateTime = const Value.absent(), + this.aText = const Value.absent(), + this.anInt = const Value.absent(), + this.anInt64 = const Value.absent(), + this.aReal = const Value.absent(), + this.aBlob = const Value.absent(), + this.anIntEnum = const Value.absent(), + this.aTextWithConverter = const Value.absent(), + }); + TableWithEveryColumnTypeCompanion.insert({ + this.id = const Value.absent(), + this.aBool = const Value.absent(), + this.aDateTime = const Value.absent(), + this.aText = const Value.absent(), + this.anInt = const Value.absent(), + this.anInt64 = const Value.absent(), + this.aReal = const Value.absent(), + this.aBlob = const Value.absent(), + this.anIntEnum = const Value.absent(), + this.aTextWithConverter = const Value.absent(), + }); + static Insertable custom({ + Expression? id, + Expression? aBool, + Expression? aDateTime, + Expression? aText, + Expression? anInt, + Expression? anInt64, + Expression? aReal, + Expression? aBlob, + Expression? anIntEnum, + Expression? aTextWithConverter, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (aBool != null) 'a_bool': aBool, + if (aDateTime != null) 'a_date_time': aDateTime, + if (aText != null) 'a_text': aText, + if (anInt != null) 'an_int': anInt, + if (anInt64 != null) 'an_int64': anInt64, + if (aReal != null) 'a_real': aReal, + if (aBlob != null) 'a_blob': aBlob, + if (anIntEnum != null) 'an_int_enum': anIntEnum, + if (aTextWithConverter != null) 'insert': aTextWithConverter, + }); + } + + TableWithEveryColumnTypeCompanion copyWith( + {Value? id, + Value? aBool, + Value? aDateTime, + Value? aText, + Value? anInt, + Value? anInt64, + Value? aReal, + Value? aBlob, + Value? anIntEnum, + Value? aTextWithConverter}) { + return TableWithEveryColumnTypeCompanion( + id: id ?? this.id, + aBool: aBool ?? this.aBool, + aDateTime: aDateTime ?? this.aDateTime, + aText: aText ?? this.aText, + anInt: anInt ?? this.anInt, + anInt64: anInt64 ?? this.anInt64, + aReal: aReal ?? this.aReal, + aBlob: aBlob ?? this.aBlob, + anIntEnum: anIntEnum ?? this.anIntEnum, + aTextWithConverter: aTextWithConverter ?? this.aTextWithConverter, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable( + $TableWithEveryColumnTypeTable.$converterid.toSql(id.value)); + } + if (aBool.present) { + map['a_bool'] = Variable(aBool.value); + } + if (aDateTime.present) { + map['a_date_time'] = Variable(aDateTime.value); + } + if (aText.present) { + map['a_text'] = Variable(aText.value); + } + if (anInt.present) { + map['an_int'] = Variable(anInt.value); + } + if (anInt64.present) { + map['an_int64'] = Variable(anInt64.value); + } + if (aReal.present) { + map['a_real'] = Variable(aReal.value); + } + if (aBlob.present) { + map['a_blob'] = Variable(aBlob.value); + } + if (anIntEnum.present) { + map['an_int_enum'] = Variable($TableWithEveryColumnTypeTable + .$converteranIntEnumn + .toSql(anIntEnum.value)); + } + if (aTextWithConverter.present) { + map['insert'] = Variable($TableWithEveryColumnTypeTable + .$converteraTextWithConvertern + .toSql(aTextWithConverter.value)); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TableWithEveryColumnTypeCompanion(') + ..write('id: $id, ') + ..write('aBool: $aBool, ') + ..write('aDateTime: $aDateTime, ') + ..write('aText: $aText, ') + ..write('anInt: $anInt, ') + ..write('anInt64: $anInt64, ') + ..write('aReal: $aReal, ') + ..write('aBlob: $aBlob, ') + ..write('anIntEnum: $anIntEnum, ') + ..write('aTextWithConverter: $aTextWithConverter') + ..write(')')) + .toString(); + } +} + class CategoryTodoCountViewData extends DataClass { final int? categoryId; final String? description; @@ -1946,6 +2404,8 @@ abstract class _$TodoDb extends GeneratedDatabase { late final $TableWithoutPKTable tableWithoutPK = $TableWithoutPKTable(this); late final $PureDefaultsTable pureDefaults = $PureDefaultsTable(this); late final $WithCustomTypeTable withCustomType = $WithCustomTypeTable(this); + late final $TableWithEveryColumnTypeTable tableWithEveryColumnType = + $TableWithEveryColumnTypeTable(this); late final $CategoryTodoCountViewView categoryTodoCountView = $CategoryTodoCountViewView(this); late final $TodoWithCategoryViewView todoWithCategoryView = @@ -1964,8 +2424,6 @@ abstract class _$TodoDb extends GeneratedDatabase { title: row.readNullable('title'), content: row.read('content'), targetDate: row.readNullable('target_date'), - someFloat: row.readNullable('some_float'), - someInt64: row.readNullable('some_int64'), category: row.readNullable('category'), status: NullAwareTypeConverter.wrapFromSql( $TodosTableTable.$converterstatus, @@ -2034,6 +2492,7 @@ abstract class _$TodoDb extends GeneratedDatabase { tableWithoutPK, pureDefaults, withCustomType, + tableWithEveryColumnType, categoryTodoCountView, todoWithCategoryView ]; @@ -2146,8 +2605,6 @@ class $$TodosTableTableFilterComposer ColumnFilters get title => ColumnFilters($table.title); ColumnFilters get content => ColumnFilters($table.content); ColumnFilters get targetDate => ColumnFilters($table.targetDate); - ColumnFilters get someFloat => ColumnFilters($table.someFloat); - ColumnFilters get someInt64 => ColumnFilters($table.someInt64); ColumnFilters get categoryId => ColumnFilters($table.category); ComposableFilter category( ComposableFilter Function($$CategoriesTableFilterComposer f) f) { @@ -2174,8 +2631,6 @@ class $$TodosTableTableOrderingComposer ColumnOrderings get title => ColumnOrderings($table.title); ColumnOrderings get content => ColumnOrderings($table.content); ColumnOrderings get targetDate => ColumnOrderings($table.targetDate); - ColumnOrderings get someFloat => ColumnOrderings($table.someFloat); - ColumnOrderings get someInt64 => ColumnOrderings($table.someInt64); ColumnOrderings get categoryId => ColumnOrderings($table.category); ComposableOrdering category( ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { @@ -2210,8 +2665,6 @@ typedef $$TodosTableTableInsertCompanionBuilder = TodosTableCompanion Function({ Value title, required String content, Value targetDate, - Value someFloat, - Value someInt64, Value category, Value status, }); @@ -2220,8 +2673,6 @@ typedef $$TodosTableTableUpdateCompanionBuilder = TodosTableCompanion Function({ Value title, Value content, Value targetDate, - Value someFloat, - Value someInt64, Value category, Value status, }); @@ -2248,8 +2699,6 @@ class $$TodosTableTableTableManager extends RootTableManager< Value title = const Value.absent(), Value content = const Value.absent(), Value targetDate = const Value.absent(), - Value someFloat = const Value.absent(), - Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent(), }) => @@ -2258,8 +2707,6 @@ class $$TodosTableTableTableManager extends RootTableManager< title: title, content: content, targetDate: targetDate, - someFloat: someFloat, - someInt64: someInt64, category: category, status: status, ), @@ -2268,8 +2715,6 @@ class $$TodosTableTableTableManager extends RootTableManager< Value title = const Value.absent(), required String content, Value targetDate = const Value.absent(), - Value someFloat = const Value.absent(), - Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent(), }) => @@ -2278,8 +2723,6 @@ class $$TodosTableTableTableManager extends RootTableManager< title: title, content: content, targetDate: targetDate, - someFloat: someFloat, - someInt64: someInt64, category: category, status: status, ))); @@ -2602,6 +3045,155 @@ class $$WithCustomTypeTableTableManager extends RootTableManager< ))); } +class $$TableWithEveryColumnTypeTableFilterComposer + extends FilterComposer<_$TodoDb, $TableWithEveryColumnTypeTable> { + $$TableWithEveryColumnTypeTableFilterComposer(super.db, super.table); + ColumnFilters get idValue => ColumnFilters($table.id); + ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters($table.id); + ColumnFilters get aBool => ColumnFilters($table.aBool); + ColumnFilters get aDateTime => ColumnFilters($table.aDateTime); + ColumnFilters get aText => ColumnFilters($table.aText); + ColumnFilters get anInt => ColumnFilters($table.anInt); + ColumnFilters get anInt64 => ColumnFilters($table.anInt64); + ColumnFilters get aReal => ColumnFilters($table.aReal); + ColumnFilters get aBlob => ColumnFilters($table.aBlob); + ColumnFilters get anIntEnumValue => ColumnFilters($table.anIntEnum); + ColumnWithTypeConverterFilters get anIntEnum => + ColumnWithTypeConverterFilters($table.anIntEnum); + ColumnFilters get aTextWithConverterValue => + ColumnFilters($table.aTextWithConverter); + ColumnWithTypeConverterFilters + get aTextWithConverter => + ColumnWithTypeConverterFilters($table.aTextWithConverter); +} + +class $$TableWithEveryColumnTypeTableOrderingComposer + extends OrderingComposer<_$TodoDb, $TableWithEveryColumnTypeTable> { + $$TableWithEveryColumnTypeTableOrderingComposer(super.db, super.table); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get aBool => ColumnOrderings($table.aBool); + ColumnOrderings get aDateTime => ColumnOrderings($table.aDateTime); + ColumnOrderings get aText => ColumnOrderings($table.aText); + ColumnOrderings get anInt => ColumnOrderings($table.anInt); + ColumnOrderings get anInt64 => ColumnOrderings($table.anInt64); + ColumnOrderings get aReal => ColumnOrderings($table.aReal); + ColumnOrderings get aBlob => ColumnOrderings($table.aBlob); + ColumnOrderings get anIntEnum => ColumnOrderings($table.anIntEnum); + ColumnOrderings get aTextWithConverter => + ColumnOrderings($table.aTextWithConverter); +} + +class $$TableWithEveryColumnTypeTableProcessedTableManager + extends ProcessedTableManager< + _$TodoDb, + $TableWithEveryColumnTypeTable, + TableWithEveryColumnTypeData, + $$TableWithEveryColumnTypeTableFilterComposer, + $$TableWithEveryColumnTypeTableOrderingComposer, + $$TableWithEveryColumnTypeTableProcessedTableManager, + $$TableWithEveryColumnTypeTableInsertCompanionBuilder, + $$TableWithEveryColumnTypeTableUpdateCompanionBuilder> { + const $$TableWithEveryColumnTypeTableProcessedTableManager(super.$state); +} + +typedef $$TableWithEveryColumnTypeTableInsertCompanionBuilder + = TableWithEveryColumnTypeCompanion Function({ + Value id, + Value aBool, + Value aDateTime, + Value aText, + Value anInt, + Value anInt64, + Value aReal, + Value aBlob, + Value anIntEnum, + Value aTextWithConverter, +}); +typedef $$TableWithEveryColumnTypeTableUpdateCompanionBuilder + = TableWithEveryColumnTypeCompanion Function({ + Value id, + Value aBool, + Value aDateTime, + Value aText, + Value anInt, + Value anInt64, + Value aReal, + Value aBlob, + Value anIntEnum, + Value aTextWithConverter, +}); + +class $$TableWithEveryColumnTypeTableTableManager extends RootTableManager< + _$TodoDb, + $TableWithEveryColumnTypeTable, + TableWithEveryColumnTypeData, + $$TableWithEveryColumnTypeTableFilterComposer, + $$TableWithEveryColumnTypeTableOrderingComposer, + $$TableWithEveryColumnTypeTableProcessedTableManager, + $$TableWithEveryColumnTypeTableInsertCompanionBuilder, + $$TableWithEveryColumnTypeTableUpdateCompanionBuilder> { + $$TableWithEveryColumnTypeTableTableManager( + _$TodoDb db, $TableWithEveryColumnTypeTable table) + : super(TableManagerState( + db: db, + table: table, + filteringComposer: + $$TableWithEveryColumnTypeTableFilterComposer(db, table), + orderingComposer: + $$TableWithEveryColumnTypeTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$TableWithEveryColumnTypeTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + Value id = const Value.absent(), + Value aBool = const Value.absent(), + Value aDateTime = const Value.absent(), + Value aText = const Value.absent(), + Value anInt = const Value.absent(), + Value anInt64 = const Value.absent(), + Value aReal = const Value.absent(), + Value aBlob = const Value.absent(), + Value anIntEnum = const Value.absent(), + Value aTextWithConverter = const Value.absent(), + }) => + TableWithEveryColumnTypeCompanion( + id: id, + aBool: aBool, + aDateTime: aDateTime, + aText: aText, + anInt: anInt, + anInt64: anInt64, + aReal: aReal, + aBlob: aBlob, + anIntEnum: anIntEnum, + aTextWithConverter: aTextWithConverter, + ), + getInsertCompanionBuilder: ({ + Value id = const Value.absent(), + Value aBool = const Value.absent(), + Value aDateTime = const Value.absent(), + Value aText = const Value.absent(), + Value anInt = const Value.absent(), + Value anInt64 = const Value.absent(), + Value aReal = const Value.absent(), + Value aBlob = const Value.absent(), + Value anIntEnum = const Value.absent(), + Value aTextWithConverter = const Value.absent(), + }) => + TableWithEveryColumnTypeCompanion.insert( + id: id, + aBool: aBool, + aDateTime: aDateTime, + aText: aText, + anInt: anInt, + anInt64: anInt64, + aReal: aReal, + aBlob: aBlob, + anIntEnum: anIntEnum, + aTextWithConverter: aTextWithConverter, + ))); +} + class _$TodoDbManager { final _$TodoDb _db; _$TodoDbManager(this._db); @@ -2617,6 +3209,9 @@ class _$TodoDbManager { $$PureDefaultsTableTableManager(_db, _db.pureDefaults); $$WithCustomTypeTableTableManager get withCustomType => $$WithCustomTypeTableTableManager(_db, _db.withCustomType); + $$TableWithEveryColumnTypeTableTableManager get tableWithEveryColumnType => + $$TableWithEveryColumnTypeTableTableManager( + _db, _db.tableWithEveryColumnType); } class AllTodosWithCategoryResult extends CustomResultSet { @@ -2624,8 +3219,6 @@ class AllTodosWithCategoryResult extends CustomResultSet { final String? title; final String content; final DateTime? targetDate; - final double? someFloat; - final BigInt? someInt64; final int? category; final TodoStatus? status; final RowId catId; @@ -2636,16 +3229,14 @@ class AllTodosWithCategoryResult extends CustomResultSet { this.title, required this.content, this.targetDate, - this.someFloat, - this.someInt64, this.category, this.status, required this.catId, required this.catDesc, }) : super(row); @override - int get hashCode => Object.hash(id, title, content, targetDate, someFloat, - someInt64, category, status, catId, catDesc); + int get hashCode => Object.hash( + id, title, content, targetDate, category, status, catId, catDesc); @override bool operator ==(Object other) => identical(this, other) || @@ -2654,8 +3245,6 @@ class AllTodosWithCategoryResult extends CustomResultSet { other.title == this.title && other.content == this.content && other.targetDate == this.targetDate && - other.someFloat == this.someFloat && - other.someInt64 == this.someInt64 && other.category == this.category && other.status == this.status && other.catId == this.catId && @@ -2667,8 +3256,6 @@ class AllTodosWithCategoryResult extends CustomResultSet { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') - ..write('someFloat: $someFloat, ') - ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status, ') ..write('catId: $catId, ') diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_tests.dart index 7af64365..925bdb96 100644 --- a/drift/test/manager/manager_filter_tests.dart +++ b/drift/test/manager/manager_filter_tests.dart @@ -23,246 +23,256 @@ void main() { // (o) => o(priority: Value(CategoryPriority.high), description: "Other")); // // School - // await db.managers.todosTable.create((o) => o( - // content: "Get that math homework done", + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: Value("Get that math homework done"), // title: Value("Math Homework"), // category: Value(schoolId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1,seconds: 10))))); - // await db.managers.todosTable.create((o) => o( - // content: "Finish that report", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1,seconds: 10))))); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Finish that report", // title: Value("Report"), // category: Value(workId), - // status: Value(TodoStatus.workInProgress), - // targetDate: Value(DateTime.now().add(Duration(days: 2,seconds: 10))))); - // await db.managers.todosTable.create((o) => o( - // content: "Get that english homework done", + // anIntEnum: Value(TodoStatus.workInProgress), + // aDateTime: Value(DateTime.now().add(Duration(days: 2,seconds: 10))))); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Get that english homework done", // title: Value("English Homework"), // category: Value(schoolId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1,seconds: 15))))); - // await db.managers.todosTable.create((o) => o( - // content: "Finish that Book report", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1,seconds: 15))))); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Finish that Book report", // title: Value("Book Report"), // category: Value(workId), - // status: Value(TodoStatus.done), - // targetDate: Value(DateTime.now().subtract(Duration(days: 2,seconds: 15))))); + // anIntEnum: Value(TodoStatus.done), + // aDateTime: Value(DateTime.now().subtract(Duration(days: 2,seconds: 15))))); // // Work - // await db.managers.todosTable.create((o) => o( - // content: "File those reports", + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "File those reports", // title: Value("File Reports"), // category: Value(workId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 20))));); - // await db.managers.todosTable.create((o) => o( - // content: "Clean the office", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 20))));); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Clean the office", // title: Value("Clean Office"), // category: Value(workId), - // status: Value(TodoStatus.workInProgress), - // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 20))));); - // await db.managers.todosTable.create((o) => o( - // content: "Nail that presentation", + // anIntEnum: Value(TodoStatus.workInProgress), + // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 20))));); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Nail that presentation", // title: Value("Presentation"), // category: Value(workId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 25))))); - // await db.managers.todosTable.create((o) => o( - // content: "Take a break", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 25))))); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Take a break", // title: Value("Break"), // category: Value(workId), - // status: Value(TodoStatus.done), - // targetDate: Value(DateTime.now().subtract(Duration(days: 2, seconds: 25))))); + // anIntEnum: Value(TodoStatus.done), + // aDateTime: Value(DateTime.now().subtract(Duration(days: 2, seconds: 25))))); // // Work - // await db.managers.todosTable.create((o) => o( - // content: "Take out the trash", + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Take out the trash", // title: Value("Trash"), // category: Value(homeId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 30))));); - // await db.managers.todosTable.create((o) => o( - // content: "Mow the lawn", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 30))));); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Mow the lawn", // title: Value("Lawn"), // category: Value(homeId), - // status: Value(TodoStatus.workInProgress), - // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 30))))); - // await db.managers.todosTable.create((o) => o( - // content: "Fix the sink", + // anIntEnum: Value(TodoStatus.workInProgress), + // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 30))))); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Fix the sink", // title: Value("Sink"), // category: Value(homeId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 35))));); - // await db.managers.todosTable.create((o) => o( - // content: "Paint the walls", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 35))));); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Paint the walls", // title: Value("Paint"), // category: Value(homeId), - // status: Value(TodoStatus.done), - // targetDate: Value(DateTime.now().subtract(Duration(days: 2, seconds: 35))))); + // anIntEnum: Value(TodoStatus.done), + // aDateTime: Value(DateTime.now().subtract(Duration(days: 2, seconds: 35))))); // // Other - // await db.managers.todosTable.create((o) => o( - // content: "Get groceries", + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Get groceries", // title: Value("Groceries"), // category: Value(otherId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 40))));); - // await db.managers.todosTable.create((o) => o( - // content: "Pick up the kids", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 40))));); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Pick up the kids", // title: Value("Kids"), // category: Value(otherId), - // status: Value(TodoStatus.workInProgress), - // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 40))));); - // await db.managers.todosTable.create((o) => o( - // content: "Take the dog for a walk", + // anIntEnum: Value(TodoStatus.workInProgress), + // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 40))));); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Take the dog for a walk", // title: Value("Dog"), // category: Value(otherId), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 45))))); + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 45))))); // // Items with no category - // await db.managers.todosTable.create((o) => o( - // content: "Get Whiteboard", + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Get Whiteboard", // title: Value("Whiteboard"), - // status: Value(TodoStatus.open), - // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 50))));); - // await db.managers.todosTable.create((o) => o( - // content: "Drink Water", + // anIntEnum: Value(TodoStatus.open), + // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 50))));); + // await db.managers.tableWithEveryColumnType.create((o) => o( + // aText: "Drink Water", // title: Value("Water"), - // status: Value(TodoStatus.workInProgress), - // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 50))))); + // anIntEnum: Value(TodoStatus.workInProgress), + // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 50))))); // }); tearDown(() => db.close()); test('manager - query generic', () async { - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someFloat: Value(5.0), - targetDate: Value(DateTime.now().add(Duration(days: 1))))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - targetDate: Value(DateTime.now().add(Duration(days: 2))))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someFloat: Value(3.0), - targetDate: Value(DateTime.now().add(Duration(days: 3))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(5.0), + aDateTime: Value(DateTime.now().add(Duration(days: 1))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aDateTime: Value(DateTime.now().add(Duration(days: 2))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(3.0), + aDateTime: Value(DateTime.now().add(Duration(days: 3))))); // Equals expect( - db.managers.todosTable.filter((f) => f.someFloat.equals(5.0)).count(), + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.equals(5.0)) + .count(), completion(1)); - expect(db.managers.todosTable.filter((f) => f.someFloat(3.0)).count(), + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal(3.0)) + .count(), completion(1)); // In expect( - db.managers.todosTable - .filter((f) => f.someFloat.isIn([3.0, 5.0])) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isIn([3.0, 5.0])) .count(), completion(2)); // Not In expect( - db.managers.todosTable - .filter((f) => f.someFloat.isNotIn([3.0, 5.0])) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isNotIn([3.0, 5.0])) .count(), completion(0)); // Null check - expect(db.managers.todosTable.filter((f) => f.someFloat.isNull()).count(), + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isNull()) + .count(), completion(1)); expect( - db.managers.todosTable.filter((f) => f.someFloat.isNotNull()).count(), + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isNotNull()) + .count(), completion(2)); }); test('manager - query number', () async { - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someFloat: Value(5.0), - targetDate: Value(DateTime.now().add(Duration(days: 1))))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - targetDate: Value(DateTime.now().add(Duration(days: 2))))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someFloat: Value(3.0), - targetDate: Value(DateTime.now().add(Duration(days: 3))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(5.0), + aDateTime: Value(DateTime.now().add(Duration(days: 1))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aDateTime: Value(DateTime.now().add(Duration(days: 2))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(3.0), + aDateTime: Value(DateTime.now().add(Duration(days: 3))))); // More than expect( - db.managers.todosTable - .filter((f) => f.someFloat.isBiggerThan(3.0)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isBiggerThan(3.0)) .count(), completion(1)); expect( - db.managers.todosTable - .filter((f) => f.someFloat.isBiggerOrEqualTo(3.0)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isBiggerOrEqualTo(3.0)) .count(), completion(2)); // Less than expect( - db.managers.todosTable - .filter((f) => f.someFloat.isSmallerThan(5.0)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isSmallerThan(5.0)) .count(), completion(1)); expect( - db.managers.todosTable - .filter((f) => f.someFloat.isSmallerOrEqualTo(5.0)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isSmallerOrEqualTo(5.0)) .count(), completion(2)); // Between expect( - db.managers.todosTable - .filter((f) => f.someFloat.isBetween(3.0, 5.0)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isBetween(3.0, 5.0)) .count(), completion(2)); expect( - db.managers.todosTable - .filter((f) => f.someFloat.isNotBetween(3.0, 5.0)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.isNotBetween(3.0, 5.0)) .count(), completion(0)); }); test('manager - query string', () async { - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), )); - await db.managers.todosTable.create((o) => o( - content: "That homework Done", + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("That homework Done"), )); - await db.managers.todosTable.create((o) => o( - content: "that MATH homework", - status: Value(TodoStatus.open), + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("that MATH homework"), + anIntEnum: Value(TodoStatus.open), )); // StartsWith expect( - db.managers.todosTable - .filter((f) => f.content.startsWith("that")) + db.managers.tableWithEveryColumnType + .filter((f) => f.aText.startsWith("that")) .count(), completion(2)); // EndsWith expect( - db.managers.todosTable - .filter((f) => f.content.endsWith("done")) + db.managers.tableWithEveryColumnType + .filter((f) => f.aText.endsWith("done")) .count(), completion(2)); // Contains expect( - db.managers.todosTable - .filter((f) => f.content.contains("math")) + db.managers.tableWithEveryColumnType + .filter((f) => f.aText.contains("math")) .count(), completion(2)); @@ -271,77 +281,77 @@ void main() { // StartsWith expect( - db.managers.todosTable - .filter((f) => f.content.startsWith("that", caseInsensitive: false)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aText.startsWith("that", caseInsensitive: false)) .count(), completion(1)); // EndsWith expect( - db.managers.todosTable - .filter((f) => f.content.endsWith("done", caseInsensitive: false)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aText.endsWith("done", caseInsensitive: false)) .count(), completion(1)); // Contains expect( - db.managers.todosTable - .filter((f) => f.content.contains("math", caseInsensitive: false)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aText.contains("math", caseInsensitive: false)) .count(), completion(1)); }); test('manager - query int64', () async { - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someInt64: Value(BigInt.from(5.0)), - targetDate: Value(DateTime.now().add(Duration(days: 1))))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - targetDate: Value(DateTime.now().add(Duration(days: 2))))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someInt64: Value(BigInt.from(3.0)), - targetDate: Value(DateTime.now().add(Duration(days: 3))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + anInt64: Value(BigInt.from(5.0)), + aDateTime: Value(DateTime.now().add(Duration(days: 1))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aDateTime: Value(DateTime.now().add(Duration(days: 2))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + anInt64: Value(BigInt.from(3.0)), + aDateTime: Value(DateTime.now().add(Duration(days: 3))))); // More than expect( - db.managers.todosTable - .filter((f) => f.someInt64.isBiggerThan(BigInt.from(3.0))) + db.managers.tableWithEveryColumnType + .filter((f) => f.anInt64.isBiggerThan(BigInt.from(3.0))) .count(), completion(1)); expect( - db.managers.todosTable - .filter((f) => f.someInt64.isBiggerOrEqualTo(BigInt.from(3.0))) + db.managers.tableWithEveryColumnType + .filter((f) => f.anInt64.isBiggerOrEqualTo(BigInt.from(3.0))) .count(), completion(2)); // Less than expect( - db.managers.todosTable - .filter((f) => f.someInt64.isSmallerThan(BigInt.from(5.0))) + db.managers.tableWithEveryColumnType + .filter((f) => f.anInt64.isSmallerThan(BigInt.from(5.0))) .count(), completion(1)); expect( - db.managers.todosTable - .filter((f) => f.someInt64.isSmallerOrEqualTo(BigInt.from(5.0))) + db.managers.tableWithEveryColumnType + .filter((f) => f.anInt64.isSmallerOrEqualTo(BigInt.from(5.0))) .count(), completion(2)); // Between expect( - db.managers.todosTable - .filter((f) => - f.someInt64.isBetween(BigInt.from(3.0), BigInt.from(5.0))) + db.managers.tableWithEveryColumnType + .filter( + (f) => f.anInt64.isBetween(BigInt.from(3.0), BigInt.from(5.0))) .count(), completion(2)); expect( - db.managers.todosTable + db.managers.tableWithEveryColumnType .filter((f) => - f.someInt64.isNotBetween(BigInt.from(3.0), BigInt.from(5.0))) + f.anInt64.isNotBetween(BigInt.from(3.0), BigInt.from(5.0))) .count(), completion(0)); }); @@ -375,95 +385,97 @@ void main() { final day1 = DateTime.now().add(Duration(days: 1)); final day2 = DateTime.now().add(Duration(days: 2)); final day3 = DateTime.now().add(Duration(days: 3)); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someFloat: Value(5.0), - targetDate: Value(day1))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - targetDate: Value(day2))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open), - someFloat: Value(3.0), - targetDate: Value(day3))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(5.0), + aDateTime: Value(day1))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aDateTime: Value(day2))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(3.0), + aDateTime: Value(day3))); // More than expect( - db.managers.todosTable - .filter((f) => f.targetDate.isAfter(day2)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aDateTime.isAfter(day2)) .count(), completion(1)); expect( - db.managers.todosTable - .filter((f) => f.targetDate.isAfterOrOn(day2)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aDateTime.isAfterOrOn(day2)) .count(), completion(2)); // Less than expect( - db.managers.todosTable - .filter((f) => f.targetDate.isBefore(day2)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aDateTime.isBefore(day2)) .count(), completion(1)); expect( - db.managers.todosTable - .filter((f) => f.targetDate.isBeforeOrOn(day2)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aDateTime.isBeforeOrOn(day2)) .count(), completion(2)); // Between expect( - db.managers.todosTable - .filter((f) => f.targetDate.isBetween(day1, day2)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aDateTime.isBetween(day1, day2)) .count(), completion(2)); expect( - db.managers.todosTable - .filter((f) => f.targetDate.isNotBetween(day1, day2)) + db.managers.tableWithEveryColumnType + .filter((f) => f.aDateTime.isNotBetween(day1, day2)) .count(), completion(1)); }); test('manager - query custom column', () async { - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.open))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.workInProgress))); - await db.managers.todosTable.create((o) => o( - content: "Get that math homework done", - status: Value(TodoStatus.done))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.workInProgress))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.done))); // Equals expect( - db.managers.todosTable - .filter((f) => f.status.equals(TodoStatus.open)) + db.managers.tableWithEveryColumnType + .filter((f) => f.anIntEnum.equals(TodoStatus.open)) .count(), completion(2)); expect( - db.managers.todosTable.filter((f) => f.status(TodoStatus.open)).count(), + db.managers.tableWithEveryColumnType + .filter((f) => f.anIntEnum(TodoStatus.open)) + .count(), completion(2)); // In expect( - db.managers.todosTable + db.managers.tableWithEveryColumnType .filter((f) => - f.status.isIn([TodoStatus.open, TodoStatus.workInProgress])) + f.anIntEnum.isIn([TodoStatus.open, TodoStatus.workInProgress])) .count(), completion(3)); // Not In expect( - db.managers.todosTable - .filter((f) => - f.status.isNotIn([TodoStatus.open, TodoStatus.workInProgress])) + db.managers.tableWithEveryColumnType + .filter((f) => f.anIntEnum + .isNotIn([TodoStatus.open, TodoStatus.workInProgress])) .count(), completion(1)); }); From cdd11c8e40dd0b1a36d0a1624645af8f48451d54 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 16 Apr 2024 14:48:43 -0400 Subject: [PATCH 56/74] add tests, fix custom type --- drift/lib/src/runtime/manager/filter.dart | 198 +++++++----- drift/test/generated/custom_tables.g.dart | 7 +- drift/test/generated/todos.g.dart | 22 +- drift/test/manager/manager_filter_tests.dart | 313 +++++++++++-------- drift_dev/lib/src/writer/manager_writer.dart | 4 +- 5 files changed, 309 insertions(+), 235 deletions(-) diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 26e17e2d..9827be99 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -9,35 +9,44 @@ class ColumnFilters { /// Use an extention to add more filters to any column type /// /// ```dart - /// extension on FilterComposer{ - /// FitlerBuilder after2000() => isAfter(DateTime(2000)); + /// extension on ColumnFilters{ + /// ComposableFilter after2000() => isAfter(DateTime(2000)); ///} /// ``` - const ColumnFilters(this.column); + const ColumnFilters(this.column, [this.inverted = false]); /// Column that this [ColumnFilters] wraps final Expression column; - /// Create a filter that checks if the column is null. - ComposableFilter isNull() => ComposableFilter(column.isNull()); + /// If true, all filters will be inverted + final bool inverted; - /// Create a filter that checks if the column is not null. - ComposableFilter isNotNull() => ComposableFilter(column.isNotNull()); + /// Returns a copy of these column filters where all the filters are inverted + /// ```dart + /// myColumn.not.equals(5); // All columns that aren't null and have a value that is not equal to 5 + /// ``` + /// Keep in mind that while using inverted filters, null is never returned. + /// + /// If you would like to include them, use the [isNull] filter as well + /// ```dart + /// myColumn.not.equals(5) | myColumn.isNull(); // All columns that are null OR have a value that is not equal to 5 will be returned + /// ``` + ColumnFilters get not => ColumnFilters(column, !inverted); /// Create a filter that checks if the column equals a value. - ComposableFilter equals(T value) => ComposableFilter(column.equals(value)); + ComposableFilter equals(T value) => + ComposableFilter(column.equals(value), inverted: inverted); + + /// Create a filter that checks if the column is null. + ComposableFilter isNull() => + ComposableFilter(column.isNull(), inverted: inverted); /// Create a filter that checks if the column is in a list of values. - ComposableFilter isIn(Iterable values) => - ComposableFilter(column.isIn(values)); - - /// Create a filter that checks if the column is not in a list of values. - ComposableFilter isNotIn(Iterable values) => - ComposableFilter(column.isNotIn(values)); + ComposableFilter(column.isIn(values), inverted: inverted); /// Shortcut for [equals] - ComposableFilter call(T value) => ComposableFilter(column.equals(value)); + ComposableFilter call(T value) => equals(value); } enum _StringFilterTypes { contains, startsWith, endsWith } @@ -76,165 +85,191 @@ extension StringFilters on ColumnFilters { } /// Create a filter to check if the this text column contains a substring - /// {@template drift.manager.likeCaseSensitivity} /// /// Setting [caseInsensitive] to false will have no effect unless the database in configured to use /// case sensitive like expressions. /// /// See https://www.sqlitetutorial.net/sqlite-like/ for more information on how /// to the like expression works. - /// {@endtemplate} ComposableFilter contains(T value, {bool caseInsensitive = true}) { return ComposableFilter( - _buildExpression(_StringFilterTypes.contains, value, caseInsensitive)); + _buildExpression(_StringFilterTypes.contains, value, caseInsensitive), + inverted: inverted); } /// Create a filter to check if the this text column starts with a substring - /// {@macro drift.manager.likeCaseSensitivity} + /// + /// Setting [caseInsensitive] to false will have no effect unless the database in configured to use + /// case sensitive like expressions. + /// + /// See https://www.sqlitetutorial.net/sqlite-like/ for more information on how + /// to the like expression works. ComposableFilter startsWith(T value, {bool caseInsensitive = true}) { - return ComposableFilter(_buildExpression( - _StringFilterTypes.startsWith, value, caseInsensitive)); + return ComposableFilter( + _buildExpression(_StringFilterTypes.startsWith, value, caseInsensitive), + inverted: inverted); } /// Create a filter to check if the this text column ends with a substring - /// {@macro drift.manager.likeCaseSensitivity} + /// + /// Setting [caseInsensitive] to false will have no effect unless the database in configured to use + /// case sensitive like expressions. + /// + /// See https://www.sqlitetutorial.net/sqlite-like/ for more information on how + /// to the like expression works. ComposableFilter endsWith(T value, {bool caseInsensitive = true}) { return ComposableFilter( - _buildExpression(_StringFilterTypes.endsWith, value, caseInsensitive)); + _buildExpression(_StringFilterTypes.endsWith, value, caseInsensitive), + inverted: inverted); } } /// Built in filters for bool columns extension BoolFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value - ComposableFilter isTrue() => ComposableFilter(column.equals(true)); + ComposableFilter isTrue() => + ComposableFilter(column.equals(true), inverted: inverted); /// Create a filter to check if the column is small than a value - ComposableFilter isFalse() => ComposableFilter(column.equals(false)); + ComposableFilter isFalse() => + ComposableFilter(column.equals(false), inverted: inverted); } /// Built in filters for int/double columns extension NumFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => - ComposableFilter(column.isBiggerThanValue(value)); + ComposableFilter(column.isBiggerThanValue(value), inverted: inverted); /// Create a filter to check if the column is small than a value ComposableFilter isSmallerThan(T value) => - ComposableFilter(column.isSmallerThanValue(value)); + ComposableFilter(column.isSmallerThanValue(value), inverted: inverted); /// Create a filter to check if the column is bigger or equal to a value ComposableFilter isBiggerOrEqualTo(T value) => - ComposableFilter(column.isBiggerOrEqualValue(value)); + ComposableFilter(column.isBiggerOrEqualValue(value), inverted: inverted); /// Create a filter to check if the column is small or equal to a value ComposableFilter isSmallerOrEqualTo(T value) => - ComposableFilter(column.isSmallerOrEqualValue(value)); + ComposableFilter(column.isSmallerOrEqualValue(value), inverted: inverted); /// Create a filter to check if the column is between two values /// This is done inclusively, so the column can be equal to the lower or higher value /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => - ComposableFilter(column.isBetweenValues(lower, higher)); - - /// Create a filter to check if the column is not between two values - /// This is done inclusively, so the column will not be equal to the lower or higher value - /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 - ComposableFilter isNotBetween(T lower, T higher) => - isBetween(lower, higher)._invert(); + ComposableFilter(column.isBetweenValues(lower, higher), + inverted: inverted); } /// Built in filters for BigInt columns extension BigIntFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value ComposableFilter isBiggerThan(T value) => - ComposableFilter(column.isBiggerThanValue(value)); + ComposableFilter(column.isBiggerThanValue(value), inverted: inverted); /// Create a filter to check if the column is small than a value ComposableFilter isSmallerThan(T value) => - ComposableFilter(column.isSmallerThanValue(value)); + ComposableFilter(column.isSmallerThanValue(value), inverted: inverted); /// Create a filter to check if the column is bigger or equal to a value ComposableFilter isBiggerOrEqualTo(T value) => - ComposableFilter(column.isBiggerOrEqualValue(value)); + ComposableFilter(column.isBiggerOrEqualValue(value), inverted: inverted); /// Create a filter to check if the column is small or equal to a value ComposableFilter isSmallerOrEqualTo(T value) => - ComposableFilter(column.isSmallerOrEqualValue(value)); + ComposableFilter(column.isSmallerOrEqualValue(value), inverted: inverted); /// Create a filter to check if the column is between two values /// This is done inclusively, so the column can be equal to the lower or higher value /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => - ComposableFilter(column.isBetweenValues(lower, higher)); - - /// Create a filter to check if the column is not between two values - /// This is done inclusively, so the column will not be equal to the lower or higher value - /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 - ComposableFilter isNotBetween(T lower, T higher) => - isBetween(lower, higher)._invert(); + ComposableFilter(column.isBetweenValues(lower, higher), + inverted: inverted); } /// Built in filters for DateTime columns extension DateFilters on ColumnFilters { /// Create a filter to check if the column is after a [DateTime] ComposableFilter isAfter(T value) => - ComposableFilter(column.isBiggerThanValue(value)); + ComposableFilter(column.isBiggerThanValue(value), inverted: inverted); /// Create a filter to check if the column is before a [DateTime] ComposableFilter isBefore(T value) => - ComposableFilter(column.isSmallerThanValue(value)); + ComposableFilter(column.isSmallerThanValue(value), inverted: inverted); /// Create a filter to check if the column is on or after a [DateTime] ComposableFilter isAfterOrOn(T value) => - ComposableFilter(column.isBiggerOrEqualValue(value)); + ComposableFilter(column.isBiggerOrEqualValue(value), inverted: inverted); /// Create a filter to check if the column is before or on a [DateTime] ComposableFilter isBeforeOrOn(T value) => - ComposableFilter(column.isSmallerOrEqualValue(value)); + ComposableFilter(column.isSmallerOrEqualValue(value), inverted: inverted); /// Create a filter to check if the column is between 2 [DateTime]s /// This is done inclusively, so the column can be equal to the lower or higher value /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => - ComposableFilter(column.isBetweenValues(lower, higher)); - - /// Create a filter to check if the column is not between 2 [DateTime]s - /// This is done inclusively, so the column will not be equal to the lower or higher value - /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 - ComposableFilter isNotBetween(T lower, T higher) => - isBetween(lower, higher)._invert(); + ComposableFilter(column.isBetweenValues(lower, higher), + inverted: inverted); } /// Defines a class which is used to wrap a column with a type converter to only expose filter functions -class ColumnWithTypeConverterFilters { +// [CustomType] is the type that the user has defined in their type converter +// [CustomTypeNonNullable] is the type that the user has defined in their type converter, but is non-nullable +class ColumnWithTypeConverterFilters { /// Similar to [ColumnFilters] but for columns with type converters\ - const ColumnWithTypeConverterFilters(this.column); + const ColumnWithTypeConverterFilters(this.column, [this.inverted = false]); + + /// If true, all filters will be inverted + final bool inverted; /// Column that this [ColumnWithTypeConverterFilters] wraps - final GeneratedColumnWithTypeConverter column; + final GeneratedColumnWithTypeConverter column; + + /// Returns a copy of these column filters where all the filters are inverted + /// ```dart + /// myColumn.not.equals(5); // All columns that aren't null and have a value that is not equal to 5 + /// ``` + /// Keep in mind that while using inverted filters, null is never returned. + /// + /// If you would like to include them, use the [isNull] filter as well + /// ```dart + /// myColumn.not.equals(5) | myColumn.isNull(); // All columns that are null OR have a value that is not equal to 5 + /// ``` + ColumnWithTypeConverterFilters + get not => ColumnWithTypeConverterFilters(column, !inverted); /// Create a filter that checks if the column is null. - ComposableFilter isNull() => ComposableFilter(column.isNull()); + ComposableFilter isNull() => + ComposableFilter(column.isNull(), inverted: inverted); - /// Create a filter that checks if the column is not null. - ComposableFilter isNotNull() => ComposableFilter(column.isNotNull()); + /// Get the actual value from the custom type + T _customTypeToSql(CustomTypeNonNullable value) { + assert(value != null, + 'The filter value cannot be null. This is likely a bug in the generated code. Please report this issue.'); + final mappedValue = column.converter.toSql(value as CustomType); + + if (mappedValue == null) { + throw ArgumentError( + 'The TypeConverter for this column returned null when converting the type to sql. Ensure that your TypeConverter never returns null when provided a non-null value.'); + } + return mappedValue; + } /// Create a filter that checks if the column equals a value. - ComposableFilter equals(CUSTOM value) => - ComposableFilter(column.equalsValue(value)); + ComposableFilter equals(CustomTypeNonNullable value) { + return ComposableFilter(column.equals(_customTypeToSql(value)), + inverted: inverted); + } /// Shortcut for [equals] - ComposableFilter call(CUSTOM value) => - ComposableFilter(column.equalsValue(value)); + ComposableFilter call(CustomTypeNonNullable value) => equals(value); /// Create a filter that checks if the column is in a list of values. - ComposableFilter isIn(Iterable values) => - ComposableFilter(column.isInValues(values)); - - /// Create a filter that checks if the column is not in a list of values. - ComposableFilter isNotIn(Iterable values) => - ComposableFilter(column.isNotInValues(values)); + ComposableFilter isIn(Iterable values) => + ComposableFilter(column.isIn(values.map(_customTypeToSql).toList()), + inverted: inverted); } /// This class is wrapper on the expression class @@ -247,10 +282,13 @@ class ComposableFilter extends HasJoinBuilders { final Set joinBuilders; /// The expression that will be applied to the query - final Expression expression; + late final Expression expression; /// Create a new [ComposableFilter] for a column without any joins - ComposableFilter(this.expression) : joinBuilders = {}; + ComposableFilter(Expression expression, {required bool inverted}) + : joinBuilders = {} { + this.expression = inverted ? expression.not() : expression; + } /// Create a new [ComposableFilter] for a column with joins ComposableFilter._(this.expression, this.joinBuilders); @@ -270,14 +308,6 @@ class ComposableFilter extends HasJoinBuilders { joinBuilders.union(other.joinBuilders), ); } - - /// Returns a copy of this filter with the expression reversed - ComposableFilter _invert() { - return ComposableFilter._( - expression.not(), - joinBuilders, - ); - } } /// The class that orchestrates the composition of filtering diff --git a/drift/test/generated/custom_tables.g.dart b/drift/test/generated/custom_tables.g.dart index f8bbe29f..51c53c4f 100644 --- a/drift/test/generated/custom_tables.g.dart +++ b/drift/test/generated/custom_tables.g.dart @@ -2139,12 +2139,13 @@ class $ConfigTableFilterComposer ColumnFilters get configKey => ColumnFilters($table.configKey); ColumnFilters get configValue => ColumnFilters($table.configValue); ColumnFilters get syncStateValue => ColumnFilters($table.syncState); - ColumnWithTypeConverterFilters get syncState => + ColumnWithTypeConverterFilters get syncState => ColumnWithTypeConverterFilters($table.syncState); ColumnFilters get syncStateImplicitValue => ColumnFilters($table.syncStateImplicit); - ColumnWithTypeConverterFilters get syncStateImplicit => - ColumnWithTypeConverterFilters($table.syncStateImplicit); + ColumnWithTypeConverterFilters + get syncStateImplicit => + ColumnWithTypeConverterFilters($table.syncStateImplicit); } class $ConfigTableOrderingComposer diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 233e26b1..0847a5c8 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -2502,12 +2502,12 @@ class $$CategoriesTableFilterComposer extends FilterComposer<_$TodoDb, $CategoriesTable> { $$CategoriesTableFilterComposer(super.db, super.table); ColumnFilters get idValue => ColumnFilters($table.id); - ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters get id => ColumnWithTypeConverterFilters($table.id); ColumnFilters get description => ColumnFilters($table.description); ColumnFilters get priorityValue => ColumnFilters($table.priority); - ColumnWithTypeConverterFilters get priority => - ColumnWithTypeConverterFilters($table.priority); + ColumnWithTypeConverterFilters + get priority => ColumnWithTypeConverterFilters($table.priority); ColumnFilters get descriptionInUpperCase => ColumnFilters($table.descriptionInUpperCase); ComposableFilter todosTableRefs( @@ -2600,7 +2600,7 @@ class $$TodosTableTableFilterComposer extends FilterComposer<_$TodoDb, $TodosTableTable> { $$TodosTableTableFilterComposer(super.db, super.table); ColumnFilters get idValue => ColumnFilters($table.id); - ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters get id => ColumnWithTypeConverterFilters($table.id); ColumnFilters get title => ColumnFilters($table.title); ColumnFilters get content => ColumnFilters($table.content); @@ -2620,7 +2620,7 @@ class $$TodosTableTableFilterComposer } ColumnFilters get statusValue => ColumnFilters($table.status); - ColumnWithTypeConverterFilters get status => + ColumnWithTypeConverterFilters get status => ColumnWithTypeConverterFilters($table.status); } @@ -2731,7 +2731,7 @@ class $$TodosTableTableTableManager extends RootTableManager< class $$UsersTableFilterComposer extends FilterComposer<_$TodoDb, $UsersTable> { $$UsersTableFilterComposer(super.db, super.table); ColumnFilters get idValue => ColumnFilters($table.id); - ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters get id => ColumnWithTypeConverterFilters($table.id); ColumnFilters get name => ColumnFilters($table.name); ColumnFilters get isAwesome => ColumnFilters($table.isAwesome); @@ -2907,8 +2907,8 @@ class $$PureDefaultsTableFilterComposer extends FilterComposer<_$TodoDb, $PureDefaultsTable> { $$PureDefaultsTableFilterComposer(super.db, super.table); ColumnFilters get txtValue => ColumnFilters($table.txt); - ColumnWithTypeConverterFilters get txt => - ColumnWithTypeConverterFilters($table.txt); + ColumnWithTypeConverterFilters + get txt => ColumnWithTypeConverterFilters($table.txt); } class $$PureDefaultsTableOrderingComposer @@ -3049,7 +3049,7 @@ class $$TableWithEveryColumnTypeTableFilterComposer extends FilterComposer<_$TodoDb, $TableWithEveryColumnTypeTable> { $$TableWithEveryColumnTypeTableFilterComposer(super.db, super.table); ColumnFilters get idValue => ColumnFilters($table.id); - ColumnWithTypeConverterFilters get id => + ColumnWithTypeConverterFilters get id => ColumnWithTypeConverterFilters($table.id); ColumnFilters get aBool => ColumnFilters($table.aBool); ColumnFilters get aDateTime => ColumnFilters($table.aDateTime); @@ -3059,11 +3059,11 @@ class $$TableWithEveryColumnTypeTableFilterComposer ColumnFilters get aReal => ColumnFilters($table.aReal); ColumnFilters get aBlob => ColumnFilters($table.aBlob); ColumnFilters get anIntEnumValue => ColumnFilters($table.anIntEnum); - ColumnWithTypeConverterFilters get anIntEnum => + ColumnWithTypeConverterFilters get anIntEnum => ColumnWithTypeConverterFilters($table.anIntEnum); ColumnFilters get aTextWithConverterValue => ColumnFilters($table.aTextWithConverter); - ColumnWithTypeConverterFilters + ColumnWithTypeConverterFilters get aTextWithConverter => ColumnWithTypeConverterFilters($table.aTextWithConverter); } diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_tests.dart index 925bdb96..7b0be64e 100644 --- a/drift/test/manager/manager_filter_tests.dart +++ b/drift/test/manager/manager_filter_tests.dart @@ -5,134 +5,12 @@ import '../generated/todos.dart'; import '../test_utils/test_utils.dart'; void main() { - driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; late TodoDb db; setUp(() { db = TodoDb(testInMemoryDatabase()); }); - // // Create a dataset for the tests - // schoolId = await db.managers.categories.create((o) => - // o(priority: Value(CategoryPriority.high), description: "School")); - // workId = await db.managers.categories.create( - // (o) => o(priority: Value(CategoryPriority.low), description: "Work")); - // homeId = await db.managers.categories.create((o) => - // o(priority: Value(CategoryPriority.medium), description: "Home")); - // otherId = await db.managers.categories.create( - // (o) => o(priority: Value(CategoryPriority.high), description: "Other")); - - // // School - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: Value("Get that math homework done"), - // title: Value("Math Homework"), - // category: Value(schoolId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1,seconds: 10))))); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Finish that report", - // title: Value("Report"), - // category: Value(workId), - // anIntEnum: Value(TodoStatus.workInProgress), - // aDateTime: Value(DateTime.now().add(Duration(days: 2,seconds: 10))))); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Get that english homework done", - // title: Value("English Homework"), - // category: Value(schoolId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1,seconds: 15))))); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Finish that Book report", - // title: Value("Book Report"), - // category: Value(workId), - // anIntEnum: Value(TodoStatus.done), - // aDateTime: Value(DateTime.now().subtract(Duration(days: 2,seconds: 15))))); - - // // Work - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "File those reports", - // title: Value("File Reports"), - // category: Value(workId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 20))));); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Clean the office", - // title: Value("Clean Office"), - // category: Value(workId), - // anIntEnum: Value(TodoStatus.workInProgress), - // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 20))));); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Nail that presentation", - // title: Value("Presentation"), - // category: Value(workId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 25))))); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Take a break", - // title: Value("Break"), - // category: Value(workId), - // anIntEnum: Value(TodoStatus.done), - // aDateTime: Value(DateTime.now().subtract(Duration(days: 2, seconds: 25))))); - - // // Work - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Take out the trash", - // title: Value("Trash"), - // category: Value(homeId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 30))));); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Mow the lawn", - // title: Value("Lawn"), - // category: Value(homeId), - // anIntEnum: Value(TodoStatus.workInProgress), - // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 30))))); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Fix the sink", - // title: Value("Sink"), - // category: Value(homeId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 35))));); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Paint the walls", - // title: Value("Paint"), - // category: Value(homeId), - // anIntEnum: Value(TodoStatus.done), - // aDateTime: Value(DateTime.now().subtract(Duration(days: 2, seconds: 35))))); - - // // Other - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Get groceries", - // title: Value("Groceries"), - // category: Value(otherId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 40))));); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Pick up the kids", - // title: Value("Kids"), - // category: Value(otherId), - // anIntEnum: Value(TodoStatus.workInProgress), - // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 40))));); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Take the dog for a walk", - // title: Value("Dog"), - // category: Value(otherId), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 45))))); - - // // Items with no category - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Get Whiteboard", - // title: Value("Whiteboard"), - // anIntEnum: Value(TodoStatus.open), - // aDateTime: Value(DateTime.now().add(Duration(days: 1, seconds: 50))));); - // await db.managers.tableWithEveryColumnType.create((o) => o( - // aText: "Drink Water", - // title: Value("Water"), - // anIntEnum: Value(TodoStatus.workInProgress), - // aDateTime: Value(DateTime.now().add(Duration(days: 2, seconds: 50))))); - // }); - tearDown(() => db.close()); test('manager - query generic', () async { @@ -162,6 +40,14 @@ void main() { .filter((f) => f.aReal(3.0)) .count(), completion(1)); + + // Not Equals - Exclude null (Default behavior) + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.aReal.not.equals(5.0)) + .count(), + completion(1)); + // In expect( db.managers.tableWithEveryColumnType @@ -172,7 +58,7 @@ void main() { // Not In expect( db.managers.tableWithEveryColumnType - .filter((f) => f.aReal.isNotIn([3.0, 5.0])) + .filter((f) => f.aReal.not.isIn([3.0, 5.0])) .count(), completion(0)); @@ -182,9 +68,10 @@ void main() { .filter((f) => f.aReal.isNull()) .count(), completion(1)); + expect( db.managers.tableWithEveryColumnType - .filter((f) => f.aReal.isNotNull()) + .filter((f) => f.aReal.not.isNull()) .count(), completion(2)); }); @@ -237,7 +124,7 @@ void main() { completion(2)); expect( db.managers.tableWithEveryColumnType - .filter((f) => f.aReal.isNotBetween(3.0, 5.0)) + .filter((f) => f.aReal.not.isBetween(3.0, 5.0)) .count(), completion(0)); }); @@ -351,7 +238,7 @@ void main() { expect( db.managers.tableWithEveryColumnType .filter((f) => - f.anInt64.isNotBetween(BigInt.from(3.0), BigInt.from(5.0))) + f.anInt64.not.isBetween(BigInt.from(3.0), BigInt.from(5.0))) .count(), completion(0)); }); @@ -432,7 +319,7 @@ void main() { completion(2)); expect( db.managers.tableWithEveryColumnType - .filter((f) => f.aDateTime.isNotBetween(day1, day2)) + .filter((f) => f.aDateTime.not.isBetween(day1, day2)) .count(), completion(1)); }); @@ -450,6 +337,8 @@ void main() { await db.managers.tableWithEveryColumnType.create((o) => o( aText: Value("Get that math homework done"), anIntEnum: Value(TodoStatus.done))); + await db.managers.tableWithEveryColumnType + .create((o) => o(aText: Value("Get that math homework done"))); // Equals expect( @@ -463,20 +352,172 @@ void main() { .count(), completion(2)); - // In expect( db.managers.tableWithEveryColumnType - .filter((f) => - f.anIntEnum.isIn([TodoStatus.open, TodoStatus.workInProgress])) + .filter((f) => f.anIntEnum.not(TodoStatus.open)) .count(), - completion(3)); + completion(2)); - // Not In + // // Not Equals + // expect( + // db.managers.tableWithEveryColumnType + // .filter((f) => f.anIntEnum.not.equals(TodoStatus.open)) + // .count(), + // completion(2)); + + // // In + // expect( + // db.managers.tableWithEveryColumnType + // .filter((f) => + // f.anIntEnum.isIn([TodoStatus.open, TodoStatus.workInProgress])) + // .count(), + // completion(3)); + + // // Not In + // expect( + // db.managers.tableWithEveryColumnType + // .filter((f) => f.anIntEnum.not + // .isIn([TodoStatus.open, TodoStatus.workInProgress])) + // .count(), + // completion(1)); + }); + + test('manager - filter related', () async { + final schoolCategoryId = await db.managers.categories.create((o) => + o(priority: Value(CategoryPriority.high), description: "School")); + final workCategoryId = await db.managers.categories.create( + (o) => o(priority: Value(CategoryPriority.low), description: "Work")); + + // School + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + title: Value("Math Homework"), + category: Value(schoolCategoryId), + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 10))))); + await db.managers.todosTable.create((o) => o( + content: "Finish that report", + title: Value("Report"), + category: Value(schoolCategoryId), + status: Value(TodoStatus.workInProgress), + targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 10))))); + await db.managers.todosTable.create((o) => o( + content: "Get that english homework done", + title: Value("English Homework"), + category: Value(schoolCategoryId), + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 15))))); + await db.managers.todosTable.create((o) => o( + content: "Finish that Book report", + title: Value("Book Report"), + category: Value(schoolCategoryId), + status: Value(TodoStatus.done), + targetDate: + Value(DateTime.now().subtract(Duration(days: 2, seconds: 15))))); + + // Work + await db.managers.todosTable.create((o) => o( + content: "File those reports", + title: Value("File Reports"), + category: Value(workCategoryId), + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 20))))); + await db.managers.todosTable.create((o) => o( + content: "Clean the office", + title: Value("Clean Office"), + category: Value(workCategoryId), + status: Value(TodoStatus.workInProgress), + targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 20))))); + await db.managers.todosTable.create((o) => o( + content: "Nail that presentation", + title: Value("Presentation"), + category: Value(workCategoryId), + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 25))))); + await db.managers.todosTable.create((o) => o( + content: "Take a break", + title: Value("Break"), + category: Value(workCategoryId), + status: Value(TodoStatus.done), + targetDate: + Value(DateTime.now().subtract(Duration(days: 2, seconds: 25))))); + + // Items with no category + await db.managers.todosTable.create((o) => o( + content: "Get Whiteboard", + title: Value("Whiteboard"), + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 50))))); + await db.managers.todosTable.create((o) => o( + content: "Drink Water", + title: Value("Water"), + status: Value(TodoStatus.workInProgress), + targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 50))))); + + // item without title + + // Equals expect( - db.managers.tableWithEveryColumnType - .filter((f) => f.anIntEnum - .isNotIn([TodoStatus.open, TodoStatus.workInProgress])) + db.managers.todosTable + .filter((f) => f.category( + (f) => f.id(RowId(schoolCategoryId)), + )) .count(), - completion(1)); + completion(4)); + + // Not Equals + expect( + db.managers.todosTable + .filter( + (f) => + f.category((f) => f.id.not.equals(RowId(schoolCategoryId))), + ) + .count(), + completion(4)); + + // Multiple filters + expect( + db.managers.todosTable + .filter((f) => f.category( + (f) => f.id(RowId(schoolCategoryId)), + )) + .filter((f) => f.status.equals(TodoStatus.open)) + .count(), + completion(2)); + + // Multiple 2 related filters + expect( + db.managers.todosTable + .filter((f) => f.category( + (f) => + f.priority.equals(CategoryPriority.low) | + f.descriptionInUpperCase.equals("SCHOOL"), + )) + .count(), + completion(8)); + + // Multiple use related filters twice + expect( + db.managers.todosTable + .filter((f) => + f.category( + (f) => f.priority.equals(CategoryPriority.low), + ) | + f.category( + (f) => f.descriptionInUpperCase.equals("SCHOOL"), + )) + .count(), + completion(8)); + // Use .filter multiple times + expect( + db.managers.todosTable + .filter((f) => f.category( + (f) => f.priority.equals(CategoryPriority.high), + )) + .filter((f) => f.category( + (f) => f.descriptionInUpperCase.equals("SCHOOL"), + )) + .count(), + completion(4)); }); } diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 67cf6b60..9ccd2a1e 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -61,9 +61,11 @@ class _FilterWithConverterWriter extends _FilterWriter { @override void writeFilter(TextEmitter leaf) { + final nonNullableConverterType = converterType.replaceFirst("?", ""); leaf ..writeDriftRef("ColumnWithTypeConverterFilters") - ..write("<$converterType,$type> get $filterName =>") + ..write( + "<$converterType,$nonNullableConverterType,$type> get $filterName =>") ..writeDriftRef("ColumnWithTypeConverterFilters") ..writeln("(\$table.$fieldGetter);"); } From 327244b607232a362b9bd316b58aff33b919af36 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 16 Apr 2024 15:34:11 -0400 Subject: [PATCH 57/74] more tests --- drift/lib/src/runtime/manager/manager.dart | 8 +- drift/test/generated/todos.dart | 2 +- drift/test/generated/todos.g.dart | 2 +- drift/test/manager/manager_filter_tests.dart | 18 ++++ drift/test/manager/manager_order_tests.dart | 91 +++++++++++++++++++ .../test/manager/processed_manager_tests.dart | 90 ++++++++++++++++++ 6 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 drift/test/manager/manager_order_tests.dart create mode 100644 drift/test/manager/processed_manager_tests.dart diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index cc1b321c..42ee79ae 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -152,6 +152,7 @@ class TableManagerState< if (joins.isEmpty && targetColumns == null) { final simpleStatement = db.select(_tableAsTableInfo, distinct: distinct ?? false); + // Apply the expression to the statement if (filter != null) { simpleStatement.where((_) => filter!); @@ -249,7 +250,12 @@ class TableManagerState< final query = existsQuery(statement); final existsStatement = db.selectOnly(_tableAsTableInfo) ..addColumns([query]); - return (await existsStatement.map((p0) => p0.read(query)).getSingle())!; + return (await existsStatement + .map((p0) => p0.read(query)) + .get() + .then((value) { + return value.firstOrNull ?? false; + })); } /// Build a delete statement based on the manager state diff --git a/drift/test/generated/todos.dart b/drift/test/generated/todos.dart index 3e4ba9af..361299fc 100644 --- a/drift/test/generated/todos.dart +++ b/drift/test/generated/todos.dart @@ -23,7 +23,7 @@ class TodosTable extends Table with AutoIncrement { TextColumn get content => text()(); @JsonKey('target_date') DateTimeColumn get targetDate => dateTime().nullable().unique()(); - + @ReferenceName("todos") IntColumn get category => integer().references(Categories, #id).nullable()(); TextColumn get status => textEnum().nullable()(); diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 0847a5c8..16738d29 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -2510,7 +2510,7 @@ class $$CategoriesTableFilterComposer get priority => ColumnWithTypeConverterFilters($table.priority); ColumnFilters get descriptionInUpperCase => ColumnFilters($table.descriptionInUpperCase); - ComposableFilter todosTableRefs( + ComposableFilter todos( ComposableFilter Function($$TodosTableTableFilterComposer f) f) { return $composeWithJoins( $db: $db, diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_tests.dart index 7b0be64e..4a074bca 100644 --- a/drift/test/manager/manager_filter_tests.dart +++ b/drift/test/manager/manager_filter_tests.dart @@ -519,5 +519,23 @@ void main() { )) .count(), completion(4)); + + // Use backreference + expect( + db.managers.categories + .filter((f) => f.todos((f) => f.title.equals("Math Homework"))) + .getSingle() + .then((value) => value.description), + completion("School")); + + // Nested backreference + expect( + db.managers.categories + .filter((f) => f.todos((f) => f.category( + (f) => f.todos((f) => f.title.equals("Math Homework"))))) + .distict() + .getSingle() + .then((value) => value.description), + completion("School")); }); } diff --git a/drift/test/manager/manager_order_tests.dart b/drift/test/manager/manager_order_tests.dart new file mode 100644 index 00000000..4f64141e --- /dev/null +++ b/drift/test/manager/manager_order_tests.dart @@ -0,0 +1,91 @@ +import 'package:drift/drift.dart'; +import 'package:test/test.dart'; + +import '../generated/todos.dart'; +import '../test_utils/test_utils.dart'; + +void main() { + late TodoDb db; + + setUp(() { + db = TodoDb(testInMemoryDatabase()); + }); + + tearDown(() => db.close()); + + test('manager - order', () async { + await db.managers.tableWithEveryColumnType.create((o) => o( + id: Value(RowId(1)), + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(5.0), + aDateTime: Value(DateTime.now().add(Duration(days: 1))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aDateTime: Value(DateTime.now().add(Duration(days: 2))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(3.0), + aDateTime: Value(DateTime.now().add(Duration(days: 3))))); + + // Equals + expect( + db.managers.tableWithEveryColumnType + .orderBy((o) => o.aDateTime.desc()) + .get() + .then((value) => value[0].id), + completion(3)); + expect( + db.managers.tableWithEveryColumnType + .orderBy((o) => o.aDateTime.asc()) + .get() + .then((value) => value[0].id), + completion(1)); + }); + + test('manager - order related', () async { + final schoolCategoryId = await db.managers.categories.create((o) => + o(priority: Value(CategoryPriority.high), description: "School")); + final workCategoryId = await db.managers.categories.create( + (o) => o(priority: Value(CategoryPriority.low), description: "Work")); + + await db.managers.todosTable.create((o) => o( + id: Value(RowId(1)), + content: "Get that english homework done", + title: Value("English Homework"), + category: Value(workCategoryId), + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 15))))); + await db.managers.todosTable.create((o) => o( + id: Value(RowId(2)), + content: "Finish that Book report", + title: Value("Book Report"), + category: Value(workCategoryId), + status: Value(TodoStatus.done), + targetDate: + Value(DateTime.now().subtract(Duration(days: 2, seconds: 15))))); + await db.managers.todosTable.create((o) => o( + id: Value(RowId(3)), + content: "Get that math homework done", + title: Value("Math Homework"), + category: Value(schoolCategoryId), + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 10))))); + await db.managers.todosTable.create((o) => o( + id: Value(RowId(4)), + content: "Finish that report", + title: Value("Report"), + category: Value(schoolCategoryId), + status: Value(TodoStatus.workInProgress), + targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 10))))); + // Order by related + expect( + db.managers.todosTable + .orderBy((o) => o.category((o) => o.id.asc())) + .get() + .then((value) => value.map((e) => e.id).toList()), + completion([3, 4, 1, 2])); + }); +} diff --git a/drift/test/manager/processed_manager_tests.dart b/drift/test/manager/processed_manager_tests.dart new file mode 100644 index 00000000..015a9c01 --- /dev/null +++ b/drift/test/manager/processed_manager_tests.dart @@ -0,0 +1,90 @@ +import 'package:drift/drift.dart'; +import 'package:test/expect.dart'; +import 'package:test/test.dart'; + +import '../generated/todos.dart'; +import '../test_utils/test_utils.dart'; + +void main() { + late TodoDb db; + + setUp(() { + db = TodoDb(testInMemoryDatabase()); + }); + + tearDown(() => db.close()); + + test('processed manager', () async { + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(5.0), + aDateTime: Value(DateTime.now().add(Duration(days: 1))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aDateTime: Value(DateTime.now().add(Duration(days: 2))))); + await db.managers.tableWithEveryColumnType.create((o) => o( + aText: Value("Get that math homework done"), + anIntEnum: Value(TodoStatus.open), + aReal: Value(3.0), + aDateTime: Value(DateTime.now().add(Duration(days: 3))))); + // Test count + expect(db.managers.tableWithEveryColumnType.all().count(), completion(3)); + // Test get + expect( + db.managers.tableWithEveryColumnType + .all() + .get() + .then((value) => value.length), + completion(3)); + // Test getSingle with limit + expect( + db.managers.tableWithEveryColumnType + .all() + .limit(1, offset: 1) + .getSingle() + .then((value) => value.id), + completion(2)); + // Test filtered delete + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.id(RowId(2))) + .delete(), + completion(1)); + + // Test filtered update + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.id(RowId(1))) + .update((o) => o(aReal: Value(10.0))), + completion(1)); + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.id(RowId(1))) + .getSingle() + .then((value) => value.aReal), + completion(10.0)); + // Test filtered exists + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.id(RowId(1))) + .exists(), + completion(true)); + + // Test filtered count + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.id(RowId(1))) + .count(), + completion(1)); + // Test delte all + expect(db.managers.tableWithEveryColumnType.delete(), completion(2)); + // Test exists - false + expect( + db.managers.tableWithEveryColumnType + .filter((f) => f.id(RowId(1))) + .exists(), + completion(false)); + }); +} From 270f8d2a49ec118bfcb1c91176ffa7eb39d38aa4 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Tue, 16 Apr 2024 15:52:57 -0400 Subject: [PATCH 58/74] lint --- drift/test/manager/processed_manager_tests.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/drift/test/manager/processed_manager_tests.dart b/drift/test/manager/processed_manager_tests.dart index 015a9c01..ee53fd93 100644 --- a/drift/test/manager/processed_manager_tests.dart +++ b/drift/test/manager/processed_manager_tests.dart @@ -1,5 +1,4 @@ import 'package:drift/drift.dart'; -import 'package:test/expect.dart'; import 'package:test/test.dart'; import '../generated/todos.dart'; From 28bf78383229254405de6521db4c4a5fc3f912ce Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 17 Apr 2024 12:30:11 +0200 Subject: [PATCH 59/74] Scaffold manager docs page --- docs/lib/snippets/dart_api/manager.dart | 9 + .../drift_files/custom_queries.drift.dart | 222 ++++++++++++ .../modular/many_to_many/json.drift.dart | 171 +++++++++ .../many_to_many/relational.drift.dart | 334 +++++++++++++++++- docs/pages/docs/Dart API/manager.md | 33 ++ 5 files changed, 766 insertions(+), 3 deletions(-) create mode 100644 docs/lib/snippets/dart_api/manager.dart create mode 100644 docs/pages/docs/Dart API/manager.md diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart new file mode 100644 index 00000000..085efc1e --- /dev/null +++ b/docs/lib/snippets/dart_api/manager.dart @@ -0,0 +1,9 @@ +import '../setup/database.dart'; + +extension ManagerExamples on AppDatabase { + // #docregion create + Future createTodoItem() async { + await managers.todoItems.create((o) => o(title: '', content: '')); + } + // #enddocregion create +} diff --git a/docs/lib/snippets/drift_files/custom_queries.drift.dart b/docs/lib/snippets/drift_files/custom_queries.drift.dart index 6bcbe9e7..2bb8229a 100644 --- a/docs/lib/snippets/drift_files/custom_queries.drift.dart +++ b/docs/lib/snippets/drift_files/custom_queries.drift.dart @@ -1,9 +1,11 @@ // ignore_for_file: type=lint import 'package:drift/drift.dart' as i0; import 'package:drift_docs/snippets/_shared/todo_tables.drift.dart' as i1; +import 'package:drift/internal/modular.dart' as i2; abstract class $MyDatabase extends i0.GeneratedDatabase { $MyDatabase(i0.QueryExecutor e) : super(e); + $MyDatabaseManager get managers => $MyDatabaseManager(this); late final i1.$CategoriesTable categories = i1.$CategoriesTable(this); late final i1.$TodoItemsTable todoItems = i1.$TodoItemsTable(this); i0.Selectable categoriesWithCount() { @@ -28,6 +30,226 @@ abstract class $MyDatabase extends i0.GeneratedDatabase { [categories, todoItems]; } +class $$CategoriesTableFilterComposer + extends i0.FilterComposer { + $$CategoriesTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get name => i0.ColumnFilters($table.name); + i0.ComposableFilter todoItemsRefs( + i0.ComposableFilter Function($$TodoItemsTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i2.ReadDatabaseContainer($db) + .resultSet('todo_items'), + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.category, + getReferencedComposer: (db, table) => + $$TodoItemsTableFilterComposer(db, table), + builder: f); + } +} + +class $$CategoriesTableOrderingComposer + extends i0.OrderingComposer { + $$CategoriesTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get name => i0.ColumnOrderings($table.name); +} + +class $$CategoriesTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$CategoriesTable, + i1.Category, + $$CategoriesTableFilterComposer, + $$CategoriesTableOrderingComposer, + $$CategoriesTableProcessedTableManager, + $$CategoriesTableInsertCompanionBuilder, + $$CategoriesTableUpdateCompanionBuilder> { + const $$CategoriesTableProcessedTableManager(super.$state); +} + +typedef $$CategoriesTableInsertCompanionBuilder = i1.CategoriesCompanion + Function({ + i0.Value id, + required String name, +}); +typedef $$CategoriesTableUpdateCompanionBuilder = i1.CategoriesCompanion + Function({ + i0.Value id, + i0.Value name, +}); + +class $$CategoriesTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$CategoriesTable, + i1.Category, + $$CategoriesTableFilterComposer, + $$CategoriesTableOrderingComposer, + $$CategoriesTableProcessedTableManager, + $$CategoriesTableInsertCompanionBuilder, + $$CategoriesTableUpdateCompanionBuilder> { + $$CategoriesTableTableManager( + i0.GeneratedDatabase db, i1.$CategoriesTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$CategoriesTableFilterComposer(db, table), + orderingComposer: $$CategoriesTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$CategoriesTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + }) => + i1.CategoriesCompanion( + id: id, + name: name, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String name, + }) => + i1.CategoriesCompanion.insert( + id: id, + name: name, + ))); +} + +class $$TodoItemsTableFilterComposer + extends i0.FilterComposer { + $$TodoItemsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get title => i0.ColumnFilters($table.title); + i0.ColumnFilters get content => i0.ColumnFilters($table.content); + i0.ColumnFilters get categoryId => i0.ColumnFilters($table.category); + i0.ComposableFilter category( + i0.ComposableFilter Function($$CategoriesTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i2.ReadDatabaseContainer($db) + .resultSet('categories'), + getCurrentColumn: (f) => f.category, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$CategoriesTableFilterComposer(db, table), + builder: f); + } + + i0.ColumnFilters get dueDate => i0.ColumnFilters($table.dueDate); +} + +class $$TodoItemsTableOrderingComposer + extends i0.OrderingComposer { + $$TodoItemsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get title => i0.ColumnOrderings($table.title); + i0.ColumnOrderings get content => i0.ColumnOrderings($table.content); + i0.ColumnOrderings get categoryId => i0.ColumnOrderings($table.category); + i0.ComposableOrdering category( + i0.ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i2.ReadDatabaseContainer($db) + .resultSet('categories'), + getCurrentColumn: (f) => f.category, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$CategoriesTableOrderingComposer(db, table), + builder: o); + } + + i0.ColumnOrderings get dueDate => i0.ColumnOrderings($table.dueDate); +} + +class $$TodoItemsTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$TodoItemsTable, + i1.TodoItem, + $$TodoItemsTableFilterComposer, + $$TodoItemsTableOrderingComposer, + $$TodoItemsTableProcessedTableManager, + $$TodoItemsTableInsertCompanionBuilder, + $$TodoItemsTableUpdateCompanionBuilder> { + const $$TodoItemsTableProcessedTableManager(super.$state); +} + +typedef $$TodoItemsTableInsertCompanionBuilder = i1.TodoItemsCompanion + Function({ + i0.Value id, + required String title, + required String content, + i0.Value category, + i0.Value dueDate, +}); +typedef $$TodoItemsTableUpdateCompanionBuilder = i1.TodoItemsCompanion + Function({ + i0.Value id, + i0.Value title, + i0.Value content, + i0.Value category, + i0.Value dueDate, +}); + +class $$TodoItemsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$TodoItemsTable, + i1.TodoItem, + $$TodoItemsTableFilterComposer, + $$TodoItemsTableOrderingComposer, + $$TodoItemsTableProcessedTableManager, + $$TodoItemsTableInsertCompanionBuilder, + $$TodoItemsTableUpdateCompanionBuilder> { + $$TodoItemsTableTableManager( + i0.GeneratedDatabase db, i1.$TodoItemsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$TodoItemsTableFilterComposer(db, table), + orderingComposer: $$TodoItemsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$TodoItemsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value title = const i0.Value.absent(), + i0.Value content = const i0.Value.absent(), + i0.Value category = const i0.Value.absent(), + i0.Value dueDate = const i0.Value.absent(), + }) => + i1.TodoItemsCompanion( + id: id, + title: title, + content: content, + category: category, + dueDate: dueDate, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String title, + required String content, + i0.Value category = const i0.Value.absent(), + i0.Value dueDate = const i0.Value.absent(), + }) => + i1.TodoItemsCompanion.insert( + id: id, + title: title, + content: content, + category: category, + dueDate: dueDate, + ))); +} + +class $MyDatabaseManager { + final $MyDatabase _db; + $MyDatabaseManager(this._db); + $$CategoriesTableTableManager get categories => + $$CategoriesTableTableManager(_db, _db.categories); + $$TodoItemsTableTableManager get todoItems => + $$TodoItemsTableTableManager(_db, _db.todoItems); +} + class CategoriesWithCountResult { final int id; final String name; diff --git a/docs/lib/snippets/modular/many_to_many/json.drift.dart b/docs/lib/snippets/modular/many_to_many/json.drift.dart index a7bdd538..18a64f91 100644 --- a/docs/lib/snippets/modular/many_to_many/json.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/json.drift.dart @@ -7,6 +7,7 @@ import 'package:drift_docs/snippets/modular/many_to_many/json.dart' as i3; abstract class $JsonBasedDatabase extends i0.GeneratedDatabase { $JsonBasedDatabase(i0.QueryExecutor e) : super(e); + $JsonBasedDatabaseManager get managers => $JsonBasedDatabaseManager(this); late final i1.$BuyableItemsTable buyableItems = i1.$BuyableItemsTable(this); late final i2.$ShoppingCartsTable shoppingCarts = i2.$ShoppingCartsTable(this); @@ -18,6 +19,176 @@ abstract class $JsonBasedDatabase extends i0.GeneratedDatabase { [buyableItems, shoppingCarts]; } +class $$BuyableItemsTableFilterComposer + extends i0.FilterComposer { + $$BuyableItemsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get description => + i0.ColumnFilters($table.description); + i0.ColumnFilters get price => i0.ColumnFilters($table.price); +} + +class $$BuyableItemsTableOrderingComposer + extends i0.OrderingComposer { + $$BuyableItemsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); + i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); +} + +class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + const $$BuyableItemsTableProcessedTableManager(super.$state); +} + +typedef $$BuyableItemsTableInsertCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + required String description, + required int price, +}); +typedef $$BuyableItemsTableUpdateCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + i0.Value description, + i0.Value price, +}); + +class $$BuyableItemsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + $$BuyableItemsTableTableManager( + i0.GeneratedDatabase db, i1.$BuyableItemsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$BuyableItemsTableFilterComposer(db, table), + orderingComposer: $$BuyableItemsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$BuyableItemsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value price = const i0.Value.absent(), + }) => + i1.BuyableItemsCompanion( + id: id, + description: description, + price: price, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String description, + required int price, + }) => + i1.BuyableItemsCompanion.insert( + id: id, + description: description, + price: price, + ))); +} + +class $$ShoppingCartsTableFilterComposer + extends i0.FilterComposer { + $$ShoppingCartsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get entriesValue => i0.ColumnFilters($table.entries); + i0.ColumnWithTypeConverterFilters< + i3.ShoppingCartEntries, + i3.ShoppingCartEntries, + String> get entries => i0.ColumnWithTypeConverterFilters($table.entries); +} + +class $$ShoppingCartsTableOrderingComposer + extends i0.OrderingComposer { + $$ShoppingCartsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get entries => i0.ColumnOrderings($table.entries); +} + +class $$ShoppingCartsTableProcessedTableManager + extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + const $$ShoppingCartsTableProcessedTableManager(super.$state); +} + +typedef $$ShoppingCartsTableInsertCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, + required i3.ShoppingCartEntries entries, +}); +typedef $$ShoppingCartsTableUpdateCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, + i0.Value entries, +}); + +class $$ShoppingCartsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + $$ShoppingCartsTableTableManager( + i0.GeneratedDatabase db, i2.$ShoppingCartsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$ShoppingCartsTableFilterComposer(db, table), + orderingComposer: $$ShoppingCartsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ShoppingCartsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value entries = + const i0.Value.absent(), + }) => + i2.ShoppingCartsCompanion( + id: id, + entries: entries, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required i3.ShoppingCartEntries entries, + }) => + i2.ShoppingCartsCompanion.insert( + id: id, + entries: entries, + ))); +} + +class $JsonBasedDatabaseManager { + final $JsonBasedDatabase _db; + $JsonBasedDatabaseManager(this._db); + $$BuyableItemsTableTableManager get buyableItems => + $$BuyableItemsTableTableManager(_db, _db.buyableItems); + $$ShoppingCartsTableTableManager get shoppingCarts => + $$ShoppingCartsTableTableManager(_db, _db.shoppingCarts); +} + class $ShoppingCartsTable extends i3.ShoppingCarts with i0.TableInfo<$ShoppingCartsTable, i2.ShoppingCart> { @override diff --git a/docs/lib/snippets/modular/many_to_many/relational.drift.dart b/docs/lib/snippets/modular/many_to_many/relational.drift.dart index 1d1bab75..ef569503 100644 --- a/docs/lib/snippets/modular/many_to_many/relational.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/relational.drift.dart @@ -4,10 +4,12 @@ import 'package:drift_docs/snippets/modular/many_to_many/shared.drift.dart' as i1; import 'package:drift_docs/snippets/modular/many_to_many/relational.drift.dart' as i2; -import 'package:drift_docs/snippets/modular/many_to_many/relational.dart' as i3; +import 'package:drift/internal/modular.dart' as i3; +import 'package:drift_docs/snippets/modular/many_to_many/relational.dart' as i4; abstract class $RelationalDatabase extends i0.GeneratedDatabase { $RelationalDatabase(i0.QueryExecutor e) : super(e); + $RelationalDatabaseManager get managers => $RelationalDatabaseManager(this); late final i1.$BuyableItemsTable buyableItems = i1.$BuyableItemsTable(this); late final i2.$ShoppingCartsTable shoppingCarts = i2.$ShoppingCartsTable(this); @@ -21,7 +23,333 @@ abstract class $RelationalDatabase extends i0.GeneratedDatabase { [buyableItems, shoppingCarts, shoppingCartEntries]; } -class $ShoppingCartsTable extends i3.ShoppingCarts +class $$BuyableItemsTableFilterComposer + extends i0.FilterComposer { + $$BuyableItemsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ColumnFilters get description => + i0.ColumnFilters($table.description); + i0.ColumnFilters get price => i0.ColumnFilters($table.price); + i0.ComposableFilter shoppingCartEntriesRefs( + i0.ComposableFilter Function($$ShoppingCartEntriesTableFilterComposer f) + f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_cart_entries'), + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.item, + getReferencedComposer: (db, table) => + $$ShoppingCartEntriesTableFilterComposer(db, table), + builder: f); + } +} + +class $$BuyableItemsTableOrderingComposer + extends i0.OrderingComposer { + $$BuyableItemsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); + i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); +} + +class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + const $$BuyableItemsTableProcessedTableManager(super.$state); +} + +typedef $$BuyableItemsTableInsertCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + required String description, + required int price, +}); +typedef $$BuyableItemsTableUpdateCompanionBuilder = i1.BuyableItemsCompanion + Function({ + i0.Value id, + i0.Value description, + i0.Value price, +}); + +class $$BuyableItemsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$BuyableItemsTable, + i1.BuyableItem, + $$BuyableItemsTableFilterComposer, + $$BuyableItemsTableOrderingComposer, + $$BuyableItemsTableProcessedTableManager, + $$BuyableItemsTableInsertCompanionBuilder, + $$BuyableItemsTableUpdateCompanionBuilder> { + $$BuyableItemsTableTableManager( + i0.GeneratedDatabase db, i1.$BuyableItemsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$BuyableItemsTableFilterComposer(db, table), + orderingComposer: $$BuyableItemsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$BuyableItemsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value price = const i0.Value.absent(), + }) => + i1.BuyableItemsCompanion( + id: id, + description: description, + price: price, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + required String description, + required int price, + }) => + i1.BuyableItemsCompanion.insert( + id: id, + description: description, + price: price, + ))); +} + +class $$ShoppingCartsTableFilterComposer + extends i0.FilterComposer { + $$ShoppingCartsTableFilterComposer(super.db, super.table); + i0.ColumnFilters get id => i0.ColumnFilters($table.id); + i0.ComposableFilter shoppingCartEntriesRefs( + i0.ComposableFilter Function($$ShoppingCartEntriesTableFilterComposer f) + f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_cart_entries'), + getCurrentColumn: (f) => f.id, + getReferencedColumn: (f) => f.shoppingCart, + getReferencedComposer: (db, table) => + $$ShoppingCartEntriesTableFilterComposer(db, table), + builder: f); + } +} + +class $$ShoppingCartsTableOrderingComposer + extends i0.OrderingComposer { + $$ShoppingCartsTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); +} + +class $$ShoppingCartsTableProcessedTableManager + extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + const $$ShoppingCartsTableProcessedTableManager(super.$state); +} + +typedef $$ShoppingCartsTableInsertCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, +}); +typedef $$ShoppingCartsTableUpdateCompanionBuilder = i2.ShoppingCartsCompanion + Function({ + i0.Value id, +}); + +class $$ShoppingCartsTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartsTable, + i2.ShoppingCart, + $$ShoppingCartsTableFilterComposer, + $$ShoppingCartsTableOrderingComposer, + $$ShoppingCartsTableProcessedTableManager, + $$ShoppingCartsTableInsertCompanionBuilder, + $$ShoppingCartsTableUpdateCompanionBuilder> { + $$ShoppingCartsTableTableManager( + i0.GeneratedDatabase db, i2.$ShoppingCartsTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: $$ShoppingCartsTableFilterComposer(db, table), + orderingComposer: $$ShoppingCartsTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ShoppingCartsTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + }) => + i2.ShoppingCartsCompanion( + id: id, + ), + getInsertCompanionBuilder: ({ + i0.Value id = const i0.Value.absent(), + }) => + i2.ShoppingCartsCompanion.insert( + id: id, + ))); +} + +class $$ShoppingCartEntriesTableFilterComposer extends i0 + .FilterComposer { + $$ShoppingCartEntriesTableFilterComposer(super.db, super.table); + i0.ColumnFilters get shoppingCartId => + i0.ColumnFilters($table.shoppingCart); + i0.ComposableFilter shoppingCart( + i0.ComposableFilter Function($$ShoppingCartsTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_carts'), + getCurrentColumn: (f) => f.shoppingCart, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$ShoppingCartsTableFilterComposer(db, table), + builder: f); + } + + i0.ColumnFilters get itemId => i0.ColumnFilters($table.item); + i0.ComposableFilter item( + i0.ComposableFilter Function($$BuyableItemsTableFilterComposer f) f) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('buyable_items'), + getCurrentColumn: (f) => f.item, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$BuyableItemsTableFilterComposer(db, table), + builder: f); + } +} + +class $$ShoppingCartEntriesTableOrderingComposer extends i0 + .OrderingComposer { + $$ShoppingCartEntriesTableOrderingComposer(super.db, super.table); + i0.ColumnOrderings get shoppingCartId => + i0.ColumnOrderings($table.shoppingCart); + i0.ComposableOrdering shoppingCart( + i0.ComposableOrdering Function($$ShoppingCartsTableOrderingComposer o) + o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('shopping_carts'), + getCurrentColumn: (f) => f.shoppingCart, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$ShoppingCartsTableOrderingComposer(db, table), + builder: o); + } + + i0.ColumnOrderings get itemId => i0.ColumnOrderings($table.item); + i0.ComposableOrdering item( + i0.ComposableOrdering Function($$BuyableItemsTableOrderingComposer o) o) { + return $composeWithJoins( + $db: $db, + $table: $table, + referencedTable: i3.ReadDatabaseContainer($db) + .resultSet('buyable_items'), + getCurrentColumn: (f) => f.item, + getReferencedColumn: (f) => f.id, + getReferencedComposer: (db, table) => + $$BuyableItemsTableOrderingComposer(db, table), + builder: o); + } +} + +class $$ShoppingCartEntriesTableProcessedTableManager + extends i0.ProcessedTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartEntriesTable, + i2.ShoppingCartEntry, + $$ShoppingCartEntriesTableFilterComposer, + $$ShoppingCartEntriesTableOrderingComposer, + $$ShoppingCartEntriesTableProcessedTableManager, + $$ShoppingCartEntriesTableInsertCompanionBuilder, + $$ShoppingCartEntriesTableUpdateCompanionBuilder> { + const $$ShoppingCartEntriesTableProcessedTableManager(super.$state); +} + +typedef $$ShoppingCartEntriesTableInsertCompanionBuilder + = i2.ShoppingCartEntriesCompanion Function({ + required int shoppingCart, + required int item, + i0.Value rowid, +}); +typedef $$ShoppingCartEntriesTableUpdateCompanionBuilder + = i2.ShoppingCartEntriesCompanion Function({ + i0.Value shoppingCart, + i0.Value item, + i0.Value rowid, +}); + +class $$ShoppingCartEntriesTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i2.$ShoppingCartEntriesTable, + i2.ShoppingCartEntry, + $$ShoppingCartEntriesTableFilterComposer, + $$ShoppingCartEntriesTableOrderingComposer, + $$ShoppingCartEntriesTableProcessedTableManager, + $$ShoppingCartEntriesTableInsertCompanionBuilder, + $$ShoppingCartEntriesTableUpdateCompanionBuilder> { + $$ShoppingCartEntriesTableTableManager( + i0.GeneratedDatabase db, i2.$ShoppingCartEntriesTable table) + : super(i0.TableManagerState( + db: db, + table: table, + filteringComposer: + $$ShoppingCartEntriesTableFilterComposer(db, table), + orderingComposer: + $$ShoppingCartEntriesTableOrderingComposer(db, table), + getChildManagerBuilder: (p0) => + $$ShoppingCartEntriesTableProcessedTableManager(p0), + getUpdateCompanionBuilder: ({ + i0.Value shoppingCart = const i0.Value.absent(), + i0.Value item = const i0.Value.absent(), + i0.Value rowid = const i0.Value.absent(), + }) => + i2.ShoppingCartEntriesCompanion( + shoppingCart: shoppingCart, + item: item, + rowid: rowid, + ), + getInsertCompanionBuilder: ({ + required int shoppingCart, + required int item, + i0.Value rowid = const i0.Value.absent(), + }) => + i2.ShoppingCartEntriesCompanion.insert( + shoppingCart: shoppingCart, + item: item, + rowid: rowid, + ))); +} + +class $RelationalDatabaseManager { + final $RelationalDatabase _db; + $RelationalDatabaseManager(this._db); + $$BuyableItemsTableTableManager get buyableItems => + $$BuyableItemsTableTableManager(_db, _db.buyableItems); + $$ShoppingCartsTableTableManager get shoppingCarts => + $$ShoppingCartsTableTableManager(_db, _db.shoppingCarts); + $$ShoppingCartEntriesTableTableManager get shoppingCartEntries => + $$ShoppingCartEntriesTableTableManager(_db, _db.shoppingCartEntries); +} + +class $ShoppingCartsTable extends i4.ShoppingCarts with i0.TableInfo<$ShoppingCartsTable, i2.ShoppingCart> { @override final i0.GeneratedDatabase attachedDatabase; @@ -163,7 +491,7 @@ class ShoppingCartsCompanion extends i0.UpdateCompanion { } } -class $ShoppingCartEntriesTable extends i3.ShoppingCartEntries +class $ShoppingCartEntriesTable extends i4.ShoppingCartEntries with i0.TableInfo<$ShoppingCartEntriesTable, i2.ShoppingCartEntry> { @override final i0.GeneratedDatabase attachedDatabase; diff --git a/docs/pages/docs/Dart API/manager.md b/docs/pages/docs/Dart API/manager.md new file mode 100644 index 00000000..f4ac99fb --- /dev/null +++ b/docs/pages/docs/Dart API/manager.md @@ -0,0 +1,33 @@ +--- +data: + title: Manager + description: Use easier bindings for common queries. + weight: 1 + +template: layouts/docs/single +--- + +{% assign snippets = 'package:drift_docs/snippets/dart_api/manager.dart.excerpt.json' | readString | json_decode %} + +With generated code, drift allows writing SQL queries in typesafe Dart. +While this is provides lots of flexibility, it requires familiarity with SQL. +As a simpler alternative, drift 2.17 introduced a new set of APIs designed to +make common queries much easier to write. + +The examples on this page use the database from the [setup]({{ '../setup.md' | pageUrl }}) +instructions. + +## Select + +### Count and exists + +### Filtering across tables + +## Updates + +## Creating rows + +{% include "blocks/snippet" snippets = snippets name = 'create' %} + +## Deleting rows + From 22ec0f77a67e8abd560cc100850106b7c484e5cf Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 12:23:21 -0400 Subject: [PATCH 60/74] move count to base table manager --- drift/lib/src/runtime/manager/manager.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 42ee79ae..22348dfe 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -342,6 +342,12 @@ abstract class BaseTableManager< /// supports setting fields back to null. Future update(Insertable
Function(CU o) f) => $state.buildUpdateStatement().write(f($state._getUpdateCompanionBuilder)); + + /// Return the count of rows matched by the built statement + /// When counting rows, the query will only count distinct rows by default + Future count([bool distinct = true]) { + return $state.copyWith(distinct: true).count(); + } } /// A table manager that can be used to select rows from a table @@ -362,12 +368,6 @@ abstract class ProcessedTableManager< /// Create a new [ProcessedTableManager] instance const ProcessedTableManager(super.$state); - /// Return the count of rows matched by the built statement - /// When counting rows, the query will only count distinct rows by default - Future count([bool distinct = true]) { - return $state.copyWith(distinct: true).count(); - } - /// Checks whether any rows exist Future exists() => $state.exists(); From f17c251126d0e2b02f0ad2d2c9b53ec5f1b8d97d Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 12:23:42 -0400 Subject: [PATCH 61/74] move count to base manager --- drift/lib/src/runtime/manager/manager.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 22348dfe..0485f0f8 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -348,6 +348,9 @@ abstract class BaseTableManager< Future count([bool distinct = true]) { return $state.copyWith(distinct: true).count(); } + + /// Checks whether any rows exist + Future exists() => $state.exists(); } /// A table manager that can be used to select rows from a table @@ -368,9 +371,6 @@ abstract class ProcessedTableManager< /// Create a new [ProcessedTableManager] instance const ProcessedTableManager(super.$state); - /// Checks whether any rows exist - Future exists() => $state.exists(); - /// Deletes all rows matched by built statement /// /// Returns the amount of rows that were deleted by this statement directly From 4c7f7402c1f27514440a755b1891ea75d6205d7c Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 13:04:47 -0400 Subject: [PATCH 62/74] 1) Apply Limit to Single Getting 2) Move Distict to positional arg 3) Add limit as a positional arg 4) Fix _tests.dart to _test.dart for CI 5) Move all methods to base - Remove all() --- drift/lib/src/runtime/manager/manager.dart | 127 ++++++++++-------- ...er_tests.dart => manager_filter_test.dart} | 1 - ...der_tests.dart => manager_order_test.dart} | 0 .../{manager_tests.dart => manager_test.dart} | 18 +-- ...tests.dart => processed_manager_test.dart} | 4 +- 5 files changed, 82 insertions(+), 68 deletions(-) rename drift/test/manager/{manager_filter_tests.dart => manager_filter_test.dart} (99%) rename drift/test/manager/{manager_order_tests.dart => manager_order_test.dart} (100%) rename drift/test/manager/{manager_tests.dart => manager_test.dart} (89%) rename drift/test/manager/{processed_manager_tests.dart => processed_manager_test.dart} (95%) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 0485f0f8..446e69b7 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -283,24 +283,28 @@ class TableManagerState< /// This is so that the state can be passed down to lower level managers @internal abstract class BaseTableManager< - DB extends GeneratedDatabase, - T extends Table, - DT extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer, - C extends ProcessedTableManager, - CI extends Function, - CU extends Function> { + DB extends GeneratedDatabase, + T extends Table, + DT extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager, + CI extends Function, + CU extends Function> + implements + MultiSelectable
, + SingleSelectable
, + SingleOrNullSelectable
{ /// The state for this manager final TableManagerState $state; /// Create a new [BaseTableManager] instance const BaseTableManager(this.$state); - /// Set the distinct flag on the statement to true - /// This will ensure that only distinct rows are returned - C distict() { - return $state._getChildManagerBuilder($state.copyWith(distinct: true)); + /// Add a limit to the statement + C limit(int limit, {int? offset}) { + return $state + ._getChildManagerBuilder($state.copyWith(limit: limit, offset: offset)); } /// Add ordering to the statement @@ -322,12 +326,6 @@ abstract class BaseTableManager< joinBuilders: $state.joinBuilders.union(filter.joinBuilders))); } - /// Add a limit to the statement - C limit(int limit, {int? offset}) { - return $state - ._getChildManagerBuilder($state.copyWith(limit: limit, offset: offset)); - } - /// Writes all non-null fields from the entity into the columns of all rows /// that match the [filter] clause. Warning: That also means that, when you're /// not setting a where clause explicitly, this method will update all rows in @@ -351,25 +349,6 @@ abstract class BaseTableManager< /// Checks whether any rows exist Future exists() => $state.exists(); -} - -/// A table manager that can be used to select rows from a table -abstract class ProcessedTableManager< - DB extends GeneratedDatabase, - T extends Table, - D extends DataClass, - FS extends FilterComposer, - OS extends OrderingComposer, - C extends ProcessedTableManager, - CI extends Function, - CU extends Function> - extends BaseTableManager - implements - MultiSelectable, - SingleSelectable, - SingleOrNullSelectable { - /// Create a new [ProcessedTableManager] instance - const ProcessedTableManager(super.$state); /// Deletes all rows matched by built statement /// @@ -390,25 +369,47 @@ abstract class ProcessedTableManager< /// /// See also: [getSingleOrNull], which returns `null` instead of /// throwing if the query completes with no rows. + /// + /// Uses the distinct flag to ensure that only distinct rows are returned @override - Future getSingle() => $state.buildSelectStatement().getSingle(); + Future
getSingle() => + $state.copyWith(distinct: true).buildSelectStatement().getSingle(); /// Creates an auto-updating stream of this statement, similar to /// [watch]. However, it is assumed that the query will only emit /// one result, so instead of returning a `Stream>`, this returns a /// `Stream`. If, at any point, the query emits no or more than one rows, /// an error will be added to the stream instead. + /// + /// Uses the distinct flag to ensure that only distinct rows are returned @override - Stream watchSingle() => $state.buildSelectStatement().watchSingle(); + Stream
watchSingle() => + $state.copyWith(distinct: true).buildSelectStatement().watchSingle(); /// Executes the statement and returns the first all rows as a list. + /// + /// Use [limit] and [offset] to limit the number of rows returned + /// An offset will only be applied if a limit is also set + /// Set [distinct] to true to ensure that only distinct rows are returned @override - Future> get() => $state.buildSelectStatement().get(); + Future> get({bool distinct = false, int? limit, int? offset}) => + $state + .copyWith(distinct: distinct, limit: limit, offset: offset) + .buildSelectStatement() + .get(); /// Creates an auto-updating stream of the result that emits new items /// whenever any table used in this statement changes. + /// + /// Use [limit] and [offset] to limit the number of rows returned + /// An offset will only be applied if a limit is also set + /// Set [distinct] to true to ensure that only distinct rows are returned @override - Stream> watch() => $state.buildSelectStatement().watch(); + Stream> watch({bool distinct = false, int? limit, int? offset}) => + $state + .copyWith(distinct: distinct, limit: limit, offset: offset) + .buildSelectStatement() + .watch(); /// Executes this statement, like [get], but only returns one /// value. If the result too many values, this method will throw. If no @@ -416,9 +417,11 @@ abstract class ProcessedTableManager< /// /// See also: [getSingle], which can be used if the query will /// always evaluate to exactly one row. + /// + /// Uses the distinct flag to ensure that only distinct rows are returned @override - Future getSingleOrNull() => - $state.buildSelectStatement().getSingleOrNull(); + Future getSingleOrNull() => + $state.copyWith(distinct: true).buildSelectStatement().getSingleOrNull(); /// Creates an auto-updating stream of this statement, similar to /// [watch]. However, it is assumed that the query will only @@ -427,9 +430,33 @@ abstract class ProcessedTableManager< /// some point, an error will be emitted to the stream instead. /// If the query emits zero rows at some point, `null` will be added /// to the stream instead. + /// + /// Uses the distinct flag to ensure that only distinct rows are returned @override - Stream watchSingleOrNull() => - $state.buildSelectStatement().watchSingleOrNull(); + Stream watchSingleOrNull() => $state + .copyWith(distinct: true) + .buildSelectStatement() + .watchSingleOrNull(); +} + +/// A table manager that exposes methods to a table manager that already has filters/orderings/limit applied +// As of now this is identical to [BaseTableManager] but it's kept seperate for future extensibility +class ProcessedTableManager< + DB extends GeneratedDatabase, + T extends Table, + D extends DataClass, + FS extends FilterComposer, + OS extends OrderingComposer, + C extends ProcessedTableManager, + CI extends Function, + CU extends Function> + extends BaseTableManager + implements + MultiSelectable, + SingleSelectable, + SingleOrNullSelectable { + /// Create a new [ProcessedTableManager] instance + const ProcessedTableManager(super.$state); } /// A table manager with top level function for creating, reading, updating, and deleting items @@ -445,16 +472,6 @@ abstract class RootTableManager< /// Create a new [RootTableManager] instance const RootTableManager(super.$state); - /// Deletes all rows matched by built statement - /// - /// Returns the amount of rows that were deleted by this statement directly - /// (not including additional rows that might be affected through triggers or - /// foreign key constraints). - Future delete() => $state.db.delete($state._tableAsTableInfo).go(); - - /// Select all rows from the table - C all() => $state._getChildManagerBuilder($state); - /// Creates a new row in the table using the given function /// /// By default, an exception will be thrown if another row with the same diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_test.dart similarity index 99% rename from drift/test/manager/manager_filter_tests.dart rename to drift/test/manager/manager_filter_test.dart index 4a074bca..38775d02 100644 --- a/drift/test/manager/manager_filter_tests.dart +++ b/drift/test/manager/manager_filter_test.dart @@ -533,7 +533,6 @@ void main() { db.managers.categories .filter((f) => f.todos((f) => f.category( (f) => f.todos((f) => f.title.equals("Math Homework"))))) - .distict() .getSingle() .then((value) => value.description), completion("School")); diff --git a/drift/test/manager/manager_order_tests.dart b/drift/test/manager/manager_order_test.dart similarity index 100% rename from drift/test/manager/manager_order_tests.dart rename to drift/test/manager/manager_order_test.dart diff --git a/drift/test/manager/manager_tests.dart b/drift/test/manager/manager_test.dart similarity index 89% rename from drift/test/manager/manager_tests.dart rename to drift/test/manager/manager_test.dart index 9f3a83c4..db0c2d01 100644 --- a/drift/test/manager/manager_tests.dart +++ b/drift/test/manager/manager_test.dart @@ -16,19 +16,19 @@ void main() { test('manager - create', () async { // Initial count should be 0 - expect(db.managers.categories.all().count(), completion(0)); + expect(db.managers.categories.count(), completion(0)); // Creating a row should return the id final create1 = db.managers.categories.create( (o) => o(priority: Value(CategoryPriority.high), description: "High")); expect(create1, completion(1)); - expect(db.managers.categories.all().count(), completion(1)); + expect(db.managers.categories.count(), completion(1)); // Creating another row should increment the id final create2 = db.managers.categories.create( (o) => o(priority: Value(CategoryPriority.low), description: "Low")); expect(create2, completion(2)); - expect(db.managers.categories.all().count(), completion(2)); + expect(db.managers.categories.count(), completion(2)); // Using an existing id should throw an exception final create3 = db.managers.categories.create((o) => o( @@ -47,7 +47,7 @@ void main() { onConflict: DoNothing()); // The is incorrect when using onConflict expect(create4, completion(2)); - expect(db.managers.categories.all().count(), completion(2)); + expect(db.managers.categories.count(), completion(2)); // Likewise, test that mode is passed to the create method final create5 = db.managers.categories.create( @@ -59,13 +59,13 @@ void main() { // The is incorrect when using mode expect(create5, completion(2)); - expect(db.managers.categories.all().count(), completion(2)); + expect(db.managers.categories.count(), completion(2)); // Test the other create methods final create6 = db.managers.categories.createReturning((o) => o(priority: Value(CategoryPriority.high), description: "Other High")); expect(create6, completion(isA())); - expect(db.managers.categories.all().count(), completion(3)); + expect(db.managers.categories.count(), completion(3)); // Will return null because the description is not unique final create7 = db.managers.categories.createReturningOrNull( @@ -84,7 +84,7 @@ void main() { priority: Value(CategoryPriority.medium), description: "Super Medium") ]); - expect(db.managers.categories.all().count(), completion(6)); + expect(db.managers.categories.count(), completion(6)); }); test('manager - update', () async { @@ -135,11 +135,11 @@ void main() { final delete1 = db.managers.categories.filter(((f) => f.id(obj1.id))).delete(); expect(delete1, completion(1)); - expect(db.managers.categories.all().count(), completion(1)); + expect(db.managers.categories.count(), completion(1)); // Delete all rows final delete2 = db.managers.categories.delete(); expect(delete2, completion(1)); - expect(db.managers.categories.all().count(), completion(0)); + expect(db.managers.categories.count(), completion(0)); }); } diff --git a/drift/test/manager/processed_manager_tests.dart b/drift/test/manager/processed_manager_test.dart similarity index 95% rename from drift/test/manager/processed_manager_tests.dart rename to drift/test/manager/processed_manager_test.dart index ee53fd93..8cf01e2b 100644 --- a/drift/test/manager/processed_manager_tests.dart +++ b/drift/test/manager/processed_manager_test.dart @@ -29,18 +29,16 @@ void main() { aReal: Value(3.0), aDateTime: Value(DateTime.now().add(Duration(days: 3))))); // Test count - expect(db.managers.tableWithEveryColumnType.all().count(), completion(3)); + expect(db.managers.tableWithEveryColumnType.count(), completion(3)); // Test get expect( db.managers.tableWithEveryColumnType - .all() .get() .then((value) => value.length), completion(3)); // Test getSingle with limit expect( db.managers.tableWithEveryColumnType - .all() .limit(1, offset: 1) .getSingle() .then((value) => value.id), From ae41baaf7b345a7ac5698f19cc8bf289611da799 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 13:27:27 -0400 Subject: [PATCH 63/74] remove manager example --- examples/manager/.gitignore | 43 - examples/manager/.metadata | 45 - examples/manager/README.md | 16 - examples/manager/analysis_options.yaml | 28 - examples/manager/android/.gitignore | 13 - examples/manager/android/app/build.gradle | 67 - .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 44 - .../com/example/manager/MainActivity.kt | 5 - .../res/drawable-v21/launch_background.xml | 12 - .../main/res/drawable/launch_background.xml | 12 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values-night/styles.xml | 18 - .../app/src/main/res/values/styles.xml | 18 - .../app/src/profile/AndroidManifest.xml | 7 - examples/manager/android/build.gradle | 18 - examples/manager/android/gradle.properties | 3 - .../gradle/wrapper/gradle-wrapper.properties | 5 - examples/manager/android/settings.gradle | 26 - examples/manager/ios/.gitignore | 34 - .../ios/Flutter/AppFrameworkInfo.plist | 26 - examples/manager/ios/Flutter/Debug.xcconfig | 1 - examples/manager/ios/Flutter/Release.xcconfig | 1 - .../ios/Runner.xcodeproj/project.pbxproj | 616 -------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 98 -- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - examples/manager/ios/Runner/AppDelegate.swift | 13 - .../AppIcon.appiconset/Contents.json | 122 -- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 295 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 450 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 282 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 462 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 704 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 586 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 1674 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 762 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 1226 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 1418 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/README.md | 5 - .../Runner/Base.lproj/LaunchScreen.storyboard | 37 - .../ios/Runner/Base.lproj/Main.storyboard | 26 - examples/manager/ios/Runner/Info.plist | 49 - .../ios/Runner/Runner-Bridging-Header.h | 1 - .../manager/ios/RunnerTests/RunnerTests.swift | 12 - examples/manager/lib/database.dart | 42 - examples/manager/lib/database.g.dart | 1405 ----------------- examples/manager/lib/main.dart | 63 - examples/manager/lib/pages/listings.dart | 151 -- examples/manager/lib/pages/owners.dart | 70 - examples/manager/lib/pages/product.dart | 113 -- examples/manager/lib/pages/store.dart | 111 -- examples/manager/lib/tables.dart | 41 - examples/manager/linux/.gitignore | 1 - examples/manager/linux/CMakeLists.txt | 145 -- examples/manager/linux/flutter/CMakeLists.txt | 88 -- .../flutter/generated_plugin_registrant.cc | 15 - .../flutter/generated_plugin_registrant.h | 15 - .../linux/flutter/generated_plugins.cmake | 24 - examples/manager/linux/main.cc | 6 - examples/manager/linux/my_application.cc | 124 -- examples/manager/linux/my_application.h | 18 - examples/manager/macos/.gitignore | 7 - .../macos/Flutter/Flutter-Debug.xcconfig | 1 - .../macos/Flutter/Flutter-Release.xcconfig | 1 - .../Flutter/GeneratedPluginRegistrant.swift | 14 - .../macos/Runner.xcodeproj/project.pbxproj | 705 --------- .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 98 -- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../manager/macos/Runner/AppDelegate.swift | 9 - .../AppIcon.appiconset/Contents.json | 68 - .../AppIcon.appiconset/app_icon_1024.png | Bin 102994 -> 0 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 5680 -> 0 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 520 -> 0 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 14142 -> 0 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 1066 -> 0 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 36406 -> 0 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 2218 -> 0 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 ---- .../macos/Runner/Configs/AppInfo.xcconfig | 14 - .../macos/Runner/Configs/Debug.xcconfig | 2 - .../macos/Runner/Configs/Release.xcconfig | 2 - .../macos/Runner/Configs/Warnings.xcconfig | 13 - .../macos/Runner/DebugProfile.entitlements | 12 - examples/manager/macos/Runner/Info.plist | 32 - .../macos/Runner/MainFlutterWindow.swift | 15 - .../manager/macos/Runner/Release.entitlements | 8 - .../macos/RunnerTests/RunnerTests.swift | 12 - examples/manager/pubspec.yaml | 41 - examples/manager/test/widget_test.dart | 30 - examples/manager/web/favicon.png | Bin 917 -> 0 bytes examples/manager/web/icons/Icon-192.png | Bin 5292 -> 0 bytes examples/manager/web/icons/Icon-512.png | Bin 8252 -> 0 bytes .../manager/web/icons/Icon-maskable-192.png | Bin 5594 -> 0 bytes .../manager/web/icons/Icon-maskable-512.png | Bin 20998 -> 0 bytes examples/manager/web/index.html | 59 - examples/manager/web/manifest.json | 35 - examples/manager/windows/.gitignore | 17 - examples/manager/windows/CMakeLists.txt | 108 -- .../manager/windows/flutter/CMakeLists.txt | 109 -- .../flutter/generated_plugin_registrant.cc | 14 - .../flutter/generated_plugin_registrant.h | 15 - .../windows/flutter/generated_plugins.cmake | 24 - .../manager/windows/runner/CMakeLists.txt | 40 - examples/manager/windows/runner/Runner.rc | 121 -- .../manager/windows/runner/flutter_window.cpp | 71 - .../manager/windows/runner/flutter_window.h | 33 - examples/manager/windows/runner/main.cpp | 43 - examples/manager/windows/runner/resource.h | 16 - .../windows/runner/resources/app_icon.ico | Bin 33772 -> 0 bytes .../windows/runner/runner.exe.manifest | 20 - examples/manager/windows/runner/utils.cpp | 65 - examples/manager/windows/runner/utils.h | 19 - .../manager/windows/runner/win32_window.cpp | 288 ---- .../manager/windows/runner/win32_window.h | 102 -- 134 files changed, 6578 deletions(-) delete mode 100644 examples/manager/.gitignore delete mode 100644 examples/manager/.metadata delete mode 100644 examples/manager/README.md delete mode 100644 examples/manager/analysis_options.yaml delete mode 100644 examples/manager/android/.gitignore delete mode 100644 examples/manager/android/app/build.gradle delete mode 100644 examples/manager/android/app/src/debug/AndroidManifest.xml delete mode 100644 examples/manager/android/app/src/main/AndroidManifest.xml delete mode 100644 examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt delete mode 100644 examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml delete mode 100644 examples/manager/android/app/src/main/res/drawable/launch_background.xml delete mode 100644 examples/manager/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 examples/manager/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 examples/manager/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 examples/manager/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 examples/manager/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 examples/manager/android/app/src/main/res/values-night/styles.xml delete mode 100644 examples/manager/android/app/src/main/res/values/styles.xml delete mode 100644 examples/manager/android/app/src/profile/AndroidManifest.xml delete mode 100644 examples/manager/android/build.gradle delete mode 100644 examples/manager/android/gradle.properties delete mode 100644 examples/manager/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 examples/manager/android/settings.gradle delete mode 100644 examples/manager/ios/.gitignore delete mode 100644 examples/manager/ios/Flutter/AppFrameworkInfo.plist delete mode 100644 examples/manager/ios/Flutter/Debug.xcconfig delete mode 100644 examples/manager/ios/Flutter/Release.xcconfig delete mode 100644 examples/manager/ios/Runner.xcodeproj/project.pbxproj delete mode 100644 examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 examples/manager/ios/Runner/AppDelegate.swift delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md delete mode 100644 examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 examples/manager/ios/Runner/Base.lproj/Main.storyboard delete mode 100644 examples/manager/ios/Runner/Info.plist delete mode 100644 examples/manager/ios/Runner/Runner-Bridging-Header.h delete mode 100644 examples/manager/ios/RunnerTests/RunnerTests.swift delete mode 100644 examples/manager/lib/database.dart delete mode 100644 examples/manager/lib/database.g.dart delete mode 100644 examples/manager/lib/main.dart delete mode 100644 examples/manager/lib/pages/listings.dart delete mode 100644 examples/manager/lib/pages/owners.dart delete mode 100644 examples/manager/lib/pages/product.dart delete mode 100644 examples/manager/lib/pages/store.dart delete mode 100644 examples/manager/lib/tables.dart delete mode 100644 examples/manager/linux/.gitignore delete mode 100644 examples/manager/linux/CMakeLists.txt delete mode 100644 examples/manager/linux/flutter/CMakeLists.txt delete mode 100644 examples/manager/linux/flutter/generated_plugin_registrant.cc delete mode 100644 examples/manager/linux/flutter/generated_plugin_registrant.h delete mode 100644 examples/manager/linux/flutter/generated_plugins.cmake delete mode 100644 examples/manager/linux/main.cc delete mode 100644 examples/manager/linux/my_application.cc delete mode 100644 examples/manager/linux/my_application.h delete mode 100644 examples/manager/macos/.gitignore delete mode 100644 examples/manager/macos/Flutter/Flutter-Debug.xcconfig delete mode 100644 examples/manager/macos/Flutter/Flutter-Release.xcconfig delete mode 100644 examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 examples/manager/macos/Runner.xcodeproj/project.pbxproj delete mode 100644 examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/manager/macos/Runner/AppDelegate.swift delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png delete mode 100644 examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png delete mode 100644 examples/manager/macos/Runner/Base.lproj/MainMenu.xib delete mode 100644 examples/manager/macos/Runner/Configs/AppInfo.xcconfig delete mode 100644 examples/manager/macos/Runner/Configs/Debug.xcconfig delete mode 100644 examples/manager/macos/Runner/Configs/Release.xcconfig delete mode 100644 examples/manager/macos/Runner/Configs/Warnings.xcconfig delete mode 100644 examples/manager/macos/Runner/DebugProfile.entitlements delete mode 100644 examples/manager/macos/Runner/Info.plist delete mode 100644 examples/manager/macos/Runner/MainFlutterWindow.swift delete mode 100644 examples/manager/macos/Runner/Release.entitlements delete mode 100644 examples/manager/macos/RunnerTests/RunnerTests.swift delete mode 100644 examples/manager/pubspec.yaml delete mode 100644 examples/manager/test/widget_test.dart delete mode 100644 examples/manager/web/favicon.png delete mode 100644 examples/manager/web/icons/Icon-192.png delete mode 100644 examples/manager/web/icons/Icon-512.png delete mode 100644 examples/manager/web/icons/Icon-maskable-192.png delete mode 100644 examples/manager/web/icons/Icon-maskable-512.png delete mode 100644 examples/manager/web/index.html delete mode 100644 examples/manager/web/manifest.json delete mode 100644 examples/manager/windows/.gitignore delete mode 100644 examples/manager/windows/CMakeLists.txt delete mode 100644 examples/manager/windows/flutter/CMakeLists.txt delete mode 100644 examples/manager/windows/flutter/generated_plugin_registrant.cc delete mode 100644 examples/manager/windows/flutter/generated_plugin_registrant.h delete mode 100644 examples/manager/windows/flutter/generated_plugins.cmake delete mode 100644 examples/manager/windows/runner/CMakeLists.txt delete mode 100644 examples/manager/windows/runner/Runner.rc delete mode 100644 examples/manager/windows/runner/flutter_window.cpp delete mode 100644 examples/manager/windows/runner/flutter_window.h delete mode 100644 examples/manager/windows/runner/main.cpp delete mode 100644 examples/manager/windows/runner/resource.h delete mode 100644 examples/manager/windows/runner/resources/app_icon.ico delete mode 100644 examples/manager/windows/runner/runner.exe.manifest delete mode 100644 examples/manager/windows/runner/utils.cpp delete mode 100644 examples/manager/windows/runner/utils.h delete mode 100644 examples/manager/windows/runner/win32_window.cpp delete mode 100644 examples/manager/windows/runner/win32_window.h diff --git a/examples/manager/.gitignore b/examples/manager/.gitignore deleted file mode 100644 index 29a3a501..00000000 --- a/examples/manager/.gitignore +++ /dev/null @@ -1,43 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.pub-cache/ -.pub/ -/build/ - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/examples/manager/.metadata b/examples/manager/.metadata deleted file mode 100644 index aa90aa80..00000000 --- a/examples/manager/.metadata +++ /dev/null @@ -1,45 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "ba393198430278b6595976de84fe170f553cc728" - channel: "stable" - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: ba393198430278b6595976de84fe170f553cc728 - base_revision: ba393198430278b6595976de84fe170f553cc728 - - platform: android - create_revision: ba393198430278b6595976de84fe170f553cc728 - base_revision: ba393198430278b6595976de84fe170f553cc728 - - platform: ios - create_revision: ba393198430278b6595976de84fe170f553cc728 - base_revision: ba393198430278b6595976de84fe170f553cc728 - - platform: linux - create_revision: ba393198430278b6595976de84fe170f553cc728 - base_revision: ba393198430278b6595976de84fe170f553cc728 - - platform: macos - create_revision: ba393198430278b6595976de84fe170f553cc728 - base_revision: ba393198430278b6595976de84fe170f553cc728 - - platform: web - create_revision: ba393198430278b6595976de84fe170f553cc728 - base_revision: ba393198430278b6595976de84fe170f553cc728 - - platform: windows - create_revision: ba393198430278b6595976de84fe170f553cc728 - base_revision: ba393198430278b6595976de84fe170f553cc728 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/examples/manager/README.md b/examples/manager/README.md deleted file mode 100644 index 9c4cd3f9..00000000 --- a/examples/manager/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# manager - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/examples/manager/analysis_options.yaml b/examples/manager/analysis_options.yaml deleted file mode 100644 index 0d290213..00000000 --- a/examples/manager/analysis_options.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/examples/manager/android/.gitignore b/examples/manager/android/.gitignore deleted file mode 100644 index 6f568019..00000000 --- a/examples/manager/android/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks diff --git a/examples/manager/android/app/build.gradle b/examples/manager/android/app/build.gradle deleted file mode 100644 index 0dd94dc2..00000000 --- a/examples/manager/android/app/build.gradle +++ /dev/null @@ -1,67 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -android { - namespace "com.example.manager" - compileSdk flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.manager" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies {} diff --git a/examples/manager/android/app/src/debug/AndroidManifest.xml b/examples/manager/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 399f6981..00000000 --- a/examples/manager/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/examples/manager/android/app/src/main/AndroidManifest.xml b/examples/manager/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 18ab6af0..00000000 --- a/examples/manager/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt b/examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt deleted file mode 100644 index c062d662..00000000 --- a/examples/manager/android/app/src/main/kotlin/com/example/manager/MainActivity.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.manager - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() diff --git a/examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml b/examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3..00000000 --- a/examples/manager/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/examples/manager/android/app/src/main/res/drawable/launch_background.xml b/examples/manager/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f8..00000000 --- a/examples/manager/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/examples/manager/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/examples/manager/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/examples/manager/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/examples/manager/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/manager/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/examples/manager/android/app/src/main/res/values-night/styles.xml b/examples/manager/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 06952be7..00000000 --- a/examples/manager/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/examples/manager/android/app/src/main/res/values/styles.xml b/examples/manager/android/app/src/main/res/values/styles.xml deleted file mode 100644 index cb1ef880..00000000 --- a/examples/manager/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/examples/manager/android/app/src/profile/AndroidManifest.xml b/examples/manager/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 399f6981..00000000 --- a/examples/manager/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/examples/manager/android/build.gradle b/examples/manager/android/build.gradle deleted file mode 100644 index bc157bd1..00000000 --- a/examples/manager/android/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/examples/manager/android/gradle.properties b/examples/manager/android/gradle.properties deleted file mode 100644 index 598d13fe..00000000 --- a/examples/manager/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx4G -android.useAndroidX=true -android.enableJetifier=true diff --git a/examples/manager/android/gradle/wrapper/gradle-wrapper.properties b/examples/manager/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index e1ca574e..00000000 --- a/examples/manager/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/examples/manager/android/settings.gradle b/examples/manager/android/settings.gradle deleted file mode 100644 index 1d6d19b7..00000000 --- a/examples/manager/android/settings.gradle +++ /dev/null @@ -1,26 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - } - settings.ext.flutterSdkPath = flutterSdkPath() - - includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false -} - -include ":app" diff --git a/examples/manager/ios/.gitignore b/examples/manager/ios/.gitignore deleted file mode 100644 index 7a7f9873..00000000 --- a/examples/manager/ios/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/examples/manager/ios/Flutter/AppFrameworkInfo.plist b/examples/manager/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 7c569640..00000000 --- a/examples/manager/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 12.0 - - diff --git a/examples/manager/ios/Flutter/Debug.xcconfig b/examples/manager/ios/Flutter/Debug.xcconfig deleted file mode 100644 index 592ceee8..00000000 --- a/examples/manager/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/examples/manager/ios/Flutter/Release.xcconfig b/examples/manager/ios/Flutter/Release.xcconfig deleted file mode 100644 index 592ceee8..00000000 --- a/examples/manager/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/examples/manager/ios/Runner.xcodeproj/project.pbxproj b/examples/manager/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 8e53c3cd..00000000 --- a/examples/manager/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,616 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C8082294A63A400263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C807B294A618700263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 331C8082294A63A400263BE5 /* RunnerTests */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - 331C8081294A63A400263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C8080294A63A400263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C807D294A63A400263BE5 /* Sources */, - 331C807F294A63A400263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C8086294A63A400263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C8080294A63A400263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - 331C8080294A63A400263BE5 /* RunnerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C807F294A63A400263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C807D294A63A400263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 331C8088294A63A400263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Debug; - }; - 331C8089294A63A400263BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Release; - }; - 331C808A294A63A400263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C8088294A63A400263BE5 /* Debug */, - 331C8089294A63A400263BE5 /* Release */, - 331C808A294A63A400263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5..00000000 --- a/examples/manager/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 8e3ca5df..00000000 --- a/examples/manager/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata b/examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16..00000000 --- a/examples/manager/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/examples/manager/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5..00000000 --- a/examples/manager/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/examples/manager/ios/Runner/AppDelegate.swift b/examples/manager/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4a..00000000 --- a/examples/manager/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab..00000000 --- a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 797d452e458972bab9d994556c8305db4c827017..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 6ed2d933e1120817fe9182483a228007b18ab6ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cd7b0099ca80c806f8fe495613e8d6c69460d76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index fe730945a01f64a61e2235dbe3f45b08f7729182..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 502f463a9bc882b461c96aadf492d1729e49e725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 0ec303439225b78712f49115768196d8d76f6790..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index e9f5fea27c705180eb716271f41b582e76dcbd90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1674 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 diff --git a/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/examples/manager/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 0467bf12aa4d28f374bb26596605a46dcbb3e7c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2f..00000000 --- a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b..00000000 --- a/examples/manager/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard b/examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7..00000000 --- a/examples/manager/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/manager/ios/Runner/Base.lproj/Main.storyboard b/examples/manager/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516..00000000 --- a/examples/manager/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/manager/ios/Runner/Info.plist b/examples/manager/ios/Runner/Info.plist deleted file mode 100644 index d2d86816..00000000 --- a/examples/manager/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Manager - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - manager - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/examples/manager/ios/Runner/Runner-Bridging-Header.h b/examples/manager/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a56..00000000 --- a/examples/manager/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/examples/manager/ios/RunnerTests/RunnerTests.swift b/examples/manager/ios/RunnerTests/RunnerTests.swift deleted file mode 100644 index 86a7c3b1..00000000 --- a/examples/manager/ios/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Flutter -import UIKit -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/examples/manager/lib/database.dart b/examples/manager/lib/database.dart deleted file mode 100644 index 7ad1aabb..00000000 --- a/examples/manager/lib/database.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'dart:io'; - -import 'package:drift/drift.dart'; -import 'package:drift/native.dart'; -import 'package:flutter/material.dart' show Color; -import 'package:manager/tables.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:path/path.dart' as p; -import 'package:sqlite3/sqlite3.dart'; -import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart'; -part 'database.g.dart'; - -@DriftDatabase(tables: [Products, Listings, Store, Owner]) -class AppDatabase extends _$AppDatabase { - AppDatabase() : super(connect()); - - @override - int get schemaVersion => 1; -} - -LazyDatabase connect() { - return LazyDatabase(() async { - // put the database file, called db.sqlite here, into the documents folder - // for your app. - final dbFolder = await getApplicationDocumentsDirectory(); - final file = File(p.join(dbFolder.path, 'db1.sqlite')); - - // Also work around limitations on old Android versions - if (Platform.isAndroid) { - await applyWorkaroundToOpenSqlite3OnOldAndroidVersions(); - } - - // Make sqlite3 pick a more suitable location for temporary files - the - // one from the system may be inaccessible due to sandboxing. - final cachebase = (await getTemporaryDirectory()).path; - // We can't access /tmp on Android, which sqlite3 would try by default. - // Explicitly tell it about the correct temporary directory. - sqlite3.tempDirectory = cachebase; - - return NativeDatabase.createInBackground(file); - }); -} diff --git a/examples/manager/lib/database.g.dart b/examples/manager/lib/database.g.dart deleted file mode 100644 index 40b0c010..00000000 --- a/examples/manager/lib/database.g.dart +++ /dev/null @@ -1,1405 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'database.dart'; - -// ignore_for_file: type=lint -class $ProductsTable extends Products with TableInfo<$ProductsTable, Product> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $ProductsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - hasAutoIncrement: true, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); - static const VerificationMeta _nameMeta = const VerificationMeta('name'); - @override - late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - additionalChecks: - GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), - type: DriftSqlType.string, - requiredDuringInsert: true); - static const VerificationMeta _descriptionMeta = - const VerificationMeta('description'); - @override - late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, false, - additionalChecks: - GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), - type: DriftSqlType.string, - requiredDuringInsert: true); - static const VerificationMeta _releaseDateMeta = - const VerificationMeta('releaseDate'); - @override - late final GeneratedColumn releaseDate = GeneratedColumn( - 'release_date', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - static const VerificationMeta _colorMeta = const VerificationMeta('color'); - @override - late final GeneratedColumnWithTypeConverter color = - GeneratedColumn('color', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true) - .withConverter($ProductsTable.$convertercolor); - @override - List get $columns => - [id, name, description, releaseDate, color]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'products'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = 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('description')) { - context.handle( - _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); - } else if (isInserting) { - context.missing(_descriptionMeta); - } - if (data.containsKey('release_date')) { - context.handle( - _releaseDateMeta, - releaseDate.isAcceptableOrUnknown( - data['release_date']!, _releaseDateMeta)); - } else if (isInserting) { - context.missing(_releaseDateMeta); - } - context.handle(_colorMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {id}; - @override - Product map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return Product( - id: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description'])!, - releaseDate: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}release_date'])!, - color: $ProductsTable.$convertercolor.fromSql(attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}color'])!), - ); - } - - @override - $ProductsTable createAlias(String alias) { - return $ProductsTable(attachedDatabase, alias); - } - - static TypeConverter $convertercolor = const ColorConverter(); -} - -class Product extends DataClass implements Insertable { - final int id; - final String name; - final String description; - final DateTime releaseDate; - final Color color; - const Product( - {required this.id, - required this.name, - required this.description, - required this.releaseDate, - required this.color}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['name'] = Variable(name); - map['description'] = Variable(description); - map['release_date'] = Variable(releaseDate); - { - map['color'] = Variable($ProductsTable.$convertercolor.toSql(color)); - } - return map; - } - - ProductsCompanion toCompanion(bool nullToAbsent) { - return ProductsCompanion( - id: Value(id), - name: Value(name), - description: Value(description), - releaseDate: Value(releaseDate), - color: Value(color), - ); - } - - factory Product.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return Product( - id: serializer.fromJson(json['id']), - name: serializer.fromJson(json['name']), - description: serializer.fromJson(json['description']), - releaseDate: serializer.fromJson(json['releaseDate']), - color: serializer.fromJson(json['color']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'name': serializer.toJson(name), - 'description': serializer.toJson(description), - 'releaseDate': serializer.toJson(releaseDate), - 'color': serializer.toJson(color), - }; - } - - Product copyWith( - {int? id, - String? name, - String? description, - DateTime? releaseDate, - Color? color}) => - Product( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - releaseDate: releaseDate ?? this.releaseDate, - color: color ?? this.color, - ); - @override - String toString() { - return (StringBuffer('Product(') - ..write('id: $id, ') - ..write('name: $name, ') - ..write('description: $description, ') - ..write('releaseDate: $releaseDate, ') - ..write('color: $color') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, name, description, releaseDate, color); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is Product && - other.id == this.id && - other.name == this.name && - other.description == this.description && - other.releaseDate == this.releaseDate && - other.color == this.color); -} - -class ProductsCompanion extends UpdateCompanion { - final Value id; - final Value name; - final Value description; - final Value releaseDate; - final Value color; - const ProductsCompanion({ - this.id = const Value.absent(), - this.name = const Value.absent(), - this.description = const Value.absent(), - this.releaseDate = const Value.absent(), - this.color = const Value.absent(), - }); - ProductsCompanion.insert({ - this.id = const Value.absent(), - required String name, - required String description, - required DateTime releaseDate, - required Color color, - }) : name = Value(name), - description = Value(description), - releaseDate = Value(releaseDate), - color = Value(color); - static Insertable custom({ - Expression? id, - Expression? name, - Expression? description, - Expression? releaseDate, - Expression? color, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (name != null) 'name': name, - if (description != null) 'description': description, - if (releaseDate != null) 'release_date': releaseDate, - if (color != null) 'color': color, - }); - } - - ProductsCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? releaseDate, - Value? color}) { - return ProductsCompanion( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - releaseDate: releaseDate ?? this.releaseDate, - color: color ?? this.color, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (name.present) { - map['name'] = Variable(name.value); - } - if (description.present) { - map['description'] = Variable(description.value); - } - if (releaseDate.present) { - map['release_date'] = Variable(releaseDate.value); - } - if (color.present) { - map['color'] = - Variable($ProductsTable.$convertercolor.toSql(color.value)); - } - return map; - } - - @override - String toString() { - return (StringBuffer('ProductsCompanion(') - ..write('id: $id, ') - ..write('name: $name, ') - ..write('description: $description, ') - ..write('releaseDate: $releaseDate, ') - ..write('color: $color') - ..write(')')) - .toString(); - } -} - -class $OwnerTable extends Owner with TableInfo<$OwnerTable, OwnerData> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $OwnerTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - hasAutoIncrement: true, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); - static const VerificationMeta _nameMeta = const VerificationMeta('name'); - @override - late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - additionalChecks: - GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), - type: DriftSqlType.string, - requiredDuringInsert: true); - @override - List get $columns => [id, name]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'owner'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = 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); - } - return context; - } - - @override - Set get $primaryKey => {id}; - @override - OwnerData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return OwnerData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - ); - } - - @override - $OwnerTable createAlias(String alias) { - return $OwnerTable(attachedDatabase, alias); - } -} - -class OwnerData extends DataClass implements Insertable { - final int id; - final String name; - const OwnerData({required this.id, required this.name}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['name'] = Variable(name); - return map; - } - - OwnerCompanion toCompanion(bool nullToAbsent) { - return OwnerCompanion( - id: Value(id), - name: Value(name), - ); - } - - factory OwnerData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return OwnerData( - id: serializer.fromJson(json['id']), - name: serializer.fromJson(json['name']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'name': serializer.toJson(name), - }; - } - - OwnerData copyWith({int? id, String? name}) => OwnerData( - id: id ?? this.id, - name: name ?? this.name, - ); - @override - String toString() { - return (StringBuffer('OwnerData(') - ..write('id: $id, ') - ..write('name: $name') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, name); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is OwnerData && other.id == this.id && other.name == this.name); -} - -class OwnerCompanion extends UpdateCompanion { - final Value id; - final Value name; - const OwnerCompanion({ - this.id = const Value.absent(), - this.name = const Value.absent(), - }); - OwnerCompanion.insert({ - this.id = const Value.absent(), - required String name, - }) : name = Value(name); - static Insertable custom({ - Expression? id, - Expression? name, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (name != null) 'name': name, - }); - } - - OwnerCompanion copyWith({Value? id, Value? name}) { - return OwnerCompanion( - id: id ?? this.id, - name: name ?? this.name, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (name.present) { - map['name'] = Variable(name.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('OwnerCompanion(') - ..write('id: $id, ') - ..write('name: $name') - ..write(')')) - .toString(); - } -} - -class $StoreTable extends Store with TableInfo<$StoreTable, StoreData> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $StoreTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - hasAutoIncrement: true, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); - static const VerificationMeta _nameMeta = const VerificationMeta('name'); - @override - late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - additionalChecks: - GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50), - type: DriftSqlType.string, - requiredDuringInsert: true); - static const VerificationMeta _ownerMeta = const VerificationMeta('owner'); - @override - late final GeneratedColumn owner = GeneratedColumn( - 'owner', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: true, - defaultConstraints: - GeneratedColumn.constraintIsAlways('REFERENCES owner (id)')); - @override - List get $columns => [id, name, owner]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'store'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = 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('owner')) { - context.handle( - _ownerMeta, owner.isAcceptableOrUnknown(data['owner']!, _ownerMeta)); - } else if (isInserting) { - context.missing(_ownerMeta); - } - return context; - } - - @override - Set get $primaryKey => {id}; - @override - StoreData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StoreData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - owner: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}owner'])!, - ); - } - - @override - $StoreTable createAlias(String alias) { - return $StoreTable(attachedDatabase, alias); - } -} - -class StoreData extends DataClass implements Insertable { - final int id; - final String name; - final int owner; - const StoreData({required this.id, required this.name, required this.owner}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['name'] = Variable(name); - map['owner'] = Variable(owner); - return map; - } - - StoreCompanion toCompanion(bool nullToAbsent) { - return StoreCompanion( - id: Value(id), - name: Value(name), - owner: Value(owner), - ); - } - - factory StoreData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StoreData( - id: serializer.fromJson(json['id']), - name: serializer.fromJson(json['name']), - owner: serializer.fromJson(json['owner']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'name': serializer.toJson(name), - 'owner': serializer.toJson(owner), - }; - } - - StoreData copyWith({int? id, String? name, int? owner}) => StoreData( - id: id ?? this.id, - name: name ?? this.name, - owner: owner ?? this.owner, - ); - @override - String toString() { - return (StringBuffer('StoreData(') - ..write('id: $id, ') - ..write('name: $name, ') - ..write('owner: $owner') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, name, owner); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StoreData && - other.id == this.id && - other.name == this.name && - other.owner == this.owner); -} - -class StoreCompanion extends UpdateCompanion { - final Value id; - final Value name; - final Value owner; - const StoreCompanion({ - this.id = const Value.absent(), - this.name = const Value.absent(), - this.owner = const Value.absent(), - }); - StoreCompanion.insert({ - this.id = const Value.absent(), - required String name, - required int owner, - }) : name = Value(name), - owner = Value(owner); - static Insertable custom({ - Expression? id, - Expression? name, - Expression? owner, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (name != null) 'name': name, - if (owner != null) 'owner': owner, - }); - } - - StoreCompanion copyWith( - {Value? id, Value? name, Value? owner}) { - return StoreCompanion( - id: id ?? this.id, - name: name ?? this.name, - owner: owner ?? this.owner, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (name.present) { - map['name'] = Variable(name.value); - } - if (owner.present) { - map['owner'] = Variable(owner.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StoreCompanion(') - ..write('id: $id, ') - ..write('name: $name, ') - ..write('owner: $owner') - ..write(')')) - .toString(); - } -} - -class $ListingsTable extends Listings with TableInfo<$ListingsTable, Listing> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $ListingsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - hasAutoIncrement: true, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); - static const VerificationMeta _productMeta = - const VerificationMeta('product'); - @override - late final GeneratedColumn product = GeneratedColumn( - 'product', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: true, - defaultConstraints: - GeneratedColumn.constraintIsAlways('REFERENCES products (id)')); - static const VerificationMeta _storeMeta = const VerificationMeta('store'); - @override - late final GeneratedColumn store = GeneratedColumn( - 'store', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: true, - defaultConstraints: - GeneratedColumn.constraintIsAlways('REFERENCES store (id)')); - static const VerificationMeta _priceMeta = const VerificationMeta('price'); - @override - late final GeneratedColumn price = GeneratedColumn( - 'price', aliasedName, false, - type: DriftSqlType.double, requiredDuringInsert: true); - @override - List get $columns => [id, product, store, price]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'listings'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } - if (data.containsKey('product')) { - context.handle(_productMeta, - product.isAcceptableOrUnknown(data['product']!, _productMeta)); - } else if (isInserting) { - context.missing(_productMeta); - } - if (data.containsKey('store')) { - context.handle( - _storeMeta, store.isAcceptableOrUnknown(data['store']!, _storeMeta)); - } else if (isInserting) { - context.missing(_storeMeta); - } - if (data.containsKey('price')) { - context.handle( - _priceMeta, price.isAcceptableOrUnknown(data['price']!, _priceMeta)); - } else if (isInserting) { - context.missing(_priceMeta); - } - return context; - } - - @override - Set get $primaryKey => {id}; - @override - Listing map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return Listing( - id: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}id'])!, - product: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}product'])!, - store: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}store'])!, - price: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}price'])!, - ); - } - - @override - $ListingsTable createAlias(String alias) { - return $ListingsTable(attachedDatabase, alias); - } -} - -class Listing extends DataClass implements Insertable { - final int id; - final int product; - final int store; - final double price; - const Listing( - {required this.id, - required this.product, - required this.store, - required this.price}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['product'] = Variable(product); - map['store'] = Variable(store); - map['price'] = Variable(price); - return map; - } - - ListingsCompanion toCompanion(bool nullToAbsent) { - return ListingsCompanion( - id: Value(id), - product: Value(product), - store: Value(store), - price: Value(price), - ); - } - - factory Listing.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return Listing( - id: serializer.fromJson(json['id']), - product: serializer.fromJson(json['product']), - store: serializer.fromJson(json['store']), - price: serializer.fromJson(json['price']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'product': serializer.toJson(product), - 'store': serializer.toJson(store), - 'price': serializer.toJson(price), - }; - } - - Listing copyWith({int? id, int? product, int? store, double? price}) => - Listing( - id: id ?? this.id, - product: product ?? this.product, - store: store ?? this.store, - price: price ?? this.price, - ); - @override - String toString() { - return (StringBuffer('Listing(') - ..write('id: $id, ') - ..write('product: $product, ') - ..write('store: $store, ') - ..write('price: $price') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, product, store, price); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is Listing && - other.id == this.id && - other.product == this.product && - other.store == this.store && - other.price == this.price); -} - -class ListingsCompanion extends UpdateCompanion { - final Value id; - final Value product; - final Value store; - final Value price; - const ListingsCompanion({ - this.id = const Value.absent(), - this.product = const Value.absent(), - this.store = const Value.absent(), - this.price = const Value.absent(), - }); - ListingsCompanion.insert({ - this.id = const Value.absent(), - required int product, - required int store, - required double price, - }) : product = Value(product), - store = Value(store), - price = Value(price); - static Insertable custom({ - Expression? id, - Expression? product, - Expression? store, - Expression? price, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (product != null) 'product': product, - if (store != null) 'store': store, - if (price != null) 'price': price, - }); - } - - ListingsCompanion copyWith( - {Value? id, - Value? product, - Value? store, - Value? price}) { - return ListingsCompanion( - id: id ?? this.id, - product: product ?? this.product, - store: store ?? this.store, - price: price ?? this.price, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (product.present) { - map['product'] = Variable(product.value); - } - if (store.present) { - map['store'] = Variable(store.value); - } - if (price.present) { - map['price'] = Variable(price.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('ListingsCompanion(') - ..write('id: $id, ') - ..write('product: $product, ') - ..write('store: $store, ') - ..write('price: $price') - ..write(')')) - .toString(); - } -} - -abstract class _$AppDatabase extends GeneratedDatabase { - _$AppDatabase(QueryExecutor e) : super(e); - _$AppDatabaseManager get managers => _$AppDatabaseManager(this); - late final $ProductsTable products = $ProductsTable(this); - late final $OwnerTable owner = $OwnerTable(this); - late final $StoreTable store = $StoreTable(this); - late final $ListingsTable listings = $ListingsTable(this); - @override - Iterable> get allTables => - allSchemaEntities.whereType>(); - @override - List get allSchemaEntities => - [products, owner, store, listings]; -} - -class $$ProductsTableFilterComposer - extends FilterComposer<_$AppDatabase, $ProductsTable> { - $$ProductsTableFilterComposer(super.db, super.table); - ColumnFilters get id => ColumnFilters($table.id); - ColumnFilters get name => ColumnFilters($table.name); - ColumnFilters get description => ColumnFilters($table.description); - ColumnFilters get releaseDate => ColumnFilters($table.releaseDate); - ColumnFilters get colorValue => ColumnFilters($table.color); - ColumnWithTypeConverterFilters get color => - ColumnWithTypeConverterFilters($table.color); - ComposableFilter listings( - ComposableFilter Function($$ListingsTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.listings, - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.product, - getReferencedComposer: (db, table) => - $$ListingsTableFilterComposer(db, table), - builder: f); - } -} - -class $$ProductsTableOrderingComposer - extends OrderingComposer<_$AppDatabase, $ProductsTable> { - $$ProductsTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get name => ColumnOrderings($table.name); - ColumnOrderings get description => ColumnOrderings($table.description); - ColumnOrderings get releaseDate => ColumnOrderings($table.releaseDate); - ColumnOrderings get color => ColumnOrderings($table.color); -} - -class $$ProductsTableProcessedTableManager extends ProcessedTableManager< - _$AppDatabase, - $ProductsTable, - Product, - $$ProductsTableFilterComposer, - $$ProductsTableOrderingComposer, - $$ProductsTableProcessedTableManager, - $$ProductsTableInsertCompanionBuilder, - $$ProductsTableUpdateCompanionBuilder> { - const $$ProductsTableProcessedTableManager(super.$state); -} - -typedef $$ProductsTableInsertCompanionBuilder = ProductsCompanion Function({ - Value id, - required String name, - required String description, - required DateTime releaseDate, - required Color color, -}); -typedef $$ProductsTableUpdateCompanionBuilder = ProductsCompanion Function({ - Value id, - Value name, - Value description, - Value releaseDate, - Value color, -}); - -class $$ProductsTableTableManager extends RootTableManager< - _$AppDatabase, - $ProductsTable, - Product, - $$ProductsTableFilterComposer, - $$ProductsTableOrderingComposer, - $$ProductsTableProcessedTableManager, - $$ProductsTableInsertCompanionBuilder, - $$ProductsTableUpdateCompanionBuilder> { - $$ProductsTableTableManager(_$AppDatabase db, $ProductsTable table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: $$ProductsTableFilterComposer(db, table), - orderingComposer: $$ProductsTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$ProductsTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - Value id = const Value.absent(), - Value name = const Value.absent(), - Value description = const Value.absent(), - Value releaseDate = const Value.absent(), - Value color = const Value.absent(), - }) => - ProductsCompanion( - id: id, - name: name, - description: description, - releaseDate: releaseDate, - color: color, - ), - getInsertCompanionBuilder: ({ - Value id = const Value.absent(), - required String name, - required String description, - required DateTime releaseDate, - required Color color, - }) => - ProductsCompanion.insert( - id: id, - name: name, - description: description, - releaseDate: releaseDate, - color: color, - ))); -} - -class $$OwnerTableFilterComposer - extends FilterComposer<_$AppDatabase, $OwnerTable> { - $$OwnerTableFilterComposer(super.db, super.table); - ColumnFilters get id => ColumnFilters($table.id); - ColumnFilters get name => ColumnFilters($table.name); - ComposableFilter stores( - ComposableFilter Function($$StoreTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.store, - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.owner, - getReferencedComposer: (db, table) => - $$StoreTableFilterComposer(db, table), - builder: f); - } -} - -class $$OwnerTableOrderingComposer - extends OrderingComposer<_$AppDatabase, $OwnerTable> { - $$OwnerTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get name => ColumnOrderings($table.name); -} - -class $$OwnerTableProcessedTableManager extends ProcessedTableManager< - _$AppDatabase, - $OwnerTable, - OwnerData, - $$OwnerTableFilterComposer, - $$OwnerTableOrderingComposer, - $$OwnerTableProcessedTableManager, - $$OwnerTableInsertCompanionBuilder, - $$OwnerTableUpdateCompanionBuilder> { - const $$OwnerTableProcessedTableManager(super.$state); -} - -typedef $$OwnerTableInsertCompanionBuilder = OwnerCompanion Function({ - Value id, - required String name, -}); -typedef $$OwnerTableUpdateCompanionBuilder = OwnerCompanion Function({ - Value id, - Value name, -}); - -class $$OwnerTableTableManager extends RootTableManager< - _$AppDatabase, - $OwnerTable, - OwnerData, - $$OwnerTableFilterComposer, - $$OwnerTableOrderingComposer, - $$OwnerTableProcessedTableManager, - $$OwnerTableInsertCompanionBuilder, - $$OwnerTableUpdateCompanionBuilder> { - $$OwnerTableTableManager(_$AppDatabase db, $OwnerTable table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: $$OwnerTableFilterComposer(db, table), - orderingComposer: $$OwnerTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$OwnerTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - Value id = const Value.absent(), - Value name = const Value.absent(), - }) => - OwnerCompanion( - id: id, - name: name, - ), - getInsertCompanionBuilder: ({ - Value id = const Value.absent(), - required String name, - }) => - OwnerCompanion.insert( - id: id, - name: name, - ))); -} - -class $$StoreTableFilterComposer - extends FilterComposer<_$AppDatabase, $StoreTable> { - $$StoreTableFilterComposer(super.db, super.table); - ColumnFilters get id => ColumnFilters($table.id); - ColumnFilters get name => ColumnFilters($table.name); - ColumnFilters get ownerId => ColumnFilters($table.owner); - ComposableFilter owner( - ComposableFilter Function($$OwnerTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.owner, - getCurrentColumn: (f) => f.owner, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$OwnerTableFilterComposer(db, table), - builder: f); - } - - ComposableFilter listings( - ComposableFilter Function($$ListingsTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.listings, - getCurrentColumn: (f) => f.id, - getReferencedColumn: (f) => f.store, - getReferencedComposer: (db, table) => - $$ListingsTableFilterComposer(db, table), - builder: f); - } -} - -class $$StoreTableOrderingComposer - extends OrderingComposer<_$AppDatabase, $StoreTable> { - $$StoreTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get name => ColumnOrderings($table.name); - ColumnOrderings get ownerId => ColumnOrderings($table.owner); - ComposableOrdering owner( - ComposableOrdering Function($$OwnerTableOrderingComposer o) o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.owner, - getCurrentColumn: (f) => f.owner, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$OwnerTableOrderingComposer(db, table), - builder: o); - } -} - -class $$StoreTableProcessedTableManager extends ProcessedTableManager< - _$AppDatabase, - $StoreTable, - StoreData, - $$StoreTableFilterComposer, - $$StoreTableOrderingComposer, - $$StoreTableProcessedTableManager, - $$StoreTableInsertCompanionBuilder, - $$StoreTableUpdateCompanionBuilder> { - const $$StoreTableProcessedTableManager(super.$state); -} - -typedef $$StoreTableInsertCompanionBuilder = StoreCompanion Function({ - Value id, - required String name, - required int owner, -}); -typedef $$StoreTableUpdateCompanionBuilder = StoreCompanion Function({ - Value id, - Value name, - Value owner, -}); - -class $$StoreTableTableManager extends RootTableManager< - _$AppDatabase, - $StoreTable, - StoreData, - $$StoreTableFilterComposer, - $$StoreTableOrderingComposer, - $$StoreTableProcessedTableManager, - $$StoreTableInsertCompanionBuilder, - $$StoreTableUpdateCompanionBuilder> { - $$StoreTableTableManager(_$AppDatabase db, $StoreTable table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: $$StoreTableFilterComposer(db, table), - orderingComposer: $$StoreTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$StoreTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - Value id = const Value.absent(), - Value name = const Value.absent(), - Value owner = const Value.absent(), - }) => - StoreCompanion( - id: id, - name: name, - owner: owner, - ), - getInsertCompanionBuilder: ({ - Value id = const Value.absent(), - required String name, - required int owner, - }) => - StoreCompanion.insert( - id: id, - name: name, - owner: owner, - ))); -} - -class $$ListingsTableFilterComposer - extends FilterComposer<_$AppDatabase, $ListingsTable> { - $$ListingsTableFilterComposer(super.db, super.table); - ColumnFilters get id => ColumnFilters($table.id); - ColumnFilters get productId => ColumnFilters($table.product); - ComposableFilter product( - ComposableFilter Function($$ProductsTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.products, - getCurrentColumn: (f) => f.product, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$ProductsTableFilterComposer(db, table), - builder: f); - } - - ColumnFilters get storeId => ColumnFilters($table.store); - ComposableFilter store( - ComposableFilter Function($$StoreTableFilterComposer f) f) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.store, - getCurrentColumn: (f) => f.store, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$StoreTableFilterComposer(db, table), - builder: f); - } - - ColumnFilters get price => ColumnFilters($table.price); -} - -class $$ListingsTableOrderingComposer - extends OrderingComposer<_$AppDatabase, $ListingsTable> { - $$ListingsTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get productId => ColumnOrderings($table.product); - ComposableOrdering product( - ComposableOrdering Function($$ProductsTableOrderingComposer o) o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.products, - getCurrentColumn: (f) => f.product, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$ProductsTableOrderingComposer(db, table), - builder: o); - } - - ColumnOrderings get storeId => ColumnOrderings($table.store); - ComposableOrdering store( - ComposableOrdering Function($$StoreTableOrderingComposer o) o) { - return $composeWithJoins( - $db: $db, - $table: $table, - referencedTable: $db.store, - getCurrentColumn: (f) => f.store, - getReferencedColumn: (f) => f.id, - getReferencedComposer: (db, table) => - $$StoreTableOrderingComposer(db, table), - builder: o); - } - - ColumnOrderings get price => ColumnOrderings($table.price); -} - -class $$ListingsTableProcessedTableManager extends ProcessedTableManager< - _$AppDatabase, - $ListingsTable, - Listing, - $$ListingsTableFilterComposer, - $$ListingsTableOrderingComposer, - $$ListingsTableProcessedTableManager, - $$ListingsTableInsertCompanionBuilder, - $$ListingsTableUpdateCompanionBuilder> { - const $$ListingsTableProcessedTableManager(super.$state); -} - -typedef $$ListingsTableInsertCompanionBuilder = ListingsCompanion Function({ - Value id, - required int product, - required int store, - required double price, -}); -typedef $$ListingsTableUpdateCompanionBuilder = ListingsCompanion Function({ - Value id, - Value product, - Value store, - Value price, -}); - -class $$ListingsTableTableManager extends RootTableManager< - _$AppDatabase, - $ListingsTable, - Listing, - $$ListingsTableFilterComposer, - $$ListingsTableOrderingComposer, - $$ListingsTableProcessedTableManager, - $$ListingsTableInsertCompanionBuilder, - $$ListingsTableUpdateCompanionBuilder> { - $$ListingsTableTableManager(_$AppDatabase db, $ListingsTable table) - : super(TableManagerState( - db: db, - table: table, - filteringComposer: $$ListingsTableFilterComposer(db, table), - orderingComposer: $$ListingsTableOrderingComposer(db, table), - getChildManagerBuilder: (p0) => - $$ListingsTableProcessedTableManager(p0), - getUpdateCompanionBuilder: ({ - Value id = const Value.absent(), - Value product = const Value.absent(), - Value store = const Value.absent(), - Value price = const Value.absent(), - }) => - ListingsCompanion( - id: id, - product: product, - store: store, - price: price, - ), - getInsertCompanionBuilder: ({ - Value id = const Value.absent(), - required int product, - required int store, - required double price, - }) => - ListingsCompanion.insert( - id: id, - product: product, - store: store, - price: price, - ))); -} - -class _$AppDatabaseManager { - final _$AppDatabase _db; - _$AppDatabaseManager(this._db); - $$ProductsTableTableManager get products => - $$ProductsTableTableManager(_db, _db.products); - $$OwnerTableTableManager get owner => - $$OwnerTableTableManager(_db, _db.owner); - $$StoreTableTableManager get store => - $$StoreTableTableManager(_db, _db.store); - $$ListingsTableTableManager get listings => - $$ListingsTableTableManager(_db, _db.listings); -} diff --git a/examples/manager/lib/main.dart b/examples/manager/lib/main.dart deleted file mode 100644 index 45cf5402..00000000 --- a/examples/manager/lib/main.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:manager/database.dart'; -import 'package:manager/pages/listings.dart'; -import 'package:manager/pages/owners.dart'; -import 'package:manager/pages/product.dart'; -import 'package:manager/pages/store.dart'; - -late final AppDatabase db; -void main() { - db = AppDatabase(); - runApp(const ProviderScope(child: MyApp())); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Manager Demo', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const Home(), - ); - } -} - -class Home extends HookConsumerWidget { - const Home({super.key}); - @override - Widget build(BuildContext context, WidgetRef ref) { - final tabs = useTabController(initialLength: 4); - - return Scaffold( - appBar: AppBar( - title: const Text('Manager Demo'), - bottom: TabBar( - controller: tabs, - tabs: const [ - Tab(text: 'Products'), - Tab(text: 'Listings'), - Tab(text: 'Stores'), - Tab(text: 'Owners'), - ], - ), - ), - body: TabBarView( - controller: tabs, - children: const [ - ProductPage(), - ListingPage(), - StorePage(), - OwnersPage(), - ], - ), - ); - } -} diff --git a/examples/manager/lib/pages/listings.dart b/examples/manager/lib/pages/listings.dart deleted file mode 100644 index 9a7c4b78..00000000 --- a/examples/manager/lib/pages/listings.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:manager/database.dart'; -import 'package:manager/main.dart'; - -class ListingPage extends HookConsumerWidget { - const ListingPage({super.key}); - @override - Widget build(BuildContext context, WidgetRef ref) { - final stream = useMemoized(() => - db.managers.listings.all().watch().asyncMap((event) async { - final products = await db.managers.products - .filter( - (f) => f.listings((f) => f.id.isIn(event.map((e) => e.id)))) - .get(); - final stores = await db.managers.store - .filter( - (f) => f.listings((f) => f.id.isIn(event.map((e) => e.id)))) - .get(); - return event.map((e) { - final product = - products.firstWhere((element) => element.id == e.product); - final store = stores.firstWhere((element) => element.id == e.store); - return (product, store, e); - }).toList(); - })); - final data = useStream(stream); - - final Widget body; - var items = <(Product, StoreData, Listing)>[]; - if (data.hasData) { - items = data.data!; - body = ListView.builder( - itemCount: items.length, - itemBuilder: (context, index) { - final (product, store, listing) = items[index]; - return ListTile( - title: Text("${product.name} - ${store.name}"), - subtitle: Text("${listing.price}"), - trailing: IconButton( - onPressed: () { - db.managers.products.delete(product); - }, - icon: const Icon(Icons.delete)), - leading: CircleAvatar( - backgroundColor: product.color, - ), - ); - }, - ); - } else { - body = const Center(child: Text("No data")); - } - - return Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: () { - showDialog( - context: context, - builder: (ctx) { - return HookBuilder(builder: (context) { - final priceTextController = useTextEditingController(); - final product = useState(null); - final store = useState(null); - - return SimpleDialog( - title: const Text("Add Listing"), - children: [ - TextField( - controller: priceTextController, - decoration: const InputDecoration(labelText: "Price"), - ), - ListTile( - title: const Text("Product"), - subtitle: product.value == null - ? null - : Text(product.value!.name), - onTap: () { - showDialog( - context: context, - builder: (ctx) { - return HookBuilder(builder: (context) { - final data = useStream( - db.managers.products.all().watch()); - final items = data.data ?? []; - return SimpleDialog( - title: const Text("Select Product"), - children: [ - ...items.map((e) => ListTile( - title: Text(e.name), - onTap: () { - product.value = e; - Navigator.of(ctx).pop(); - }, - )) - ], - ); - }); - }); - }, - ), - ListTile( - title: const Text("Store"), - subtitle: store.value == null - ? null - : Text(store.value!.name), - onTap: () { - showDialog( - context: context, - builder: (ctx) { - return HookBuilder(builder: (context) { - final data = useStream( - db.managers.store.all().watch()); - final items = data.data ?? []; - return SimpleDialog( - title: const Text("Select Store"), - children: [ - ...items.map((e) => ListTile( - title: Text(e.name), - onTap: () { - store.value = e; - Navigator.of(ctx).pop(); - }, - )) - ], - ); - }); - }); - }, - ), - ElevatedButton( - onPressed: () { - db.managers.listings.create((o) => o( - price: double.parse(priceTextController.text), - product: product.value!.id, - store: store.value!.id)); - Navigator.of(ctx).pop(); - }, - child: const Text("Add")) - ], - ); - }); - }); - }, - child: const Icon(Icons.add), - ), - body: body, - ); - } -} diff --git a/examples/manager/lib/pages/owners.dart b/examples/manager/lib/pages/owners.dart deleted file mode 100644 index bf2646c6..00000000 --- a/examples/manager/lib/pages/owners.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:manager/database.dart'; -import 'package:manager/main.dart'; - -class OwnersPage extends HookConsumerWidget { - const OwnersPage({super.key}); - @override - Widget build(BuildContext context, WidgetRef ref) { - final stream = useMemoized(() => db.managers.owner.all().watch()); - final data = useStream(stream); - - final Widget body; - var items = []; - if (data.hasData) { - items = data.data!; - body = ListView.builder( - itemCount: items.length, - itemBuilder: (context, index) { - final product = items[index]; - return ListTile( - title: Text(product.name), - trailing: IconButton( - onPressed: () { - db.managers.owner.delete(product); - }, - icon: const Icon(Icons.delete)), - ); - }, - ); - } else { - body = const Center(child: Text("No data")); - } - - return Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: () { - showDialog( - context: context, - builder: (ctx) { - return HookBuilder(builder: (context) { - final nameTextController = useTextEditingController(); - - return SimpleDialog( - title: const Text("Add Owner"), - children: [ - TextField( - controller: nameTextController, - decoration: const InputDecoration(labelText: "Name"), - ), - ElevatedButton( - onPressed: () { - db.managers.owner.create((o) => o( - name: nameTextController.text, - )); - Navigator.of(ctx).pop(); - }, - child: const Text("Add")) - ], - ); - }); - }); - }, - child: const Icon(Icons.add), - ), - body: body, - ); - } -} diff --git a/examples/manager/lib/pages/product.dart b/examples/manager/lib/pages/product.dart deleted file mode 100644 index d7cb3a10..00000000 --- a/examples/manager/lib/pages/product.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'package:flex_color_picker/flex_color_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:manager/database.dart'; -import 'package:manager/main.dart'; - -class ProductPage extends HookConsumerWidget { - const ProductPage({super.key}); - @override - Widget build(BuildContext context, WidgetRef ref) { - final stream = useMemoized(() => db.managers.products.all().watch()); - final data = useStream(stream); - - final Widget body; - var items = []; - if (data.hasData) { - items = data.data!; - body = ListView.builder( - itemCount: items.length, - itemBuilder: (context, index) { - final product = items[index]; - return ListTile( - title: Text(product.name), - subtitle: Text("${product.description} ${product.releaseDate}"), - trailing: IconButton( - onPressed: () { - db.managers.products.delete(product); - }, - icon: const Icon(Icons.delete)), - leading: CircleAvatar( - backgroundColor: product.color, - ), - ); - }, - ); - } else { - body = const Center(child: Text("No data")); - } - - return Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: () { - showDialog( - context: context, - builder: (ctx) { - return HookBuilder(builder: (context) { - final nameTextController = useTextEditingController(); - final descriptionTextController = useTextEditingController(); - final color = useState(null); - final releaseDate = useState(null); - - return SimpleDialog( - title: const Text("Add Product"), - children: [ - TextField( - controller: nameTextController, - decoration: const InputDecoration(labelText: "Name"), - ), - TextField( - controller: descriptionTextController, - decoration: - const InputDecoration(labelText: "Description"), - ), - ListTile( - leading: CircleAvatar( - backgroundColor: color.value, - ), - title: const Text("Color"), - onTap: () { - showColorPickerDialog( - context, color.value ?? Colors.blue) - .then((value) => color.value = value); - }, - ), - ListTile( - leading: const Icon(Icons.calendar_month), - title: const Text("Date"), - subtitle: releaseDate.value != null - ? Text(releaseDate.value.toString()) - : null, - onTap: () { - showDatePicker( - context: ctx, - initialDate: releaseDate.value ?? DateTime.now(), - firstDate: DateTime(2000), - lastDate: DateTime(2100), - ).then((value) { - releaseDate.value = value; - }); - }, - ), - ElevatedButton( - onPressed: () { - db.managers.products.create((o) => o( - color: color.value ?? Colors.red, - description: descriptionTextController.text, - name: nameTextController.text, - releaseDate: releaseDate.value!)); - Navigator.of(ctx).pop(); - }, - child: const Text("Add")) - ], - ); - }); - }); - }, - child: const Icon(Icons.add), - ), - body: body, - ); - } -} diff --git a/examples/manager/lib/pages/store.dart b/examples/manager/lib/pages/store.dart deleted file mode 100644 index 749c6682..00000000 --- a/examples/manager/lib/pages/store.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:manager/database.dart'; -import 'package:manager/main.dart'; - -class StorePage extends HookConsumerWidget { - const StorePage({super.key}); - @override - Widget build(BuildContext context, WidgetRef ref) { - final stream = useMemoized(() => - db.managers.store.all().watch().asyncMap((event) async { - final owners = await db.managers.owner - .filter((f) => f.stores((f) => f.id.isIn(event.map((e) => e.id)))) - .get(); - return event.map((e) { - final owner = owners.firstWhere((element) => element.id == e.owner); - return (owner, e); - }).toList(); - })); - final data = useStream(stream); - - final Widget body; - var items = <(OwnerData, StoreData)>[]; - if (data.hasData) { - items = data.data!; - body = ListView.builder( - itemCount: items.length, - itemBuilder: (context, index) { - final (owner, store) = items[index]; - return ListTile( - title: Text(store.name), - subtitle: Text(owner.name), - trailing: IconButton( - onPressed: () { - db.managers.store.delete(store); - }, - icon: const Icon(Icons.delete)), - ); - }, - ); - } else { - body = const Center(child: Text("No data")); - } - - return Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: () { - showDialog( - context: context, - builder: (ctx) { - return HookBuilder(builder: (context) { - final nameTextController = useTextEditingController(); - final owner = useState(null); - - return SimpleDialog( - title: const Text("Add Store"), - children: [ - TextField( - controller: nameTextController, - decoration: const InputDecoration(labelText: "Name"), - ), - ListTile( - title: const Text("Owner"), - subtitle: Text(owner.value?.name ?? "Select Owner"), - onTap: () { - showDialog( - context: context, - builder: (ctx) { - return HookBuilder(builder: (context) { - final stream = useMemoized( - () => db.managers.owner.all().watch()); - final data = useStream(stream); - - return SimpleDialog( - title: const Text("Select Owner"), - children: [ - if (data.hasData) - for (final o in data.data!) - ListTile( - title: Text(o.name), - onTap: () { - owner.value = o; - Navigator.of(ctx).pop(owner); - }, - ) - ], - ); - }); - }, - ); - }), - ElevatedButton( - onPressed: () { - db.managers.store.create((o) => o( - name: nameTextController.text, - owner: owner.value!.id)); - Navigator.of(ctx).pop(); - }, - child: const Text("Add")) - ], - ); - }); - }); - }, - child: const Icon(Icons.add), - ), - body: body, - ); - } -} diff --git a/examples/manager/lib/tables.dart b/examples/manager/lib/tables.dart deleted file mode 100644 index fc104715..00000000 --- a/examples/manager/lib/tables.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:flutter/material.dart' show Color; - -class Products extends Table { - IntColumn get id => integer().autoIncrement()(); - TextColumn get name => text().withLength(min: 1, max: 50)(); - TextColumn get description => text().withLength(min: 1, max: 50)(); - DateTimeColumn get releaseDate => dateTime()(); - IntColumn get color => integer().map(const ColorConverter())(); -} - -class Listings extends Table { - IntColumn get id => integer().autoIncrement()(); - @ReferenceName("listings") - IntColumn get product => integer().references(Products, #id)(); - @ReferenceName("listings") - IntColumn get store => integer().references(Store, #id)(); - RealColumn get price => real()(); -} - -class Store extends Table { - IntColumn get id => integer().autoIncrement()(); - TextColumn get name => text().withLength(min: 1, max: 50)(); - @ReferenceName("stores") - IntColumn get owner => integer().references(Owner, #id)(); -} - -class Owner extends Table { - IntColumn get id => integer().autoIncrement()(); - TextColumn get name => text().withLength(min: 1, max: 50)(); -} - -class ColorConverter extends TypeConverter { - const ColorConverter(); - - @override - Color fromSql(int fromDb) => Color(fromDb); - - @override - int toSql(Color value) => value.value; -} diff --git a/examples/manager/linux/.gitignore b/examples/manager/linux/.gitignore deleted file mode 100644 index d3896c98..00000000 --- a/examples/manager/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/examples/manager/linux/CMakeLists.txt b/examples/manager/linux/CMakeLists.txt deleted file mode 100644 index 87384c78..00000000 --- a/examples/manager/linux/CMakeLists.txt +++ /dev/null @@ -1,145 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "manager") -# The unique GTK application identifier for this application. See: -# https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.manager") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Load bundled libraries from the lib/ directory relative to the binary. -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Root filesystem for cross-building. -if(FLUTTER_TARGET_PLATFORM_SYSROOT) - set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endif() - -# Define build configuration options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) - -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) - install(FILES "${bundled_library}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endforeach(bundled_library) - -# Copy the native assets provided by the build.dart from all packages. -set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") -install(DIRECTORY "${NATIVE_ASSETS_DIR}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/examples/manager/linux/flutter/CMakeLists.txt b/examples/manager/linux/flutter/CMakeLists.txt deleted file mode 100644 index d5bd0164..00000000 --- a/examples/manager/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,88 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/examples/manager/linux/flutter/generated_plugin_registrant.cc b/examples/manager/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 2c1ec4fe..00000000 --- a/examples/manager/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); - sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); -} diff --git a/examples/manager/linux/flutter/generated_plugin_registrant.h b/examples/manager/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47b..00000000 --- a/examples/manager/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/examples/manager/linux/flutter/generated_plugins.cmake b/examples/manager/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 7ea2a801..00000000 --- a/examples/manager/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - sqlite3_flutter_libs -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/examples/manager/linux/main.cc b/examples/manager/linux/main.cc deleted file mode 100644 index e7c5c543..00000000 --- a/examples/manager/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/examples/manager/linux/my_application.cc b/examples/manager/linux/my_application.cc deleted file mode 100644 index 058b1555..00000000 --- a/examples/manager/linux/my_application.cc +++ /dev/null @@ -1,124 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "manager"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "manager"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GApplication::startup. -static void my_application_startup(GApplication* application) { - //MyApplication* self = MY_APPLICATION(object); - - // Perform any actions required at application startup. - - G_APPLICATION_CLASS(my_application_parent_class)->startup(application); -} - -// Implements GApplication::shutdown. -static void my_application_shutdown(GApplication* application) { - //MyApplication* self = MY_APPLICATION(object); - - // Perform any actions required at application shutdown. - - G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_APPLICATION_CLASS(klass)->startup = my_application_startup; - G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); -} diff --git a/examples/manager/linux/my_application.h b/examples/manager/linux/my_application.h deleted file mode 100644 index 72271d5e..00000000 --- a/examples/manager/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/examples/manager/macos/.gitignore b/examples/manager/macos/.gitignore deleted file mode 100644 index 746adbb6..00000000 --- a/examples/manager/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/examples/manager/macos/Flutter/Flutter-Debug.xcconfig b/examples/manager/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index c2efd0b6..00000000 --- a/examples/manager/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/manager/macos/Flutter/Flutter-Release.xcconfig b/examples/manager/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index c2efd0b6..00000000 --- a/examples/manager/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift b/examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 14fa8bc9..00000000 --- a/examples/manager/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import path_provider_foundation -import sqlite3_flutter_libs - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) -} diff --git a/examples/manager/macos/Runner.xcodeproj/project.pbxproj b/examples/manager/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 80acabf8..00000000 --- a/examples/manager/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,705 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC10EC2044A3C60003C045; - remoteInfo = Runner; - }; - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* manager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "manager.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 331C80D2294CF70F00263BE5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C80D6294CF71000263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C80D7294CF71000263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 331C80D6294CF71000263BE5 /* RunnerTests */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* manager.app */, - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C80D4294CF70F00263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C80D1294CF70F00263BE5 /* Sources */, - 331C80D2294CF70F00263BE5 /* Frameworks */, - 331C80D3294CF70F00263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C80DA294CF71000263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* manager.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C80D4294CF70F00263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 33CC10EC2044A3C60003C045; - }; - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 331C80D4294CF70F00263BE5 /* RunnerTests */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C80D3294CF70F00263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C80D1294CF70F00263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC10EC2044A3C60003C045 /* Runner */; - targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; - }; - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 331C80DB294CF71000263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; - }; - name = Debug; - }; - 331C80DC294CF71000263BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; - }; - name = Release; - }; - 331C80DD294CF71000263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; - }; - name = Profile; - }; - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C80DB294CF71000263BE5 /* Debug */, - 331C80DC294CF71000263BE5 /* Release */, - 331C80DD294CF71000263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/examples/manager/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index c72bbcf3..00000000 --- a/examples/manager/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata b/examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16..00000000 --- a/examples/manager/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/examples/manager/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/examples/manager/macos/Runner/AppDelegate.swift b/examples/manager/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef643..00000000 --- a/examples/manager/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f1..00000000 --- a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d9a33e198f5747104729e1fcef999772a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eba55c6dabc3aac36f33d859266c18fa0d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5680 zcmaiYXH?Tqu=Xz`p-L#B_gI#0we$cm_HcmYFP$?wjD#BaCN4mzC5#`>w9y6=ThxrYZc0WPXprg zYjB`UsV}0=eUtY$(P6YW}npdd;%9pi?zS3k-nqCob zSX_AQEf|=wYT3r?f!*Yt)ar^;l3Sro{z(7deUBPd2~(SzZ-s@0r&~Km2S?8r##9-< z)2UOSVaHqq6}%sA9Ww;V2LG=PnNAh6mA2iWOuV7T_lRDR z&N8-eN=U)-T|;wo^Wv=34wtV0g}sAAe}`Ph@~!|<;z7*K8(qkX0}o=!(+N*UWrkEja*$_H6mhK1u{P!AC39} z|3+Z(mAOq#XRYS)TLoHv<)d%$$I@+x+2)V{@o~~J-!YUI-Q9%!Ldi4Op&Lw&B>jj* zwAgC#Y>gbIqv!d|J5f!$dbCXoq(l3GR(S>(rtZ~Z*agXMMKN!@mWT_vmCbSd3dUUm z4M&+gz?@^#RRGal%G3dDvj7C5QTb@9+!MG+>0dcjtZEB45c+qx*c?)d<%htn1o!#1 zpIGonh>P1LHu3s)fGFF-qS}AXjW|M*2Xjkh7(~r(lN=o#mBD9?jt74=Rz85I4Nfx_ z7Z)q?!};>IUjMNM6ee2Thq7))a>My?iWFxQ&}WvsFP5LP+iGz+QiYek+K1`bZiTV- zHHYng?ct@Uw5!gquJ(tEv1wTrRR7cemI>aSzLI^$PxW`wL_zt@RSfZ1M3c2sbebM* ze0=;sy^!90gL~YKISz*x;*^~hcCoO&CRD)zjT(A2b_uRue=QXFe5|!cf0z1m!iwv5GUnLw9Dr*Ux z)3Lc!J@Ei;&&yxGpf2kn@2wJ2?t6~obUg;?tBiD#uo$SkFIasu+^~h33W~`r82rSa ztyE;ehFjC2hjpJ-e__EH&z?!~>UBb=&%DS>NT)1O3Isn-!SElBV2!~m6v0$vx^a<@ISutdTk1@?;i z<8w#b-%|a#?e5(n@7>M|v<<0Kpg?BiHYMRe!3Z{wYc2hN{2`6(;q`9BtXIhVq6t~KMH~J0~XtUuT06hL8c1BYZWhN zk4F2I;|za*R{ToHH2L?MfRAm5(i1Ijw;f+0&J}pZ=A0;A4M`|10ZskA!a4VibFKn^ zdVH4OlsFV{R}vFlD~aA4xxSCTTMW@Gws4bFWI@xume%smAnuJ0b91QIF?ZV!%VSRJ zO7FmG!swKO{xuH{DYZ^##gGrXsUwYfD0dxXX3>QmD&`mSi;k)YvEQX?UyfIjQeIm! z0ME3gmQ`qRZ;{qYOWt}$-mW*>D~SPZKOgP)T-Sg%d;cw^#$>3A9I(%#vsTRQe%moT zU`geRJ16l>FV^HKX1GG7fR9AT((jaVb~E|0(c-WYQscVl(z?W!rJp`etF$dBXP|EG z=WXbcZ8mI)WBN>3<@%4eD597FD5nlZajwh8(c$lum>yP)F}=(D5g1-WVZRc)(!E3} z-6jy(x$OZOwE=~{EQS(Tp`yV2&t;KBpG*XWX!yG+>tc4aoxbXi7u@O*8WWFOxUjcq z^uV_|*818$+@_{|d~VOP{NcNi+FpJ9)aA2So<7sB%j`$Prje&auIiTBb{oD7q~3g0 z>QNIwcz(V-y{Ona?L&=JaV5`o71nIsWUMA~HOdCs10H+Irew#Kr(2cn>orG2J!jvP zqcVX0OiF}c<)+5&p}a>_Uuv)L_j}nqnJ5a?RPBNi8k$R~zpZ33AA4=xJ@Z($s3pG9 zkURJY5ZI=cZGRt_;`hs$kE@B0FrRx(6K{`i1^*TY;Vn?|IAv9|NrN*KnJqO|8$e1& zb?OgMV&q5|w7PNlHLHF) zB+AK#?EtCgCvwvZ6*u|TDhJcCO+%I^@Td8CR}+nz;OZ*4Dn?mSi97m*CXXc=};!P`B?}X`F-B5v-%ACa8fo0W++j&ztmqK z;&A)cT4ob9&MxpQU41agyMU8jFq~RzXOAsy>}hBQdFVL%aTn~M>5t9go2j$i9=(rZ zADmVj;Qntcr3NIPPTggpUxL_z#5~C!Gk2Rk^3jSiDqsbpOXf^f&|h^jT4|l2ehPat zb$<*B+x^qO8Po2+DAmrQ$Zqc`1%?gp*mDk>ERf6I|42^tjR6>}4`F_Mo^N(~Spjcg z_uY$}zui*PuDJjrpP0Pd+x^5ds3TG#f?57dFL{auS_W8|G*o}gcnsKYjS6*t8VI<) zcjqTzW(Hk*t-Qhq`Xe+x%}sxXRerScbPGv8hlJ;CnU-!Nl=# zR=iTFf9`EItr9iAlAGi}i&~nJ-&+)Y| zMZigh{LXe)uR+4D_Yb+1?I93mHQ5{pId2Fq%DBr7`?ipi;CT!Q&|EO3gH~7g?8>~l zT@%*5BbetH)~%TrAF1!-!=)`FIS{^EVA4WlXYtEy^|@y@yr!C~gX+cp2;|O4x1_Ol z4fPOE^nj(}KPQasY#U{m)}TZt1C5O}vz`A|1J!-D)bR%^+=J-yJsQXDzFiqb+PT0! zIaDWWU(AfOKlSBMS};3xBN*1F2j1-_=%o($ETm8@oR_NvtMDVIv_k zlnNBiHU&h8425{MCa=`vb2YP5KM7**!{1O>5Khzu+5OVGY;V=Vl+24fOE;tMfujoF z0M``}MNnTg3f%Uy6hZi$#g%PUA_-W>uVCYpE*1j>U8cYP6m(>KAVCmbsDf39Lqv0^ zt}V6FWjOU@AbruB7MH2XqtnwiXS2scgjVMH&aF~AIduh#^aT1>*V>-st8%=Kk*{bL zzbQcK(l2~)*A8gvfX=RPsNnjfkRZ@3DZ*ff5rmx{@iYJV+a@&++}ZW+za2fU>&(4y`6wgMpQGG5Ah(9oGcJ^P(H< zvYn5JE$2B`Z7F6ihy>_49!6}(-)oZ(zryIXt=*a$bpIw^k?>RJ2 zQYr>-D#T`2ZWDU$pM89Cl+C<;J!EzHwn(NNnWpYFqDDZ_*FZ{9KQRcSrl5T>dj+eA zi|okW;6)6LR5zebZJtZ%6Gx8^=2d9>_670!8Qm$wd+?zc4RAfV!ZZ$jV0qrv(D`db zm_T*KGCh3CJGb(*X6nXzh!h9@BZ-NO8py|wG8Qv^N*g?kouH4%QkPU~Vizh-D3<@% zGomx%q42B7B}?MVdv1DFb!axQ73AUxqr!yTyFlp%Z1IAgG49usqaEbI_RnbweR;Xs zpJq7GKL_iqi8Md?f>cR?^0CA+Uk(#mTlGdZbuC*$PrdB$+EGiW**=$A3X&^lM^K2s zzwc3LtEs5|ho z2>U(-GL`}eNgL-nv3h7E<*<>C%O^=mmmX0`jQb6$mP7jUKaY4je&dCG{x$`0=_s$+ zSpgn!8f~ya&U@c%{HyrmiW2&Wzc#Sw@+14sCpTWReYpF9EQ|7vF*g|sqG3hx67g}9 zwUj5QP2Q-(KxovRtL|-62_QsHLD4Mu&qS|iDp%!rs(~ah8FcrGb?Uv^Qub5ZT_kn%I^U2rxo1DDpmN@8uejxik`DK2~IDi1d?%~pR7i#KTS zA78XRx<(RYO0_uKnw~vBKi9zX8VnjZEi?vD?YAw}y+)wIjIVg&5(=%rjx3xQ_vGCy z*&$A+bT#9%ZjI;0w(k$|*x{I1c!ECMus|TEA#QE%#&LxfGvijl7Ih!B2 z6((F_gwkV;+oSKrtr&pX&fKo3s3`TG@ye+k3Ov)<#J|p8?vKh@<$YE@YIU1~@7{f+ zydTna#zv?)6&s=1gqH<-piG>E6XW8ZI7&b@-+Yk0Oan_CW!~Q2R{QvMm8_W1IV8<+ zQTyy=(Wf*qcQubRK)$B;QF}Y>V6d_NM#=-ydM?%EPo$Q+jkf}*UrzR?Nsf?~pzIj$ z<$wN;7c!WDZ(G_7N@YgZ``l;_eAd3+;omNjlpfn;0(B7L)^;;1SsI6Le+c^ULe;O@ zl+Z@OOAr4$a;=I~R0w4jO`*PKBp?3K+uJ+Tu8^%i<_~bU!p%so z^sjol^slR`W@jiqn!M~eClIIl+`A5%lGT{z^mRbpv}~AyO%R*jmG_Wrng{B9TwIuS z0!@fsM~!57K1l0%{yy(#no}roy#r!?0wm~HT!vLDfEBs9x#`9yCKgufm0MjVRfZ=f z4*ZRc2Lgr(P+j2zQE_JzYmP0*;trl7{*N341Cq}%^M^VC3gKG-hY zmPT>ECyrhIoFhnMB^qpdbiuI}pk{qPbK^}0?Rf7^{98+95zNq6!RuV_zAe&nDk0;f zez~oXlE5%ve^TmBEt*x_X#fs(-En$jXr-R4sb$b~`nS=iOy|OVrph(U&cVS!IhmZ~ zKIRA9X%Wp1J=vTvHZ~SDe_JXOe9*fa zgEPf;gD^|qE=dl>Qkx3(80#SE7oxXQ(n4qQ#by{uppSKoDbaq`U+fRqk0BwI>IXV3 zD#K%ASkzd7u>@|pA=)Z>rQr@dLH}*r7r0ng zxa^eME+l*s7{5TNu!+bD{Pp@2)v%g6^>yj{XP&mShhg9GszNu4ITW=XCIUp2Xro&1 zg_D=J3r)6hp$8+94?D$Yn2@Kp-3LDsci)<-H!wCeQt$e9Jk)K86hvV^*Nj-Ea*o;G zsuhRw$H{$o>8qByz1V!(yV{p_0X?Kmy%g#1oSmlHsw;FQ%j9S#}ha zm0Nx09@jmOtP8Q+onN^BAgd8QI^(y!n;-APUpo5WVdmp8!`yKTlF>cqn>ag`4;o>i zl!M0G-(S*fm6VjYy}J}0nX7nJ$h`|b&KuW4d&W5IhbR;-)*9Y0(Jj|@j`$xoPQ=Cl diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa40fb3d1e0710331a48de5d256da3f275d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 520 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 diff --git a/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/examples/manager/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632cfddf3d9dade342351e627a0a75609fb46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/manager/macos/Runner/Configs/AppInfo.xcconfig b/examples/manager/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 8167a552..00000000 --- a/examples/manager/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = manager - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.manager - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/examples/manager/macos/Runner/Configs/Debug.xcconfig b/examples/manager/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd94..00000000 --- a/examples/manager/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/examples/manager/macos/Runner/Configs/Release.xcconfig b/examples/manager/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f495..00000000 --- a/examples/manager/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/examples/manager/macos/Runner/Configs/Warnings.xcconfig b/examples/manager/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf47..00000000 --- a/examples/manager/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/examples/manager/macos/Runner/DebugProfile.entitlements b/examples/manager/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a30..00000000 --- a/examples/manager/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/examples/manager/macos/Runner/Info.plist b/examples/manager/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6..00000000 --- a/examples/manager/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/examples/manager/macos/Runner/MainFlutterWindow.swift b/examples/manager/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 3cc05eb2..00000000 --- a/examples/manager/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/examples/manager/macos/Runner/Release.entitlements b/examples/manager/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a4..00000000 --- a/examples/manager/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/examples/manager/macos/RunnerTests/RunnerTests.swift b/examples/manager/macos/RunnerTests/RunnerTests.swift deleted file mode 100644 index 5418c9f5..00000000 --- a/examples/manager/macos/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import FlutterMacOS -import Cocoa -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/examples/manager/pubspec.yaml b/examples/manager/pubspec.yaml deleted file mode 100644 index f4c8be00..00000000 --- a/examples/manager/pubspec.yaml +++ /dev/null @@ -1,41 +0,0 @@ -name: manager -description: "A new Flutter project." -publish_to: "none" - -version: 1.0.0+1 - -environment: - sdk: ">=3.3.1 <4.0.0" -dependencies: - flutter: - sdk: flutter - cupertino_icons: ^1.0.6 - drift: ^2.16.0 - drift_dev: ^2.16.0 - flutter_riverpod: ^2.5.1 - hooks_riverpod: ^2.5.1 - flutter_hooks: ^0.20.5 - riverpod_annotation: ^2.3.5 - sqlite3_flutter_libs: ^0.5.20 - sqlite3: ^2.4.2 - path: ^1.9.0 - path_provider: ^2.1.2 - flex_color_picker: ^3.4.1 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^3.0.0 - build_runner: ^2.4.9 - riverpod_generator: ^2.4.0 - custom_lint: ^0.6.4 - riverpod_lint: ^2.3.10 - -flutter: - uses-material-design: true - -dependency_overrides: - drift_dev: - path: ../../drift_dev - drift: - path: ../../drift \ No newline at end of file diff --git a/examples/manager/test/widget_test.dart b/examples/manager/test/widget_test.dart deleted file mode 100644 index a9ed2a8d..00000000 --- a/examples/manager/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:manager/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/examples/manager/web/favicon.png b/examples/manager/web/favicon.png deleted file mode 100644 index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/examples/manager/web/icons/Icon-192.png b/examples/manager/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07473333cf1dd31e9eed89862a5d52aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5292 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 diff --git a/examples/manager/web/icons/Icon-512.png b/examples/manager/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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 diff --git a/examples/manager/web/icons/Icon-maskable-192.png b/examples/manager/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e525556d5d89141648c724331630325d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! diff --git a/examples/manager/web/icons/Icon-maskable-512.png b/examples/manager/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691fbdb0b7efa65097c7cc1edac12a6d3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20998 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 MPgg&ebxsLQ025IeI{*Lx diff --git a/examples/manager/web/index.html b/examples/manager/web/index.html deleted file mode 100644 index 39bf2a38..00000000 --- a/examples/manager/web/index.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - manager - - - - - - - - - - diff --git a/examples/manager/web/manifest.json b/examples/manager/web/manifest.json deleted file mode 100644 index 7cd1d06a..00000000 --- a/examples/manager/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "manager", - "short_name": "manager", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/examples/manager/windows/.gitignore b/examples/manager/windows/.gitignore deleted file mode 100644 index d492d0d9..00000000 --- a/examples/manager/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/examples/manager/windows/CMakeLists.txt b/examples/manager/windows/CMakeLists.txt deleted file mode 100644 index 90b7800c..00000000 --- a/examples/manager/windows/CMakeLists.txt +++ /dev/null @@ -1,108 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.14) -project(manager LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "manager") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(VERSION 3.14...3.25) - -# Define build configuration option. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() -# Define settings for the Profile build mode. -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build; see runner/CMakeLists.txt. -add_subdirectory("runner") - - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Copy the native assets provided by the build.dart from all packages. -set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") -install(DIRECTORY "${NATIVE_ASSETS_DIR}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/examples/manager/windows/flutter/CMakeLists.txt b/examples/manager/windows/flutter/CMakeLists.txt deleted file mode 100644 index 903f4899..00000000 --- a/examples/manager/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,109 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# Set fallback configurations for older versions of the flutter tool. -if (NOT DEFINED FLUTTER_TARGET_PLATFORM) - set(FLUTTER_TARGET_PLATFORM "windows-x64") -endif() - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - ${FLUTTER_TARGET_PLATFORM} $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/examples/manager/windows/flutter/generated_plugin_registrant.cc b/examples/manager/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 988f3c8f..00000000 --- a/examples/manager/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - Sqlite3FlutterLibsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); -} diff --git a/examples/manager/windows/flutter/generated_plugin_registrant.h b/examples/manager/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85..00000000 --- a/examples/manager/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/examples/manager/windows/flutter/generated_plugins.cmake b/examples/manager/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 8abff957..00000000 --- a/examples/manager/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - sqlite3_flutter_libs -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/examples/manager/windows/runner/CMakeLists.txt b/examples/manager/windows/runner/CMakeLists.txt deleted file mode 100644 index 394917c0..00000000 --- a/examples/manager/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -# Define the application target. To change its name, change BINARY_NAME in the -# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer -# work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add preprocessor definitions for the build version. -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") - -# Disable Windows macros that collide with C++ standard library functions. -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") - -# Add dependency libraries and include directories. Add any application-specific -# dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/examples/manager/windows/runner/Runner.rc b/examples/manager/windows/runner/Runner.rc deleted file mode 100644 index 1819ae23..00000000 --- a/examples/manager/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) -#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD -#else -#define VERSION_AS_NUMBER 1,0,0,0 -#endif - -#if defined(FLUTTER_VERSION) -#define VERSION_AS_STRING FLUTTER_VERSION -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "manager" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "manager" "\0" - VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "manager.exe" "\0" - VALUE "ProductName", "manager" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/examples/manager/windows/runner/flutter_window.cpp b/examples/manager/windows/runner/flutter_window.cpp deleted file mode 100644 index 955ee303..00000000 --- a/examples/manager/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - - flutter_controller_->engine()->SetNextFrameCallback([&]() { - this->Show(); - }); - - // Flutter can complete the first frame before the "show window" callback is - // registered. The following call ensures a frame is pending to ensure the - // window is shown. It is a no-op if the first frame hasn't completed yet. - flutter_controller_->ForceRedraw(); - - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/examples/manager/windows/runner/flutter_window.h b/examples/manager/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652f..00000000 --- a/examples/manager/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/examples/manager/windows/runner/main.cpp b/examples/manager/windows/runner/main.cpp deleted file mode 100644 index 609d5eac..00000000 --- a/examples/manager/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.Create(L"manager", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/examples/manager/windows/runner/resource.h b/examples/manager/windows/runner/resource.h deleted file mode 100644 index 66a65d1e..00000000 --- a/examples/manager/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/examples/manager/windows/runner/resources/app_icon.ico b/examples/manager/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6370ebb9253ad831cc31de4a9c965f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33772 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 diff --git a/examples/manager/windows/runner/runner.exe.manifest b/examples/manager/windows/runner/runner.exe.manifest deleted file mode 100644 index a42ea768..00000000 --- a/examples/manager/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/examples/manager/windows/runner/utils.cpp b/examples/manager/windows/runner/utils.cpp deleted file mode 100644 index b2b08734..00000000 --- a/examples/manager/windows/runner/utils.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr) - -1; // remove the trailing null character - int input_length = (int)wcslen(utf16_string); - std::string utf8_string; - if (target_length <= 0 || target_length > utf8_string.max_size()) { - return utf8_string; - } - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - input_length, utf8_string.data(), target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/examples/manager/windows/runner/utils.h b/examples/manager/windows/runner/utils.h deleted file mode 100644 index 3879d547..00000000 --- a/examples/manager/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/examples/manager/windows/runner/win32_window.cpp b/examples/manager/windows/runner/win32_window.cpp deleted file mode 100644 index 60608d0f..00000000 --- a/examples/manager/windows/runner/win32_window.cpp +++ /dev/null @@ -1,288 +0,0 @@ -#include "win32_window.h" - -#include -#include - -#include "resource.h" - -namespace { - -/// Window attribute that enables dark mode window decorations. -/// -/// Redefined in case the developer's machine has a Windows SDK older than -/// version 10.0.22000.0. -/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute -#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE -#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 -#endif - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -/// Registry key for app theme preference. -/// -/// A value of 0 indicates apps should use dark mode. A non-zero or missing -/// value indicates apps should use light mode. -constexpr const wchar_t kGetPreferredBrightnessRegKey[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; -constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - } - FreeLibrary(user32_module); -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registrar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::Create(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - UpdateTheme(window); - - return OnCreate(); -} - -bool Win32Window::Show() { - return ShowWindow(window_handle_, SW_SHOWNORMAL); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - - case WM_DWMCOLORIZATIONCOLORCHANGED: - UpdateTheme(hwnd); - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} - -void Win32Window::UpdateTheme(HWND const window) { - DWORD light_mode; - DWORD light_mode_size = sizeof(light_mode); - LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, - kGetPreferredBrightnessRegValue, - RRF_RT_REG_DWORD, nullptr, &light_mode, - &light_mode_size); - - if (result == ERROR_SUCCESS) { - BOOL enable_dark_mode = light_mode == 0; - DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, - &enable_dark_mode, sizeof(enable_dark_mode)); - } -} diff --git a/examples/manager/windows/runner/win32_window.h b/examples/manager/windows/runner/win32_window.h deleted file mode 100644 index e901dde6..00000000 --- a/examples/manager/windows/runner/win32_window.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates a win32 window with |title| that is positioned and sized using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size this function will scale the inputted width and height as - // as appropriate for the default monitor. The window is invisible until - // |Show| is called. Returns true if the window was created successfully. - bool Create(const std::wstring& title, const Point& origin, const Size& size); - - // Show the current window. Returns true if the window was successfully shown. - bool Show(); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - // Update the window frame's theme to match the system theme. - static void UpdateTheme(HWND const window); - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ From 1a68efce584f10a09d31f32421cefd6371ff0d77 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 13:54:26 -0400 Subject: [PATCH 64/74] Add bulk replace --- drift/lib/src/runtime/manager/manager.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drift/lib/src/runtime/manager/manager.dart b/drift/lib/src/runtime/manager/manager.dart index 446e69b7..ba366487 100644 --- a/drift/lib/src/runtime/manager/manager.dart +++ b/drift/lib/src/runtime/manager/manager.dart @@ -564,4 +564,16 @@ abstract class RootTableManager< Future replace(Insertable entity) { return $state.db.update($state._tableAsTableInfo).replace(entity); } + + /// Replace multiple rows in the table + /// + /// If any of the [entities] has an absent value (set to null on the [DataClass] or + /// explicitly to absent on the [UpdateCompanion]), and a default value for + /// the field exists, that default value will be used. Otherwise, the field + /// will be reset to null. This behavior is different to [update], which simply + /// ignores such fields without changing them in the database. + Future bulkReplace(Iterable> entities) { + return $state.db + .batch((b) => b.replaceAll($state._tableAsTableInfo, entities)); + } } From 4c4122f52cfd38dfb8cc182131dcfde29231642e Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 13:56:45 -0400 Subject: [PATCH 65/74] add tests for bulk replace --- drift/test/manager/manager_test.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drift/test/manager/manager_test.dart b/drift/test/manager/manager_test.dart index db0c2d01..3bfdd0bc 100644 --- a/drift/test/manager/manager_test.dart +++ b/drift/test/manager/manager_test.dart @@ -105,6 +105,24 @@ void main() { .then((value) => value.description), completion("Hello")); + // Bulk Replace + await db.managers.categories.bulkReplace([ + obj1.copyWith(description: "Hello"), + obj2.copyWith(description: "World") + ]); + expect( + db.managers.categories + .filter(((f) => f.id(obj1.id))) + .getSingle() + .then((value) => value.description), + completion("Hello")); + expect( + db.managers.categories + .filter(((f) => f.id(obj2.id))) + .getSingle() + .then((value) => value.description), + completion("World")); + // Update All Rows final update2 = db.managers.categories .update((o) => o(priority: Value(CategoryPriority.high))); From 2ea3d3d2bcefc272ee11e9cd2b99821eeb6366ba Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 13:59:57 -0400 Subject: [PATCH 66/74] add docs for CRUD --- docs/lib/snippets/dart_api/manager.dart | 61 +++++++++++++++++++++++-- docs/pages/docs/Dart API/manager.md | 18 +++++++- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index 085efc1e..933234ed 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -1,9 +1,64 @@ +import 'package:drift/drift.dart'; + import '../setup/database.dart'; extension ManagerExamples on AppDatabase { - // #docregion create + // #docregion manager_create Future createTodoItem() async { - await managers.todoItems.create((o) => o(title: '', content: '')); + // Create a new item + await managers.todoItems + .create((o) => o(title: 'Title', content: 'Content')); + + // We can also use `mode` and `onConflict` parameters, just + // like in the `[InsertStatement.insert]` method on the table + await managers.todoItems.create( + (o) => o(title: 'Title', content: 'New Content'), + mode: InsertMode.replace); + + // We can also create multiple items at once + await managers.todoItems.bulkCreate( + (o) => [ + o(title: 'Title 1', content: 'Content 1'), + o(title: 'Title 2', content: 'Content 2'), + ], + ); } - // #enddocregion create + // #enddocregion manager_create + + // #docregion manager_update + Future updateTodoItems() async { + // Update all items + await managers.todoItems.update((o) => o(content: Value('New Content'))); + + // Update multiple items + await managers.todoItems + .filter((f) => f.id.isIn([1, 2, 3])) + .update((o) => o(content: Value('New Content'))); + } + // #docregion manager_update + + // #docregion manager_replace + Future replaceTodoItems() async { + // Replace a single item + var obj = await managers.todoItems.filter((o) => o.id(1)).getSingle(); + obj = obj.copyWith(content: 'New Content'); + await managers.todoItems.replace(obj); + + // Replace multiple items + var objs = + await managers.todoItems.filter((o) => o.id.isIn([1, 2, 3])).get(); + objs = objs.map((o) => o.copyWith(content: 'New Content')).toList(); + await managers.todoItems.bulkReplace(objs); + } + // #docregion manager_replace + + // #docregion manager_delete + Future deleteTodoItems() async { + // Delete all items + await managers.todoItems.delete(); + + // Delete a single item + await managers.todoItems.filter((f) => f.id(5)).delete(); + } + // #docregion manager_delete } diff --git a/docs/pages/docs/Dart API/manager.md b/docs/pages/docs/Dart API/manager.md index f4ac99fb..d43efbfb 100644 --- a/docs/pages/docs/Dart API/manager.md +++ b/docs/pages/docs/Dart API/manager.md @@ -23,11 +23,27 @@ instructions. ### Filtering across tables +### Ordering + ## Updates +We can use the manager to update rows in bulk or individual rows that meet a certain condition. + +{% include "blocks/snippet" snippets = snippets name = 'manager_update' %} + +We can also replace an entire row with a new one. Or even replace multiple rows at once. + +{% include "blocks/snippet" snippets = snippets name = 'manager_replace' %} ## Creating rows +The manager includes a method for quickly inserting rows into a table. +We can insert a single row or multiple rows at once. + +{% include "blocks/snippet" snippets = snippets name = 'manager_create' %} -{% include "blocks/snippet" snippets = snippets name = 'create' %} ## Deleting rows +We may also delete rows from a table using the manager. +Any rows that meet the specified condition will be deleted. + +{% include "blocks/snippet" snippets = snippets name = 'manager_delete' %} From 69c25d8e622d2fa45acfc338f23f00de9305313f Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 15:21:20 -0400 Subject: [PATCH 67/74] add type to column ordering --- drift_dev/lib/src/writer/manager_writer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drift_dev/lib/src/writer/manager_writer.dart b/drift_dev/lib/src/writer/manager_writer.dart index 9ccd2a1e..2aa2f319 100644 --- a/drift_dev/lib/src/writer/manager_writer.dart +++ b/drift_dev/lib/src/writer/manager_writer.dart @@ -149,7 +149,7 @@ class _RegularOrderingWriter extends _OrderingWriter { void writeOrdering(TextEmitter leaf) { leaf ..writeDriftRef("ColumnOrderings") - ..write(" get $orderingName =>") + ..write("<$type> get $orderingName =>") ..writeDriftRef("ColumnOrderings") ..write("(\$table.$fieldGetter);"); } From b67e4234e7642833f9535449c922d5a6138a24c4 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 15:43:36 -0400 Subject: [PATCH 68/74] update docs --- docs/lib/snippets/dart_api/manager.dart | 144 +++++++++++++++++- .../drift_files/custom_queries.drift.dart | 15 +- .../modular/many_to_many/json.drift.dart | 11 +- .../many_to_many/relational.drift.dart | 13 +- docs/lib/snippets/setup/database.dart | 12 +- docs/pages/docs/Dart API/manager.md | 57 ++++++- drift/example/main.g.dart | 15 +- drift/lib/src/runtime/manager/ordering.dart | 4 +- drift/test/generated/custom_tables.g.dart | 37 ++--- drift/test/generated/todos.g.dart | 62 ++++---- .../regress_2166_test.g.dart | 4 +- 11 files changed, 291 insertions(+), 83 deletions(-) diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index 933234ed..7d4c40b4 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -35,7 +35,7 @@ extension ManagerExamples on AppDatabase { .filter((f) => f.id.isIn([1, 2, 3])) .update((o) => o(content: Value('New Content'))); } - // #docregion manager_update + // #enddocregion manager_update // #docregion manager_replace Future replaceTodoItems() async { @@ -50,7 +50,7 @@ extension ManagerExamples on AppDatabase { objs = objs.map((o) => o.copyWith(content: 'New Content')).toList(); await managers.todoItems.bulkReplace(objs); } - // #docregion manager_replace + // #enddocregion manager_replace // #docregion manager_delete Future deleteTodoItems() async { @@ -60,5 +60,143 @@ extension ManagerExamples on AppDatabase { // Delete a single item await managers.todoItems.filter((f) => f.id(5)).delete(); } - // #docregion manager_delete + // #enddocregion manager_delete + + // #docregion manager_select + Future selectTodoItems() async { + // Get all items + managers.todoItems.get(); + + // A stream of all the todo items, updated in real-time + managers.todoItems.watch(); + + // To get a single item, apply a filter and call `getSingle` + await managers.todoItems.filter((f) => f.id(1)).getSingle(); + } + // #enddocregion manager_select + + // #docregion manager_filter + Future filterTodoItems() async { + // All items with a title of "Title" + await managers.todoItems.filter((f) => f.title("Title")).get(); + + // All items with a title of "Title" and content of "Content" + await managers.todoItems + .filter((f) => f.title("Title") & f.content("Content")) + .get(); + + // All items with a title of "Title" or content that is not null + await managers.todoItems + .filter((f) => f.title("Title") | f.content.not.isNull()) + .get(); + } + // #enddocregion manager_filter + + // #docregion manager_type_specific_filter + Future filterWithType() async { + // Filter all items created since 7 days ago + await managers.todoItems + .filter((f) => + f.createdAt.isAfter(DateTime.now().subtract(Duration(days: 7)))) + .get(); + + // Filter all items with a title that starts with "Title" + await managers.todoItems.filter((f) => f.title.startsWith('Title')).get(); + } +// #enddocregion manager_type_specific_filter + +// #docregion manager_ordering + Future orderWithType() async { + // Order all items by their creation date in ascending order + await managers.todoItems.orderBy((o) => o.createdAt.asc()).get(); + + // Order all items by their title in ascending order and then by their content in ascending order + await managers.todoItems + .orderBy((o) => o.title.asc() & o.content.asc()) + .get(); + } +// #enddocregion manager_ordering + +// #docregion manager_count + Future count() async { + // Count all items + await managers.todoItems.count(); + + // Count all items with a title of "Title" + await managers.todoItems.filter((f) => f.title("Title")).count(); + } +// #enddocregion manager_count + +// #docregion manager_exists + Future exists() async { + // Check if any items exist + await managers.todoItems.exists(); + + // Check if any items with a title of "Title" exist + await managers.todoItems.filter((f) => f.title("Title")).exists(); + } +// #enddocregion manager_exists } + +// #docregion manager_filter_extensions +// Extend drifts built-in filters by combining the existing filters to create a new one +// or by creating a new filter from scratch +extension After2000Filter on ColumnFilters { + // Create a new filter by combining existing filters + ComposableFilter after2000orBefore1900() => + isAfter(DateTime(2000)) | isBefore(DateTime(1900)); + + // Create a new filter from scratch using the `column` property + ComposableFilter filterOnUnixEpoch(int value) => + ComposableFilter(column.unixepoch.equals(value), inverted: inverted); +} + +Future filterWithExtension(AppDatabase db) async { + // Use the custom filters on any column that is of type DateTime + await db.managers.todoItems + .filter((f) => f.createdAt.after2000orBefore1900()) + .get(); + + // Use the custom filter on the `unixepoch` column + await db.managers.todoItems + .filter((f) => f.createdAt.filterOnUnixEpoch(0)) + .get(); +} +// #enddocregion manager_filter_extensions + +// #docregion manager_ordering_extensions +// Extend drifts built-in orderings by create a new ordering from scratch +extension After2000Ordering on ColumnOrderings { + ComposableOrdering byUnixEpoch() => ColumnOrderings(column.unixepoch).asc(); +} + +Future orderingWithExtension(AppDatabase db) async { + // Use the custom orderings on any column that is of type DateTime + await db.managers.todoItems.orderBy((f) => f.createdAt.byUnixEpoch()).get(); +} +// #enddocregion manager_ordering_extensions + +// #docregion manager_custom_filter +// Extend the generated table filter composer to add a custom filter +extension NoContentOrBefore2000FilterX on $$TodoItemsTableFilterComposer { + ComposableFilter noContentOrBefore2000() => + (content.isNull() | createdAt.isBefore(DateTime(2000))); +} + +Future customFilter(AppDatabase db) async { + // Use the custom filter on the `TodoItems` table + await db.managers.todoItems.filter((f) => f.noContentOrBefore2000()).get(); +} +// #enddocregion manager_custom_filter + +// #docregion manager_custom_ordering +// Extend the generated table filter composer to add a custom filter +extension ContentThenCreationDataX on $$TodoItemsTableOrderingComposer { + ComposableOrdering contentThenCreatedAt() => content.asc() & createdAt.asc(); +} + +Future customOrdering(AppDatabase db) async { + // Use the custom ordering on the `TodoItems` table + await db.managers.todoItems.orderBy((f) => f.contentThenCreatedAt()).get(); +} +// #enddocregion manager_custom_ordering diff --git a/docs/lib/snippets/drift_files/custom_queries.drift.dart b/docs/lib/snippets/drift_files/custom_queries.drift.dart index 2bb8229a..651da4ec 100644 --- a/docs/lib/snippets/drift_files/custom_queries.drift.dart +++ b/docs/lib/snippets/drift_files/custom_queries.drift.dart @@ -53,8 +53,8 @@ class $$CategoriesTableFilterComposer class $$CategoriesTableOrderingComposer extends i0.OrderingComposer { $$CategoriesTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get name => i0.ColumnOrderings($table.name); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get name => i0.ColumnOrderings($table.name); } class $$CategoriesTableProcessedTableManager extends i0.ProcessedTableManager< @@ -143,10 +143,10 @@ class $$TodoItemsTableFilterComposer class $$TodoItemsTableOrderingComposer extends i0.OrderingComposer { $$TodoItemsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get title => i0.ColumnOrderings($table.title); - i0.ColumnOrderings get content => i0.ColumnOrderings($table.content); - i0.ColumnOrderings get categoryId => i0.ColumnOrderings($table.category); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get title => i0.ColumnOrderings($table.title); + i0.ColumnOrderings get content => i0.ColumnOrderings($table.content); + i0.ColumnOrderings get categoryId => i0.ColumnOrderings($table.category); i0.ComposableOrdering category( i0.ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { return $composeWithJoins( @@ -161,7 +161,8 @@ class $$TodoItemsTableOrderingComposer builder: o); } - i0.ColumnOrderings get dueDate => i0.ColumnOrderings($table.dueDate); + i0.ColumnOrderings get dueDate => + i0.ColumnOrderings($table.dueDate); } class $$TodoItemsTableProcessedTableManager extends i0.ProcessedTableManager< diff --git a/docs/lib/snippets/modular/many_to_many/json.drift.dart b/docs/lib/snippets/modular/many_to_many/json.drift.dart index 18a64f91..e83c4267 100644 --- a/docs/lib/snippets/modular/many_to_many/json.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/json.drift.dart @@ -31,9 +31,10 @@ class $$BuyableItemsTableFilterComposer class $$BuyableItemsTableOrderingComposer extends i0.OrderingComposer { $$BuyableItemsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); - i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get description => + i0.ColumnOrderings($table.description); + i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); } class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< @@ -115,8 +116,8 @@ class $$ShoppingCartsTableFilterComposer class $$ShoppingCartsTableOrderingComposer extends i0.OrderingComposer { $$ShoppingCartsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get entries => i0.ColumnOrderings($table.entries); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get entries => i0.ColumnOrderings($table.entries); } class $$ShoppingCartsTableProcessedTableManager diff --git a/docs/lib/snippets/modular/many_to_many/relational.drift.dart b/docs/lib/snippets/modular/many_to_many/relational.drift.dart index ef569503..100d677e 100644 --- a/docs/lib/snippets/modular/many_to_many/relational.drift.dart +++ b/docs/lib/snippets/modular/many_to_many/relational.drift.dart @@ -49,9 +49,10 @@ class $$BuyableItemsTableFilterComposer class $$BuyableItemsTableOrderingComposer extends i0.OrderingComposer { $$BuyableItemsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); - i0.ColumnOrderings get description => i0.ColumnOrderings($table.description); - i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get description => + i0.ColumnOrderings($table.description); + i0.ColumnOrderings get price => i0.ColumnOrderings($table.price); } class $$BuyableItemsTableProcessedTableManager extends i0.ProcessedTableManager< @@ -142,7 +143,7 @@ class $$ShoppingCartsTableFilterComposer class $$ShoppingCartsTableOrderingComposer extends i0.OrderingComposer { $$ShoppingCartsTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); + i0.ColumnOrderings get id => i0.ColumnOrderings($table.id); } class $$ShoppingCartsTableProcessedTableManager @@ -237,7 +238,7 @@ class $$ShoppingCartEntriesTableFilterComposer extends i0 class $$ShoppingCartEntriesTableOrderingComposer extends i0 .OrderingComposer { $$ShoppingCartEntriesTableOrderingComposer(super.db, super.table); - i0.ColumnOrderings get shoppingCartId => + i0.ColumnOrderings get shoppingCartId => i0.ColumnOrderings($table.shoppingCart); i0.ComposableOrdering shoppingCart( i0.ComposableOrdering Function($$ShoppingCartsTableOrderingComposer o) @@ -254,7 +255,7 @@ class $$ShoppingCartEntriesTableOrderingComposer extends i0 builder: o); } - i0.ColumnOrderings get itemId => i0.ColumnOrderings($table.item); + i0.ColumnOrderings get itemId => i0.ColumnOrderings($table.item); i0.ComposableOrdering item( i0.ComposableOrdering Function($$BuyableItemsTableOrderingComposer o) o) { return $composeWithJoins( diff --git a/docs/lib/snippets/setup/database.dart b/docs/lib/snippets/setup/database.dart index 00d175a5..06fd8d17 100644 --- a/docs/lib/snippets/setup/database.dart +++ b/docs/lib/snippets/setup/database.dart @@ -24,12 +24,20 @@ class TodoItems extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get title => text().withLength(min: 6, max: 32)(); TextColumn get content => text().named('body')(); - IntColumn get category => integer().nullable()(); + IntColumn get category => + integer().nullable().references(TodoCategory, #id)(); + DateTimeColumn get createdAt => dateTime().nullable()(); } + +class TodoCategory extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get description => text()(); +} + // #enddocregion table // #docregion open -@DriftDatabase(tables: [TodoItems]) +@DriftDatabase(tables: [TodoItems, TodoCategory]) class AppDatabase extends _$AppDatabase { // #enddocregion open // After generating code, this class needs to define a `schemaVersion` getter diff --git a/docs/pages/docs/Dart API/manager.md b/docs/pages/docs/Dart API/manager.md index d43efbfb..35c4ce98 100644 --- a/docs/pages/docs/Dart API/manager.md +++ b/docs/pages/docs/Dart API/manager.md @@ -5,11 +5,12 @@ data: weight: 1 template: layouts/docs/single +path: /docs/getting-started/manager/ --- {% assign snippets = 'package:drift_docs/snippets/dart_api/manager.dart.excerpt.json' | readString | json_decode %} -With generated code, drift allows writing SQL queries in typesafe Dart. +With generated code, drift allows writing SQL queries in type-safe Dart. While this is provides lots of flexibility, it requires familiarity with SQL. As a simpler alternative, drift 2.17 introduced a new set of APIs designed to make common queries much easier to write. @@ -19,12 +20,40 @@ instructions. ## Select -### Count and exists +The manager simplifies the process of retrieving rows from a table. Use it to read rows from the table or watch +for changes to the table. + +{% include "blocks/snippet" snippets = snippets name = 'manager_select' %} + +The manager provides a really easy to use API for selecting rows from a table. These can be combined with `|` and `&` and parenthesis to construct more complex queries. Use `.not` to negate a condition. + +{% include "blocks/snippet" snippets = snippets name = 'manager_filter' %} + +Every column has filters for equality, inequality and nullability. +Type specific filters for `int`, `double`, `Int64`, `DateTime` and `String` are included out of the box. + +{% include "blocks/snippet" snippets = snippets name = 'manager_type_specific_filter' %} + ### Filtering across tables ### Ordering +You can also order the results of a query using the `orderBy` method. The syntax is similar to the `filter` method. +Use the `&` to combine multiple orderings. Orderings are applied in the order they are added. +You can also use ordering across multiple tables just like with filters. + +{% include "blocks/snippet" snippets = snippets name = 'manager_ordering' %} + + +### Count and exists +The manager makes it easy to check if a row exists or to count the number of rows that match a certain condition. + +{% include "blocks/snippet" snippets = snippets name = 'manager_count' %} + +{% include "blocks/snippet" snippets = snippets name = 'manager_exists' %} + + ## Updates We can use the manager to update rows in bulk or individual rows that meet a certain condition. @@ -47,3 +76,27 @@ Any rows that meet the specified condition will be deleted. {% include "blocks/snippet" snippets = snippets name = 'manager_delete' %} +## Extensions +The manager provides a set of filters and orderings out of the box for common types, however you can +extend them to add new filters and orderings. + +#### Custom Column Filters +If you want to add new filters for individual columns types, you can extend the `ColumnFilter` class. + +{% include "blocks/snippet" snippets = snippets name = 'manager_filter_extensions' %} + +#### Custom Table Filters +You can also create custom filters that operate on multiple columns by extending generated filtersets. + +{% include "blocks/snippet" snippets = snippets name = 'manager_custom_filter' %} + +#### Custom Column Orderings +You can create new ordering methods for individual columns types by extending the `ColumnOrdering` class. +Use the `ComposableOrdering` class to create complex orderings. + +{% include "blocks/snippet" snippets = snippets name = 'manager_ordering_extensions' %} + +#### Custom Table Filters +You can also create custom filters that operate on multiple columns by extending generated filtersets. + +{% include "blocks/snippet" snippets = snippets name = 'manager_custom_filter' %} \ No newline at end of file diff --git a/drift/example/main.g.dart b/drift/example/main.g.dart index c2ecb319..32549c1a 100644 --- a/drift/example/main.g.dart +++ b/drift/example/main.g.dart @@ -729,8 +729,8 @@ class $$TodoCategoriesTableFilterComposer class $$TodoCategoriesTableOrderingComposer extends OrderingComposer<_$Database, $TodoCategoriesTable> { $$TodoCategoriesTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get name => ColumnOrderings($table.name); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); } class $$TodoCategoriesTableProcessedTableManager extends ProcessedTableManager< @@ -818,10 +818,10 @@ class $$TodoItemsTableFilterComposer class $$TodoItemsTableOrderingComposer extends OrderingComposer<_$Database, $TodoItemsTable> { $$TodoItemsTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get title => ColumnOrderings($table.title); - ColumnOrderings get content => ColumnOrderings($table.content); - ColumnOrderings get categoryIdId => ColumnOrderings($table.categoryId); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get content => ColumnOrderings($table.content); + ColumnOrderings get categoryIdId => ColumnOrderings($table.categoryId); ComposableOrdering categoryId( ComposableOrdering Function($$TodoCategoriesTableOrderingComposer o) o) { return $composeWithJoins( @@ -835,7 +835,8 @@ class $$TodoItemsTableOrderingComposer builder: o); } - ColumnOrderings get generatedText => ColumnOrderings($table.generatedText); + ColumnOrderings get generatedText => + ColumnOrderings($table.generatedText); } class $$TodoItemsTableProcessedTableManager extends ProcessedTableManager< diff --git a/drift/lib/src/runtime/manager/ordering.dart b/drift/lib/src/runtime/manager/ordering.dart index ed607eb5..ac703254 100644 --- a/drift/lib/src/runtime/manager/ordering.dart +++ b/drift/lib/src/runtime/manager/ordering.dart @@ -9,7 +9,7 @@ class ColumnOrderings { ColumnOrderings(this.column); /// Column that this [ColumnOrderings] wraps - GeneratedColumn column; + Expression column; /// Sort this column in ascending order /// @@ -30,7 +30,7 @@ class OrderingBuilder { final OrderingMode mode; /// The column that the ordering is applied to - final GeneratedColumn column; + final Expression column; /// Create a new ordering builder, will be used by the [TableManagerState] to create [OrderingTerm]s OrderingBuilder(this.mode, this.column); diff --git a/drift/test/generated/custom_tables.g.dart b/drift/test/generated/custom_tables.g.dart index 51c53c4f..a949b85b 100644 --- a/drift/test/generated/custom_tables.g.dart +++ b/drift/test/generated/custom_tables.g.dart @@ -1981,8 +1981,8 @@ class $WithDefaultsFilterComposer class $WithDefaultsOrderingComposer extends OrderingComposer<_$CustomTablesDb, WithDefaults> { $WithDefaultsOrderingComposer(super.db, super.table); - ColumnOrderings get a => ColumnOrderings($table.a); - ColumnOrderings get b => ColumnOrderings($table.b); + ColumnOrderings get a => ColumnOrderings($table.a); + ColumnOrderings get b => ColumnOrderings($table.b); } class $WithDefaultsProcessedTableManager extends ProcessedTableManager< @@ -2058,9 +2058,9 @@ class $WithConstraintsFilterComposer class $WithConstraintsOrderingComposer extends OrderingComposer<_$CustomTablesDb, WithConstraints> { $WithConstraintsOrderingComposer(super.db, super.table); - ColumnOrderings get a => ColumnOrderings($table.a); - ColumnOrderings get b => ColumnOrderings($table.b); - ColumnOrderings get c => ColumnOrderings($table.c); + ColumnOrderings get a => ColumnOrderings($table.a); + ColumnOrderings get b => ColumnOrderings($table.b); + ColumnOrderings get c => ColumnOrderings($table.c); } class $WithConstraintsProcessedTableManager extends ProcessedTableManager< @@ -2151,10 +2151,11 @@ class $ConfigTableFilterComposer class $ConfigTableOrderingComposer extends OrderingComposer<_$CustomTablesDb, ConfigTable> { $ConfigTableOrderingComposer(super.db, super.table); - ColumnOrderings get configKey => ColumnOrderings($table.configKey); - ColumnOrderings get configValue => ColumnOrderings($table.configValue); - ColumnOrderings get syncState => ColumnOrderings($table.syncState); - ColumnOrderings get syncStateImplicit => + ColumnOrderings get configKey => ColumnOrderings($table.configKey); + ColumnOrderings get configValue => + ColumnOrderings($table.configValue); + ColumnOrderings get syncState => ColumnOrderings($table.syncState); + ColumnOrderings get syncStateImplicit => ColumnOrderings($table.syncStateImplicit); } @@ -2243,10 +2244,10 @@ class $MytableFilterComposer extends FilterComposer<_$CustomTablesDb, Mytable> { class $MytableOrderingComposer extends OrderingComposer<_$CustomTablesDb, Mytable> { $MytableOrderingComposer(super.db, super.table); - ColumnOrderings get someid => ColumnOrderings($table.someid); - ColumnOrderings get sometext => ColumnOrderings($table.sometext); - ColumnOrderings get isInserting => ColumnOrderings($table.isInserting); - ColumnOrderings get somedate => ColumnOrderings($table.somedate); + ColumnOrderings get someid => ColumnOrderings($table.someid); + ColumnOrderings get sometext => ColumnOrderings($table.sometext); + ColumnOrderings get isInserting => ColumnOrderings($table.isInserting); + ColumnOrderings get somedate => ColumnOrderings($table.somedate); } class $MytableProcessedTableManager extends ProcessedTableManager< @@ -2325,9 +2326,9 @@ class $EmailFilterComposer extends FilterComposer<_$CustomTablesDb, Email> { class $EmailOrderingComposer extends OrderingComposer<_$CustomTablesDb, Email> { $EmailOrderingComposer(super.db, super.table); - ColumnOrderings get sender => ColumnOrderings($table.sender); - ColumnOrderings get title => ColumnOrderings($table.title); - ColumnOrderings get body => ColumnOrderings($table.body); + ColumnOrderings get sender => ColumnOrderings($table.sender); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get body => ColumnOrderings($table.body); } class $EmailProcessedTableManager extends ProcessedTableManager< @@ -2407,8 +2408,8 @@ class $WeirdTableFilterComposer class $WeirdTableOrderingComposer extends OrderingComposer<_$CustomTablesDb, WeirdTable> { $WeirdTableOrderingComposer(super.db, super.table); - ColumnOrderings get sqlClass => ColumnOrderings($table.sqlClass); - ColumnOrderings get textColumn => ColumnOrderings($table.textColumn); + ColumnOrderings get sqlClass => ColumnOrderings($table.sqlClass); + ColumnOrderings get textColumn => ColumnOrderings($table.textColumn); } class $WeirdTableProcessedTableManager extends ProcessedTableManager< diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 16738d29..817eef4f 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -2527,10 +2527,11 @@ class $$CategoriesTableFilterComposer class $$CategoriesTableOrderingComposer extends OrderingComposer<_$TodoDb, $CategoriesTable> { $$CategoriesTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get description => ColumnOrderings($table.description); - ColumnOrderings get priority => ColumnOrderings($table.priority); - ColumnOrderings get descriptionInUpperCase => + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get description => + ColumnOrderings($table.description); + ColumnOrderings get priority => ColumnOrderings($table.priority); + ColumnOrderings get descriptionInUpperCase => ColumnOrderings($table.descriptionInUpperCase); } @@ -2627,11 +2628,12 @@ class $$TodosTableTableFilterComposer class $$TodosTableTableOrderingComposer extends OrderingComposer<_$TodoDb, $TodosTableTable> { $$TodosTableTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get title => ColumnOrderings($table.title); - ColumnOrderings get content => ColumnOrderings($table.content); - ColumnOrderings get targetDate => ColumnOrderings($table.targetDate); - ColumnOrderings get categoryId => ColumnOrderings($table.category); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get title => ColumnOrderings($table.title); + ColumnOrderings get content => ColumnOrderings($table.content); + ColumnOrderings get targetDate => + ColumnOrderings($table.targetDate); + ColumnOrderings get categoryId => ColumnOrderings($table.category); ComposableOrdering category( ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { return $composeWithJoins( @@ -2645,7 +2647,7 @@ class $$TodosTableTableOrderingComposer builder: o); } - ColumnOrderings get status => ColumnOrderings($table.status); + ColumnOrderings get status => ColumnOrderings($table.status); } class $$TodosTableTableProcessedTableManager extends ProcessedTableManager< @@ -2744,11 +2746,13 @@ class $$UsersTableFilterComposer extends FilterComposer<_$TodoDb, $UsersTable> { class $$UsersTableOrderingComposer extends OrderingComposer<_$TodoDb, $UsersTable> { $$UsersTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get name => ColumnOrderings($table.name); - ColumnOrderings get isAwesome => ColumnOrderings($table.isAwesome); - ColumnOrderings get profilePicture => ColumnOrderings($table.profilePicture); - ColumnOrderings get creationTime => ColumnOrderings($table.creationTime); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); + ColumnOrderings get isAwesome => ColumnOrderings($table.isAwesome); + ColumnOrderings get profilePicture => + ColumnOrderings($table.profilePicture); + ColumnOrderings get creationTime => + ColumnOrderings($table.creationTime); } class $$UsersTableProcessedTableManager extends ProcessedTableManager< @@ -2835,8 +2839,8 @@ class $$SharedTodosTableFilterComposer class $$SharedTodosTableOrderingComposer extends OrderingComposer<_$TodoDb, $SharedTodosTable> { $$SharedTodosTableOrderingComposer(super.db, super.table); - ColumnOrderings get todo => ColumnOrderings($table.todo); - ColumnOrderings get user => ColumnOrderings($table.user); + ColumnOrderings get todo => ColumnOrderings($table.todo); + ColumnOrderings get user => ColumnOrderings($table.user); } class $$SharedTodosTableProcessedTableManager extends ProcessedTableManager< @@ -2914,7 +2918,7 @@ class $$PureDefaultsTableFilterComposer class $$PureDefaultsTableOrderingComposer extends OrderingComposer<_$TodoDb, $PureDefaultsTable> { $$PureDefaultsTableOrderingComposer(super.db, super.table); - ColumnOrderings get txt => ColumnOrderings($table.txt); + ColumnOrderings get txt => ColumnOrderings($table.txt); } class $$PureDefaultsTableProcessedTableManager extends ProcessedTableManager< @@ -2984,7 +2988,7 @@ class $$WithCustomTypeTableFilterComposer class $$WithCustomTypeTableOrderingComposer extends OrderingComposer<_$TodoDb, $WithCustomTypeTable> { $$WithCustomTypeTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get id => ColumnOrderings($table.id); } class $$WithCustomTypeTableProcessedTableManager extends ProcessedTableManager< @@ -3071,16 +3075,16 @@ class $$TableWithEveryColumnTypeTableFilterComposer class $$TableWithEveryColumnTypeTableOrderingComposer extends OrderingComposer<_$TodoDb, $TableWithEveryColumnTypeTable> { $$TableWithEveryColumnTypeTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get aBool => ColumnOrderings($table.aBool); - ColumnOrderings get aDateTime => ColumnOrderings($table.aDateTime); - ColumnOrderings get aText => ColumnOrderings($table.aText); - ColumnOrderings get anInt => ColumnOrderings($table.anInt); - ColumnOrderings get anInt64 => ColumnOrderings($table.anInt64); - ColumnOrderings get aReal => ColumnOrderings($table.aReal); - ColumnOrderings get aBlob => ColumnOrderings($table.aBlob); - ColumnOrderings get anIntEnum => ColumnOrderings($table.anIntEnum); - ColumnOrderings get aTextWithConverter => + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get aBool => ColumnOrderings($table.aBool); + ColumnOrderings get aDateTime => ColumnOrderings($table.aDateTime); + ColumnOrderings get aText => ColumnOrderings($table.aText); + ColumnOrderings get anInt => ColumnOrderings($table.anInt); + ColumnOrderings get anInt64 => ColumnOrderings($table.anInt64); + ColumnOrderings get aReal => ColumnOrderings($table.aReal); + ColumnOrderings get aBlob => ColumnOrderings($table.aBlob); + ColumnOrderings get anIntEnum => ColumnOrderings($table.anIntEnum); + ColumnOrderings get aTextWithConverter => ColumnOrderings($table.aTextWithConverter); } diff --git a/drift/test/integration_tests/regress_2166_test.g.dart b/drift/test/integration_tests/regress_2166_test.g.dart index 516af6da..a25f8da4 100644 --- a/drift/test/integration_tests/regress_2166_test.g.dart +++ b/drift/test/integration_tests/regress_2166_test.g.dart @@ -203,8 +203,8 @@ class $$_SomeTableTableFilterComposer class $$_SomeTableTableOrderingComposer extends OrderingComposer<_$_SomeDb, $_SomeTableTable> { $$_SomeTableTableOrderingComposer(super.db, super.table); - ColumnOrderings get id => ColumnOrderings($table.id); - ColumnOrderings get name => ColumnOrderings($table.name); + ColumnOrderings get id => ColumnOrderings($table.id); + ColumnOrderings get name => ColumnOrderings($table.name); } class $$_SomeTableTableProcessedTableManager extends ProcessedTableManager< From 3c39d8456b6166b6aa8d8ee3f26ee2aff9485e90 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 16:00:50 -0400 Subject: [PATCH 69/74] docs complete! --- docs/lib/snippets/dart_api/manager.dart | 81 +++++++++++++++++-------- docs/pages/docs/Dart API/manager.md | 12 ++++ 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index 7d4c40b4..1b586f61 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -78,42 +78,34 @@ extension ManagerExamples on AppDatabase { // #docregion manager_filter Future filterTodoItems() async { // All items with a title of "Title" - await managers.todoItems.filter((f) => f.title("Title")).get(); + managers.todoItems.filter((f) => f.title("Title")); // All items with a title of "Title" and content of "Content" - await managers.todoItems - .filter((f) => f.title("Title") & f.content("Content")) - .get(); + managers.todoItems.filter((f) => f.title("Title") & f.content("Content")); // All items with a title of "Title" or content that is not null - await managers.todoItems - .filter((f) => f.title("Title") | f.content.not.isNull()) - .get(); + managers.todoItems.filter((f) => f.title("Title") | f.content.not.isNull()); } // #enddocregion manager_filter // #docregion manager_type_specific_filter Future filterWithType() async { // Filter all items created since 7 days ago - await managers.todoItems - .filter((f) => - f.createdAt.isAfter(DateTime.now().subtract(Duration(days: 7)))) - .get(); + managers.todoItems.filter( + (f) => f.createdAt.isAfter(DateTime.now().subtract(Duration(days: 7)))); // Filter all items with a title that starts with "Title" - await managers.todoItems.filter((f) => f.title.startsWith('Title')).get(); + managers.todoItems.filter((f) => f.title.startsWith('Title')); } // #enddocregion manager_type_specific_filter // #docregion manager_ordering Future orderWithType() async { // Order all items by their creation date in ascending order - await managers.todoItems.orderBy((o) => o.createdAt.asc()).get(); + managers.todoItems.orderBy((o) => o.createdAt.asc()); // Order all items by their title in ascending order and then by their content in ascending order - await managers.todoItems - .orderBy((o) => o.title.asc() & o.content.asc()) - .get(); + managers.todoItems.orderBy((o) => o.title.asc() & o.content.asc()); } // #enddocregion manager_ordering @@ -136,6 +128,35 @@ extension ManagerExamples on AppDatabase { await managers.todoItems.filter((f) => f.title("Title")).exists(); } // #enddocregion manager_exists + +// #docregion manager_filter_forward_references + Future relationalFilter() async { + // Get all items with a category description of "School" + managers.todoItems + .filter((f) => f.category((f) => f.description("School"))); + + // These can be combined with other filters + // For example, get all items with a title of "Title" or a category description of "School" + await managers.todoItems + .filter( + (f) => f.title("Title") | f.category((f) => f.description("School")), + ) + .exists(); + } +// #enddocregion manager_filter_forward_references + +// #docregion manager_filter_forward_references + Future reverseRelationalFilter() async { + // Get the category that has a todo item with an id of 1 + managers.todoCategory.filter((f) => f.todoItemsRefs((f) => f.id(1))); + + // These can be combined with other filters + // For example, get all categories with a description of "School" or a todo item with an id of 1 + managers.todoCategory.filter( + (f) => f.description("School") | f.todoItemsRefs((f) => f.id(1)), + ); + } +// #enddocregion manager_filter_forward_references } // #docregion manager_filter_extensions @@ -153,14 +174,10 @@ extension After2000Filter on ColumnFilters { Future filterWithExtension(AppDatabase db) async { // Use the custom filters on any column that is of type DateTime - await db.managers.todoItems - .filter((f) => f.createdAt.after2000orBefore1900()) - .get(); + db.managers.todoItems.filter((f) => f.createdAt.after2000orBefore1900()); // Use the custom filter on the `unixepoch` column - await db.managers.todoItems - .filter((f) => f.createdAt.filterOnUnixEpoch(0)) - .get(); + db.managers.todoItems.filter((f) => f.createdAt.filterOnUnixEpoch(0)); } // #enddocregion manager_filter_extensions @@ -172,7 +189,7 @@ extension After2000Ordering on ColumnOrderings { Future orderingWithExtension(AppDatabase db) async { // Use the custom orderings on any column that is of type DateTime - await db.managers.todoItems.orderBy((f) => f.createdAt.byUnixEpoch()).get(); + db.managers.todoItems.orderBy((f) => f.createdAt.byUnixEpoch()); } // #enddocregion manager_ordering_extensions @@ -185,7 +202,7 @@ extension NoContentOrBefore2000FilterX on $$TodoItemsTableFilterComposer { Future customFilter(AppDatabase db) async { // Use the custom filter on the `TodoItems` table - await db.managers.todoItems.filter((f) => f.noContentOrBefore2000()).get(); + db.managers.todoItems.filter((f) => f.noContentOrBefore2000()); } // #enddocregion manager_custom_filter @@ -197,6 +214,20 @@ extension ContentThenCreationDataX on $$TodoItemsTableOrderingComposer { Future customOrdering(AppDatabase db) async { // Use the custom ordering on the `TodoItems` table - await db.managers.todoItems.orderBy((f) => f.contentThenCreatedAt()).get(); + db.managers.todoItems.orderBy((f) => f.contentThenCreatedAt()); } // #enddocregion manager_custom_ordering + +// #docregion reference_name_example +class User extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get name => text()(); + @ReferenceName("users") + IntColumn get group => integer().nullable().references(Group, #id)(); +} + +class Group extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get name => text()(); +} +// #enddocregion reference_name_example \ No newline at end of file diff --git a/docs/pages/docs/Dart API/manager.md b/docs/pages/docs/Dart API/manager.md index 35c4ce98..ee1b888b 100644 --- a/docs/pages/docs/Dart API/manager.md +++ b/docs/pages/docs/Dart API/manager.md @@ -36,6 +36,18 @@ Type specific filters for `int`, `double`, `Int64`, `DateTime` and `String` are ### Filtering across tables +You can filter across references to other tables by using the generated reference filters. You can nest these as deep as you'd like and the manager will take care of adding the aliased joins behind the scenes. + +{% include "blocks/snippet" snippets = snippets name = 'manager_filter_forward_references' %} + +You can also filter across back references. This is useful when you have a one-to-many relationship and want to filter the parent table based on the child table. + +{% include "blocks/snippet" snippets = snippets name = 'manager_filter_back_references' %} + +The code generator will name this filterset using the name of the table that is being referenced. If there are multiple references to the same table, the code generator will print an error and wont generate the filterset. +Use the `@ReferenceName(...)` on the foreign key to specify a custom name for the filterset. + +{% include "blocks/snippet" snippets = snippets name = 'reference_name_example' %} ### Ordering From 3bae9cf5eb020d24b18711c5ec536aaddb697f8f Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 16:09:36 -0400 Subject: [PATCH 70/74] typos --- docs/pages/docs/Dart API/manager.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/pages/docs/Dart API/manager.md b/docs/pages/docs/Dart API/manager.md index ee1b888b..78e8522a 100644 --- a/docs/pages/docs/Dart API/manager.md +++ b/docs/pages/docs/Dart API/manager.md @@ -18,10 +18,14 @@ make common queries much easier to write. The examples on this page use the database from the [setup]({{ '../setup.md' | pageUrl }}) instructions. +When manager generation is enabled (default), drift will generate a manager for each table in the database. +A collection of these managers are accessed by a getter `managers` on the database class. +Each table will have a manager generated for it unless it uses a custom row class. + ## Select The manager simplifies the process of retrieving rows from a table. Use it to read rows from the table or watch -for changes to the table. +for changes. {% include "blocks/snippet" snippets = snippets name = 'manager_select' %} @@ -49,6 +53,7 @@ Use the `@ReferenceName(...)` on the foreign key to specify a custom name for th {% include "blocks/snippet" snippets = snippets name = 'reference_name_example' %} + ### Ordering You can also order the results of a query using the `orderBy` method. The syntax is similar to the `filter` method. From 5a9e78303066100288108f79dc88e2ee3475ccd3 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Wed, 17 Apr 2024 16:10:31 -0400 Subject: [PATCH 71/74] format --- docs/lib/snippets/dart_api/manager.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index 1b586f61..bca3f671 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -230,4 +230,4 @@ class Group extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get name => text()(); } -// #enddocregion reference_name_example \ No newline at end of file +// #enddocregion reference_name_example From 52da52d737bee11d868d7cd17d82e45390e47019 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 21 Apr 2024 17:38:38 -0400 Subject: [PATCH 72/74] add backref to docs --- docs/lib/snippets/dart_api/manager.dart | 74 +++++++++++++++++++------ docs/pages/docs/Dart API/manager.md | 12 +++- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index bca3f671..25c73489 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -1,6 +1,46 @@ import 'package:drift/drift.dart'; -import '../setup/database.dart'; +part 'manager.g.dart'; + +class TodoItems extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get title => text().withLength(min: 6, max: 32)(); + TextColumn get content => text().named('body')(); + IntColumn get category => + integer().nullable().references(TodoCategory, #id)(); + DateTimeColumn get createdAt => dateTime().nullable()(); +} + +class TodoCategory extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get description => text()(); +} + +// #docregion user_group_tables + +class Users extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get name => text()(); + IntColumn get groupId => integer().nullable().references(Groups, #id)(); +} + +class Groups extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get name => text()(); + @ReferenceName("administeredGroups") + IntColumn get admin => integer().nullable().references(Users, #id)(); + @ReferenceName("ownedGroups") + IntColumn get owner => integer().references(Users, #id)(); +} + +// #enddocregion user_group_tables + +@DriftDatabase(tables: [TodoItems, TodoCategory, Groups, Users]) +class AppDatabase extends _$AppDatabase { + AppDatabase(super.e); + @override + int get schemaVersion => 1; +} extension ManagerExamples on AppDatabase { // #docregion manager_create @@ -145,7 +185,7 @@ extension ManagerExamples on AppDatabase { } // #enddocregion manager_filter_forward_references -// #docregion manager_filter_forward_references +// #docregion manager_filter_back_references Future reverseRelationalFilter() async { // Get the category that has a todo item with an id of 1 managers.todoCategory.filter((f) => f.todoItemsRefs((f) => f.id(1))); @@ -156,7 +196,19 @@ extension ManagerExamples on AppDatabase { (f) => f.description("School") | f.todoItemsRefs((f) => f.id(1)), ); } -// #enddocregion manager_filter_forward_references +// #enddocregion manager_filter_back_references + +// #docregion manager_filter_custom_back_references + Future reverseNamedRelationalFilter() async { + // Get all users who are administrators of a group with a name containing "Business" + // or who own a group with an id of 1, 2, 4, or 5 + managers.users.filter( + (f) => + f.administeredGroups((f) => f.name.contains("Business")) | + f.ownedGroups((f) => f.id.isIn([1, 2, 4, 5])), + ); + } +// #enddocregion manager_filter_custom_back_references } // #docregion manager_filter_extensions @@ -216,18 +268,4 @@ Future customOrdering(AppDatabase db) async { // Use the custom ordering on the `TodoItems` table db.managers.todoItems.orderBy((f) => f.contentThenCreatedAt()); } -// #enddocregion manager_custom_ordering - -// #docregion reference_name_example -class User extends Table { - IntColumn get id => integer().autoIncrement()(); - TextColumn get name => text()(); - @ReferenceName("users") - IntColumn get group => integer().nullable().references(Group, #id)(); -} - -class Group extends Table { - IntColumn get id => integer().autoIncrement()(); - TextColumn get name => text()(); -} -// #enddocregion reference_name_example +// #enddocregion manager_custom_ordering \ No newline at end of file diff --git a/docs/pages/docs/Dart API/manager.md b/docs/pages/docs/Dart API/manager.md index 78e8522a..6bf5cfb4 100644 --- a/docs/pages/docs/Dart API/manager.md +++ b/docs/pages/docs/Dart API/manager.md @@ -48,10 +48,16 @@ You can also filter across back references. This is useful when you have a one-t {% include "blocks/snippet" snippets = snippets name = 'manager_filter_back_references' %} -The code generator will name this filterset using the name of the table that is being referenced. If there are multiple references to the same table, the code generator will print an error and wont generate the filterset. -Use the `@ReferenceName(...)` on the foreign key to specify a custom name for the filterset. +The code generator will name this filterset using the name of the table that is being referenced. In the above example, the filterset is named `todoItemsRefs`, because the `TodoItems` table is being referenced. +However, you can also specify a custom name for the filterset using the `@ReferenceName(...)` annotation on the foreign key. This may be necessary if you have multiple references to the same table, take the following example: -{% include "blocks/snippet" snippets = snippets name = 'reference_name_example' %} +{% include "blocks/snippet" snippets = snippets name = 'user_group_tables' %} + +We can now use them in a query like this: + +{% include "blocks/snippet" snippets = snippets name = 'manager_filter_custom_back_references' %} + +In this example, had we not specified a custom name for the reference, the code generator would have named both filtersets `userRefs` for both references to the `User` table. This would have caused a conflict. By specifying a custom name, we can avoid this issue. ### Ordering From 95b92386eecb94c8a39d9a78191418c9dee4ec59 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 21 Apr 2024 18:06:39 -0400 Subject: [PATCH 73/74] format --- docs/lib/snippets/dart_api/manager.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index 25c73489..34267a3f 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -268,4 +268,4 @@ Future customOrdering(AppDatabase db) async { // Use the custom ordering on the `TodoItems` table db.managers.todoItems.orderBy((f) => f.contentThenCreatedAt()); } -// #enddocregion manager_custom_ordering \ No newline at end of file +// #enddocregion manager_custom_ordering From 216f583617ca62156507613311c74911f43b9230 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 21 Apr 2024 18:39:16 -0400 Subject: [PATCH 74/74] fix example --- docs/lib/snippets/dart_api/manager.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index 34267a3f..65d343d3 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -21,7 +21,6 @@ class TodoCategory extends Table { class Users extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get name => text()(); - IntColumn get groupId => integer().nullable().references(Groups, #id)(); } class Groups extends Table {