3.1.0+1
This commit is contained in:
commit
47c37c5e74
1
CHANGELOG.md
Normal file
1
CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
||||
See [Isar Changelog](https://pub.dev/packages/isar/changelog)
|
202
LICENSE
Normal file
202
LICENSE
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
1
README.md
Normal file
1
README.md
Normal file
@ -0,0 +1 @@
|
||||
### Code generator for the [Isar Database](https://github.com/isar/isar) please go there for documentation.
|
9
analysis_options.yaml
Normal file
9
analysis_options.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
include: package:very_good_analysis/analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
cascade_invocations: ignore
|
||||
avoid_positional_boolean_parameters: ignore
|
||||
parameter_assignments: ignore
|
||||
public_member_api_docs: ignore
|
||||
use_string_buffers: ignore
|
8
build.yaml
Normal file
8
build.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
builders:
|
||||
isar_generator:
|
||||
import: "package:isar_generator/isar_generator.dart"
|
||||
builder_factories: ["getIsarGenerator"]
|
||||
build_extensions: { ".dart": ["isar_generator.g.part"] }
|
||||
auto_apply: dependents
|
||||
build_to: cache
|
||||
applies_builders: ["source_gen|combining_builder"]
|
11
lib/isar_generator.dart
Normal file
11
lib/isar_generator.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import 'package:build/build.dart';
|
||||
import 'package:isar_generator/src/collection_generator.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
Builder getIsarGenerator(BuilderOptions options) => SharedPartBuilder(
|
||||
[
|
||||
IsarCollectionGenerator(),
|
||||
IsarEmbeddedGenerator(),
|
||||
],
|
||||
'isar_generator',
|
||||
);
|
119
lib/src/code_gen/by_index_generator.dart
Normal file
119
lib/src/code_gen/by_index_generator.dart
Normal file
@ -0,0 +1,119 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String generateByIndexExtension(ObjectInfo oi) {
|
||||
final uniqueIndexes = oi.indexes.where((e) => e.unique).toList();
|
||||
if (uniqueIndexes.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
var code =
|
||||
'extension ${oi.dartName}ByIndex on IsarCollection<${oi.dartName}> {';
|
||||
for (final index in uniqueIndexes) {
|
||||
code += generateSingleByIndex(oi, index);
|
||||
code += generateAllByIndex(oi, index);
|
||||
if (!index.properties.first.isMultiEntry) {
|
||||
code += generatePutByIndex(oi, index);
|
||||
}
|
||||
}
|
||||
return '''
|
||||
$code
|
||||
}''';
|
||||
}
|
||||
|
||||
extension on ObjectIndex {
|
||||
String get dartName {
|
||||
return properties.map((e) => e.property.dartName.capitalize()).join();
|
||||
}
|
||||
}
|
||||
|
||||
String generateSingleByIndex(ObjectInfo oi, ObjectIndex index) {
|
||||
final params = index.properties
|
||||
.map((i) => '${i.property.dartType} ${i.property.dartName}')
|
||||
.join(',');
|
||||
final paramsList = index.properties.map((i) => i.property.dartName).join(',');
|
||||
return '''
|
||||
Future<${oi.dartName}?> getBy${index.dartName}($params) {
|
||||
return getByIndex(r'${index.name}', [$paramsList]);
|
||||
}
|
||||
|
||||
${oi.dartName}? getBy${index.dartName}Sync($params) {
|
||||
return getByIndexSync(r'${index.name}', [$paramsList]);
|
||||
}
|
||||
|
||||
Future<bool> deleteBy${index.dartName}($params) {
|
||||
return deleteByIndex(r'${index.name}', [$paramsList]);
|
||||
}
|
||||
|
||||
bool deleteBy${index.dartName}Sync($params) {
|
||||
return deleteByIndexSync(r'${index.name}', [$paramsList]);
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateAllByIndex(ObjectInfo oi, ObjectIndex index) {
|
||||
String valsName(ObjectProperty p) => '${p.dartName}Values';
|
||||
|
||||
final props = index.properties;
|
||||
final params = props
|
||||
.map((ip) => 'List<${ip.property.dartType}> ${valsName(ip.property)}')
|
||||
.join(',');
|
||||
String createValues;
|
||||
if (props.length == 1) {
|
||||
final p = props.first.property;
|
||||
createValues = 'final values = ${valsName(p)}.map((e) => [e]).toList();';
|
||||
} else {
|
||||
final lenAssert = props
|
||||
.sublist(1)
|
||||
.map((i) => '${valsName(i.property)}.length == len')
|
||||
.join('&&');
|
||||
createValues = '''
|
||||
final len = ${valsName(props.first.property)}.length;
|
||||
assert($lenAssert, 'All index values must have the same length');
|
||||
final values = <List<dynamic>>[];
|
||||
for (var i = 0; i < len; i++) {
|
||||
values.add([${props.map((ip) => '${valsName(ip.property)}[i]').join(',')}]);
|
||||
}
|
||||
''';
|
||||
}
|
||||
return '''
|
||||
Future<List<${oi.dartName}?>> getAllBy${index.dartName}($params) {
|
||||
$createValues
|
||||
return getAllByIndex(r'${index.name}', values);
|
||||
}
|
||||
|
||||
List<${oi.dartName}?> getAllBy${index.dartName}Sync($params) {
|
||||
$createValues
|
||||
return getAllByIndexSync(r'${index.name}', values);
|
||||
}
|
||||
|
||||
Future<int> deleteAllBy${index.dartName}($params) {
|
||||
$createValues
|
||||
return deleteAllByIndex(r'${index.name}', values);
|
||||
}
|
||||
|
||||
int deleteAllBy${index.dartName}Sync($params) {
|
||||
$createValues
|
||||
return deleteAllByIndexSync(r'${index.name}', values);
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generatePutByIndex(ObjectInfo oi, ObjectIndex index) {
|
||||
return '''
|
||||
Future<Id> putBy${index.dartName}(${oi.dartName} object) {
|
||||
return putByIndex(r'${index.name}', object);
|
||||
}
|
||||
|
||||
Id putBy${index.dartName}Sync(${oi.dartName} object, {bool saveLinks = true}) {
|
||||
return putByIndexSync(r'${index.name}', object, saveLinks: saveLinks);
|
||||
}
|
||||
|
||||
Future<List<Id>> putAllBy${index.dartName}(List<${oi.dartName}> objects) {
|
||||
return putAllByIndex(r'${index.name}', objects);
|
||||
}
|
||||
|
||||
List<Id> putAllBy${index.dartName}Sync(List<${oi.dartName}> objects, {bool saveLinks = true}) {
|
||||
return putAllByIndexSync(r'${index.name}', objects, saveLinks: saveLinks);
|
||||
}
|
||||
''';
|
||||
}
|
112
lib/src/code_gen/collection_schema_generator.dart
Normal file
112
lib/src/code_gen/collection_schema_generator.dart
Normal file
@ -0,0 +1,112 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/isar_type.dart';
|
||||
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String generateSchema(ObjectInfo object) {
|
||||
var code = 'const ${object.dartName.capitalize()}Schema = ';
|
||||
if (!object.isEmbedded) {
|
||||
code += 'CollectionSchema(';
|
||||
} else {
|
||||
code += 'Schema(';
|
||||
}
|
||||
|
||||
final properties = object.objectProperties
|
||||
.mapIndexed(
|
||||
(i, e) => "r'${e.isarName}': ${_generatePropertySchema(object, i)}",
|
||||
)
|
||||
.join(',');
|
||||
|
||||
code += '''
|
||||
name: r'${object.isarName}',
|
||||
id: ${object.id},
|
||||
properties: {$properties},
|
||||
|
||||
estimateSize: ${object.estimateSizeName},
|
||||
serialize: ${object.serializeName},
|
||||
deserialize: ${object.deserializeName},
|
||||
deserializeProp: ${object.deserializePropName},''';
|
||||
|
||||
if (!object.isEmbedded) {
|
||||
final indexes = object.indexes
|
||||
.map((e) => "r'${e.name}': ${_generateIndexSchema(e)}")
|
||||
.join(',');
|
||||
final links = object.links
|
||||
.map((e) => "r'${e.isarName}': ${_generateLinkSchema(object, e)}")
|
||||
.join(',');
|
||||
final embeddedSchemas = object.embeddedDartNames.entries
|
||||
.map((e) => "r'${e.key}': ${e.value.capitalize()}Schema")
|
||||
.join(',');
|
||||
|
||||
code += '''
|
||||
idName: r'${object.idProperty.isarName}',
|
||||
indexes: {$indexes},
|
||||
links: {$links},
|
||||
embeddedSchemas: {$embeddedSchemas},
|
||||
|
||||
getId: ${object.getIdName},
|
||||
getLinks: ${object.getLinksName},
|
||||
attach: ${object.attachName},
|
||||
version: '${Isar.version}',
|
||||
''';
|
||||
}
|
||||
|
||||
return '$code);';
|
||||
}
|
||||
|
||||
String _generatePropertySchema(ObjectInfo object, int index) {
|
||||
final property = object.objectProperties[index];
|
||||
var enumMap = '';
|
||||
if (property.isEnum) {
|
||||
enumMap = 'enumMap: ${property.enumValueMapName(object)},';
|
||||
}
|
||||
var target = '';
|
||||
if (property.targetIsarName != null) {
|
||||
target = "target: r'${property.targetIsarName}',";
|
||||
}
|
||||
return '''
|
||||
PropertySchema(
|
||||
id: $index,
|
||||
name: r'${property.isarName}',
|
||||
type: IsarType.${property.isarType.name},
|
||||
$enumMap
|
||||
$target
|
||||
)
|
||||
''';
|
||||
}
|
||||
|
||||
String _generateIndexSchema(ObjectIndex index) {
|
||||
final properties = index.properties.map((e) {
|
||||
return '''
|
||||
IndexPropertySchema(
|
||||
name: r'${e.property.isarName}',
|
||||
type: IndexType.${e.type.name},
|
||||
caseSensitive: ${e.caseSensitive},
|
||||
)''';
|
||||
}).join(',');
|
||||
|
||||
return '''
|
||||
IndexSchema(
|
||||
id: ${index.id},
|
||||
name: r'${index.name}',
|
||||
unique: ${index.unique},
|
||||
replace: ${index.replace},
|
||||
properties: [$properties],
|
||||
)''';
|
||||
}
|
||||
|
||||
String _generateLinkSchema(ObjectInfo object, ObjectLink link) {
|
||||
var linkName = '';
|
||||
if (link.isBacklink) {
|
||||
linkName = "linkName: r'${link.targetLinkIsarName}',";
|
||||
}
|
||||
return '''
|
||||
LinkSchema(
|
||||
id: ${link.id(object.isarName)},
|
||||
name: r'${link.isarName}',
|
||||
target: r'${link.targetCollectionIsarName}',
|
||||
single: ${link.isSingle},
|
||||
$linkName
|
||||
)''';
|
||||
}
|
27
lib/src/code_gen/query_distinct_by_generator.dart
Normal file
27
lib/src/code_gen/query_distinct_by_generator.dart
Normal file
@ -0,0 +1,27 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/isar_type.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String generateDistinctBy(ObjectInfo oi) {
|
||||
var code = '''
|
||||
extension ${oi.dartName}QueryWhereDistinct on QueryBuilder<${oi.dartName}, ${oi.dartName}, QDistinct> {''';
|
||||
for (final property in oi.objectProperties) {
|
||||
if (property.isarType == IsarType.string) {
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QDistinct>distinctBy${property.dartName.capitalize()}({bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'${property.isarName}', caseSensitive: caseSensitive);
|
||||
});
|
||||
}''';
|
||||
} else if (!property.isarType.containsObject) {
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QDistinct>distinctBy${property.dartName.capitalize()}() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'${property.isarName}');
|
||||
});
|
||||
}''';
|
||||
}
|
||||
}
|
||||
return '$code}';
|
||||
}
|
278
lib/src/code_gen/query_filter_generator.dart
Normal file
278
lib/src/code_gen/query_filter_generator.dart
Normal file
@ -0,0 +1,278 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_filter_length.dart';
|
||||
import 'package:isar_generator/src/isar_type.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
class FilterGenerator {
|
||||
FilterGenerator(this.object) : objName = object.dartName;
|
||||
final ObjectInfo object;
|
||||
final String objName;
|
||||
|
||||
String generate() {
|
||||
var code =
|
||||
'extension ${objName}QueryFilter on QueryBuilder<$objName, $objName, '
|
||||
'QFilterCondition> {';
|
||||
for (final property in object.properties) {
|
||||
if (property.nullable) {
|
||||
code += generateIsNull(property);
|
||||
code += generateIsNotNull(property);
|
||||
}
|
||||
if (property.elementNullable) {
|
||||
code += generateElementIsNull(property);
|
||||
code += generateElementIsNotNull(property);
|
||||
}
|
||||
|
||||
if (!property.isarType.containsObject) {
|
||||
code += generateEqualTo(property);
|
||||
|
||||
if (!property.isarType.containsBool) {
|
||||
code += generateGreaterThan(property);
|
||||
code += generateLessThan(property);
|
||||
code += generateBetween(property);
|
||||
}
|
||||
}
|
||||
|
||||
if (property.isarType.containsString) {
|
||||
code += generateStringStartsWith(property);
|
||||
code += generateStringEndsWith(property);
|
||||
code += generateStringContains(property);
|
||||
code += generateStringMatches(property);
|
||||
code += generateStringIsEmpty(property);
|
||||
code += generateStringIsNotEmpty(property);
|
||||
}
|
||||
|
||||
if (property.isarType.isList) {
|
||||
code += generateListLength(property);
|
||||
}
|
||||
}
|
||||
return '''
|
||||
$code
|
||||
}''';
|
||||
}
|
||||
|
||||
String mPrefix(ObjectProperty p, [bool listElement = true]) {
|
||||
final any = listElement && p.isarType.isList ? 'Element' : '';
|
||||
return 'QueryBuilder<$objName, $objName, QAfterFilterCondition> '
|
||||
'${p.dartName.decapitalize()}$any';
|
||||
}
|
||||
|
||||
String generateEqualTo(ObjectProperty p) {
|
||||
final optional = [
|
||||
if (p.isarType.containsString) 'bool caseSensitive = true',
|
||||
if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',
|
||||
].join(',');
|
||||
return '''
|
||||
${mPrefix(p)}EqualTo(${p.nScalarDartType} value ${optional.isNotBlank ? ', {$optional,}' : ''}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'${p.isarName}',
|
||||
value: value,
|
||||
${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}
|
||||
${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateGreaterThan(ObjectProperty p) {
|
||||
final optional = [
|
||||
'bool include = false',
|
||||
if (p.isarType.containsString) 'bool caseSensitive = true',
|
||||
if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',
|
||||
].join(',');
|
||||
return '''
|
||||
${mPrefix(p)}GreaterThan(${p.nScalarDartType} value, {$optional,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'${p.isarName}',
|
||||
value: value,
|
||||
${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}
|
||||
${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateLessThan(ObjectProperty p) {
|
||||
final optional = [
|
||||
'bool include = false',
|
||||
if (p.isarType.containsString) 'bool caseSensitive = true',
|
||||
if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',
|
||||
].join(',');
|
||||
return '''
|
||||
${mPrefix(p)}LessThan(${p.nScalarDartType} value, {$optional,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'${p.isarName}',
|
||||
value: value,
|
||||
${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}
|
||||
${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateBetween(ObjectProperty p) {
|
||||
final optional = [
|
||||
'bool includeLower = true',
|
||||
'bool includeUpper = true',
|
||||
if (p.isarType.containsString) 'bool caseSensitive = true',
|
||||
if (p.isarType.containsFloat) 'double epsilon = Query.epsilon',
|
||||
].join(',');
|
||||
return '''
|
||||
${mPrefix(p)}Between(${p.nScalarDartType} lower, ${p.nScalarDartType} upper, {$optional,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'${p.isarName}',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
${p.isarType.containsString ? 'caseSensitive: caseSensitive,' : ''}
|
||||
${p.isarType.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateIsNull(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p, false)}IsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'${p.isarName}',
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateElementIsNull(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}IsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.elementIsNull(
|
||||
property: r'${p.isarName}',
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateIsNotNull(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p, false)}IsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query
|
||||
.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'${p.isarName}',
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateElementIsNotNull(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}IsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query
|
||||
.addFilterCondition(const FilterCondition.elementIsNotNull(
|
||||
property: r'${p.isarName}',
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateStringStartsWith(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}StartsWith(String value, {bool caseSensitive = true,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'${p.isarName}',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateStringEndsWith(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}EndsWith(String value, {bool caseSensitive = true,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'${p.isarName}',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateStringContains(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}Contains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'${p.isarName}',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateStringMatches(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}Matches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'${p.isarName}',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateStringIsEmpty(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}IsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'${p.isarName}',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateStringIsNotEmpty(ObjectProperty p) {
|
||||
return '''
|
||||
${mPrefix(p)}IsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
property: r'${p.isarName}',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateListLength(ObjectProperty p) {
|
||||
return generateLength(objName, p.dartName,
|
||||
(lower, includeLower, upper, includeUpper) {
|
||||
return '''
|
||||
QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'${p.isarName}',
|
||||
$lower,
|
||||
$includeLower,
|
||||
$upper,
|
||||
$includeUpper,
|
||||
);
|
||||
})''';
|
||||
});
|
||||
}
|
||||
}
|
50
lib/src/code_gen/query_filter_length.dart
Normal file
50
lib/src/code_gen/query_filter_length.dart
Normal file
@ -0,0 +1,50 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
|
||||
String generateLength(
|
||||
String objectName,
|
||||
String propertyName,
|
||||
String Function(
|
||||
String lower,
|
||||
String includeLower,
|
||||
String upper,
|
||||
String includeUpper,
|
||||
)
|
||||
codeGen,
|
||||
) {
|
||||
return '''
|
||||
QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthEqualTo(int length) {
|
||||
return ${codeGen('length', 'true', 'length', 'true')};
|
||||
}
|
||||
|
||||
QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}IsEmpty() {
|
||||
return ${codeGen('0', 'true', '0', 'true')};
|
||||
}
|
||||
|
||||
QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}IsNotEmpty() {
|
||||
return ${codeGen('0', 'false', '999999', 'true')};
|
||||
}
|
||||
|
||||
QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthLessThan(
|
||||
int length, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return ${codeGen('0', 'true', 'length', 'include')};
|
||||
}
|
||||
|
||||
QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthGreaterThan(
|
||||
int length, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return ${codeGen('length', 'include', '999999', 'true')};
|
||||
}
|
||||
|
||||
QueryBuilder<$objectName, $objectName, QAfterFilterCondition> ${propertyName.decapitalize()}LengthBetween(
|
||||
int lower,
|
||||
int upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return ${codeGen('lower', 'includeLower', 'upper', 'includeUpper')};
|
||||
}
|
||||
''';
|
||||
}
|
38
lib/src/code_gen/query_link_generator.dart
Normal file
38
lib/src/code_gen/query_link_generator.dart
Normal file
@ -0,0 +1,38 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_filter_length.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String generateQueryLinks(ObjectInfo oi) {
|
||||
var code =
|
||||
'extension ${oi.dartName}QueryLinks on QueryBuilder<${oi.dartName}, '
|
||||
'${oi.dartName}, QFilterCondition> {';
|
||||
for (final link in oi.links) {
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterFilterCondition> ${link.dartName.decapitalize()}(FilterQuery<${link.targetCollectionDartName}> q) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.link(q, r'${link.isarName}');
|
||||
});
|
||||
}''';
|
||||
|
||||
if (link.isSingle) {
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterFilterCondition> ${link.dartName.decapitalize()}IsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.linkLength(r'${link.isarName}', 0, true, 0, true);
|
||||
});
|
||||
}''';
|
||||
} else {
|
||||
code += generateLength(oi.dartName, link.dartName,
|
||||
(lower, includeLower, upper, includeUpper) {
|
||||
return '''
|
||||
QueryBuilder.apply(this, (query) {
|
||||
return query.linkLength(r'${link.isarName}', $lower, $includeLower, $upper, $includeUpper);
|
||||
})''';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return '''
|
||||
$code
|
||||
}''';
|
||||
}
|
29
lib/src/code_gen/query_object_generator.dart
Normal file
29
lib/src/code_gen/query_object_generator.dart
Normal file
@ -0,0 +1,29 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/isar_type.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String generateQueryObjects(ObjectInfo oi) {
|
||||
var code =
|
||||
'extension ${oi.dartName}QueryObject on QueryBuilder<${oi.dartName}, '
|
||||
'${oi.dartName}, QFilterCondition> {';
|
||||
for (final property in oi.objectProperties) {
|
||||
if (!property.isarType.containsObject) {
|
||||
continue;
|
||||
}
|
||||
var name = property.dartName.decapitalize();
|
||||
if (property.isarType.isList) {
|
||||
name += 'Element';
|
||||
}
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterFilterCondition> $name(FilterQuery<${property.typeClassName}> q) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.object(q, r'${property.isarName}');
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
return '''
|
||||
$code
|
||||
}''';
|
||||
}
|
25
lib/src/code_gen/query_property_generator.dart
Normal file
25
lib/src/code_gen/query_property_generator.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String generatePropertyQuery(ObjectInfo oi) {
|
||||
var code = '''
|
||||
extension ${oi.dartName}QueryProperty on QueryBuilder<${oi.dartName}, ${oi.dartName}, QQueryProperty> {''';
|
||||
|
||||
// Ids are always non-nullable regardless of their specified nullability
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, int, QQueryOperations>${oi.idProperty.dartName}Property() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'${oi.idProperty.isarName}');
|
||||
});
|
||||
}''';
|
||||
|
||||
for (final property in oi.objectProperties) {
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${property.dartType}, QQueryOperations>${property.dartName}Property() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'${property.isarName}');
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
return '$code}';
|
||||
}
|
55
lib/src/code_gen/query_sort_by_generator.dart
Normal file
55
lib/src/code_gen/query_sort_by_generator.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/isar_type.dart';
|
||||
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String generateSortBy(ObjectInfo oi) {
|
||||
var code = '''
|
||||
extension ${oi.dartName}QuerySortBy on QueryBuilder<${oi.dartName}, ${oi.dartName}, QSortBy> {''';
|
||||
|
||||
for (final property in oi.objectProperties) {
|
||||
if (property.isarType.isList || property.isarType.containsObject) {
|
||||
continue;
|
||||
}
|
||||
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>sortBy${property.dartName.capitalize()}() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'${property.isarName}', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>sortBy${property.dartName.capitalize()}Desc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'${property.isarName}', Sort.desc);
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
code += '''
|
||||
}
|
||||
|
||||
extension ${oi.dartName}QuerySortThenBy on QueryBuilder<${oi.dartName}, ${oi.dartName}, QSortThenBy> {''';
|
||||
|
||||
for (final property in oi.properties) {
|
||||
if (property.isarType.isList || property.isarType.containsObject) {
|
||||
continue;
|
||||
}
|
||||
|
||||
code += '''
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>thenBy${property.dartName.capitalize()}() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'${property.isarName}', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<${oi.dartName}, ${oi.dartName}, QAfterSortBy>thenBy${property.dartName.capitalize()}Desc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'${property.isarName}', Sort.desc);
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
return '$code}';
|
||||
}
|
568
lib/src/code_gen/query_where_generator.dart
Normal file
568
lib/src/code_gen/query_where_generator.dart
Normal file
@ -0,0 +1,568 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
class WhereGenerator {
|
||||
WhereGenerator(this.object)
|
||||
: objName = object.dartName,
|
||||
id = object.idProperty;
|
||||
final ObjectInfo object;
|
||||
final String objName;
|
||||
final ObjectProperty id;
|
||||
final existing = <String>{};
|
||||
|
||||
String generate() {
|
||||
var code = 'extension ${objName}QueryWhereSort on QueryBuilder<$objName, '
|
||||
'$objName, QWhere> {';
|
||||
|
||||
code += generateAnyId();
|
||||
for (final index in object.indexes) {
|
||||
if (index.properties.all((element) => element.type == IndexType.value)) {
|
||||
code += generateAny(index);
|
||||
}
|
||||
}
|
||||
|
||||
code += '''
|
||||
}
|
||||
|
||||
extension ${objName}QueryWhere on QueryBuilder<$objName, $objName, QWhereClause> {
|
||||
''';
|
||||
|
||||
code += generateWhereIdEqualTo();
|
||||
code += generateWhereIdNotEqualTo();
|
||||
code += generateWhereIdGreaterThan();
|
||||
code += generateWhereIdLessThan();
|
||||
code += generateWhereIdBetween();
|
||||
|
||||
for (final index in object.indexes) {
|
||||
for (var n = 0; n < index.properties.length; n++) {
|
||||
final indexProperty = index.properties[n];
|
||||
final property = indexProperty.property;
|
||||
|
||||
if ((property.nullable && !indexProperty.isMultiEntry) ||
|
||||
(property.elementNullable && indexProperty.isMultiEntry)) {
|
||||
code += generateWhereIsNull(index, n + 1);
|
||||
code += generateWhereIsNotNull(index, n + 1);
|
||||
}
|
||||
|
||||
code += generateWhereEqualTo(index, n + 1);
|
||||
code += generateWhereNotEqualTo(index, n + 1);
|
||||
|
||||
if (indexProperty.type == IndexType.value) {
|
||||
if (property.isarType != IsarType.bool &&
|
||||
property.isarType != IsarType.boolList) {
|
||||
code += generateWhereGreaterThan(index, n + 1);
|
||||
code += generateWhereLessThan(index, n + 1);
|
||||
code += generateWhereBetween(index, n + 1);
|
||||
}
|
||||
|
||||
if (property.isarType == IsarType.string ||
|
||||
property.isarType == IsarType.stringList) {
|
||||
code += generateWhereStartsWith(index, n + 1);
|
||||
code += generateStringIsEmpty(index, n + 1);
|
||||
code += generateStringIsNotEmpty(index, n + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '$code}';
|
||||
}
|
||||
|
||||
String getMethodName(ObjectIndex index, int propertyCount, [String? method]) {
|
||||
String propertyName(ObjectIndexProperty p) {
|
||||
var name = p.property.dartName.capitalize();
|
||||
if (p.isMultiEntry) {
|
||||
name += 'Element';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
var name = '';
|
||||
final eqProperties =
|
||||
index.properties.sublist(0, propertyCount - (method != null ? 1 : 0));
|
||||
if (eqProperties.isNotEmpty) {
|
||||
name += eqProperties.map(propertyName).join();
|
||||
name += 'EqualTo';
|
||||
}
|
||||
|
||||
if (method != null) {
|
||||
name += propertyName(index.properties[propertyCount - 1]);
|
||||
name += method;
|
||||
}
|
||||
|
||||
final remainingProperties = propertyCount < index.properties.length
|
||||
? index.properties.sublist(propertyCount)
|
||||
: null;
|
||||
|
||||
if (remainingProperties != null) {
|
||||
name += 'Any';
|
||||
name += remainingProperties.map(propertyName).join();
|
||||
}
|
||||
|
||||
return name.decapitalize();
|
||||
}
|
||||
|
||||
String paramType(ObjectIndexProperty p) {
|
||||
if (p.property.isarType.isList && p.type == IndexType.hash) {
|
||||
return p.property.dartType;
|
||||
} else {
|
||||
return p.property.nScalarDartType;
|
||||
}
|
||||
}
|
||||
|
||||
String paramName(ObjectIndexProperty p) {
|
||||
if (p.property.isarType.isList && p.type != IndexType.hash) {
|
||||
return '${p.property.dartName}Element';
|
||||
} else {
|
||||
return p.property.dartName;
|
||||
}
|
||||
}
|
||||
|
||||
String joinToParams(List<ObjectIndexProperty> properties) {
|
||||
return properties
|
||||
.map((it) => '${paramType(it)} ${paramName(it)}')
|
||||
.join(',');
|
||||
}
|
||||
|
||||
String joinToValues(List<ObjectIndexProperty> properties) {
|
||||
return properties.map((it) {
|
||||
if (it.property.isarType.isList && it.type != IndexType.hash) {
|
||||
return '${it.property.dartName}Element';
|
||||
} else {
|
||||
return paramName(it);
|
||||
}
|
||||
}).join(', ');
|
||||
}
|
||||
|
||||
String generateAnyId() {
|
||||
return '''
|
||||
QueryBuilder<$objName, $objName, QAfterWhere> any${id.dartName.capitalize()}() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateAny(ObjectIndex index) {
|
||||
final name = getMethodName(index, 0);
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
return '''
|
||||
QueryBuilder<$objName, $objName, QAfterWhere> $name() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
const IndexWhereClause.any(indexName: r'${index.name}'),
|
||||
);
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String get mPrefix => 'QueryBuilder<$objName, $objName, QAfterWhereClause>';
|
||||
|
||||
String generateWhereIdEqualTo() {
|
||||
final idName = id.dartName.decapitalize();
|
||||
return '''
|
||||
$mPrefix ${idName}EqualTo(Id $idName) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(
|
||||
lower: $idName,
|
||||
upper: $idName,
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereEqualTo(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount);
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.takeFirst(propertyCount);
|
||||
final values = joinToValues(properties);
|
||||
final params = joinToParams(properties);
|
||||
return '''
|
||||
$mPrefix $name($params ${properties.containsFloat ? ', {double epsilon = Query.epsilon,}' : ''}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.equalTo(
|
||||
indexName: r'${index.name}',
|
||||
value: [$values],
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereIdNotEqualTo() {
|
||||
final idName = id.dartName.decapitalize();
|
||||
return '''
|
||||
$mPrefix ${idName}NotEqualTo(Id $idName) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: $idName, includeUpper: false),
|
||||
).addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: $idName, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: $idName, includeLower: false),
|
||||
).addWhereClause(
|
||||
IdWhereClause.lessThan(upper: $idName, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereNotEqualTo(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'NotEqualTo');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.takeFirst(propertyCount);
|
||||
final params = joinToParams(properties);
|
||||
|
||||
final equalProperties = properties.dropLast(1);
|
||||
final notEqualProperty = properties.last;
|
||||
final equalValues = joinToValues(equalProperties);
|
||||
var notEqualValue = joinToValues([notEqualProperty]);
|
||||
if (equalValues.isNotEmpty) {
|
||||
notEqualValue = ',$notEqualValue';
|
||||
}
|
||||
|
||||
return '''
|
||||
$mPrefix $name($params ${properties.containsFloat ? ', {double epsilon = Query.epsilon,}' : ''}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [$equalValues],
|
||||
upper: [$equalValues $notEqualValue],
|
||||
includeUpper: false,
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
)).addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [$equalValues $notEqualValue],
|
||||
includeLower: false,
|
||||
upper: [$equalValues],
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
} else {
|
||||
return query.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [$equalValues $notEqualValue],
|
||||
includeLower: false,
|
||||
upper: [$equalValues],
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
)).addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [$equalValues],
|
||||
upper: [$equalValues $notEqualValue],
|
||||
includeUpper: false,
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereIdGreaterThan() {
|
||||
final idName = id.dartName.decapitalize();
|
||||
return '''
|
||||
$mPrefix ${idName}GreaterThan(Id $idName, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: $idName, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereGreaterThan(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'GreaterThan');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.takeFirst(propertyCount);
|
||||
final optional = [
|
||||
'bool include = false',
|
||||
if (properties.containsFloat) 'double epsilon = Query.epsilon',
|
||||
].join(',');
|
||||
return '''
|
||||
$mPrefix $name(${joinToParams(properties)}, {$optional,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [${joinToValues(properties)}],
|
||||
includeLower: include,
|
||||
upper: [${joinToValues(properties.dropLast(1))}],
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereIdLessThan() {
|
||||
final idName = id.dartName.decapitalize();
|
||||
return '''
|
||||
$mPrefix ${idName}LessThan(Id $idName, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: $idName, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereLessThan(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'LessThan');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.takeFirst(propertyCount);
|
||||
final optional = [
|
||||
'bool include = false',
|
||||
if (properties.containsFloat) 'double epsilon = Query.epsilon',
|
||||
].join(',');
|
||||
return '''
|
||||
$mPrefix $name(${joinToParams(properties)}, {$optional,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [${joinToValues(properties.dropLast(1))}],
|
||||
upper: [${joinToValues(properties)}],
|
||||
includeUpper: include,
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereIdBetween() {
|
||||
final idName = id.dartName.decapitalize();
|
||||
final lowerName = 'lower${id.dartName.capitalize()}';
|
||||
final upperName = 'upper${id.dartName.capitalize()}';
|
||||
return '''
|
||||
$mPrefix ${idName}Between(Id $lowerName, Id $upperName, {bool includeLower = true, bool includeUpper = true,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(
|
||||
lower: $lowerName,
|
||||
includeLower: includeLower,
|
||||
upper: $upperName,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereBetween(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'Between');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.takeFirst(propertyCount);
|
||||
final equalProperties = properties.dropLast(1);
|
||||
final betweenProperty = properties.last;
|
||||
var params = joinToParams(equalProperties);
|
||||
if (params.isNotEmpty) {
|
||||
params += ',';
|
||||
}
|
||||
|
||||
final betweenType = paramType(betweenProperty);
|
||||
final lowerName = 'lower${paramName(betweenProperty).capitalize()}';
|
||||
final upperName = 'upper${paramName(betweenProperty).capitalize()}';
|
||||
params += '$betweenType $lowerName, $betweenType $upperName';
|
||||
|
||||
var values = joinToValues(equalProperties);
|
||||
if (values.isNotEmpty) {
|
||||
values += ',';
|
||||
}
|
||||
|
||||
final optional = [
|
||||
'bool includeLower = true',
|
||||
'bool includeUpper = true',
|
||||
if (properties.containsFloat) 'double epsilon = Query.epsilon',
|
||||
].join(',');
|
||||
return '''
|
||||
$mPrefix $name($params, {$optional,}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [$values $lowerName],
|
||||
includeLower: includeLower,
|
||||
upper: [$values $upperName],
|
||||
includeUpper: includeUpper,
|
||||
${properties.containsFloat ? 'epsilon: epsilon,' : ''}
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereIsNull(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'IsNull');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.takeFirst(propertyCount - 1);
|
||||
var values = joinToValues(properties);
|
||||
if (values.isNotEmpty) {
|
||||
values += ',';
|
||||
}
|
||||
final params = joinToParams(properties);
|
||||
return '''
|
||||
$mPrefix $name($params) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.equalTo(
|
||||
indexName: r'${index.name}',
|
||||
value: [$values null],
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereIsNotNull(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'IsNotNull');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.takeFirst(propertyCount - 1);
|
||||
var values = joinToValues(properties);
|
||||
if (values.isNotEmpty) {
|
||||
values += ',';
|
||||
}
|
||||
final params = joinToParams(properties);
|
||||
return '''
|
||||
$mPrefix $name($params) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [$values null],
|
||||
includeLower: false,
|
||||
upper: [$values],
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateWhereStartsWith(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'StartsWith');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final equalProperties = index.properties.dropLast(1);
|
||||
var params = joinToParams(equalProperties);
|
||||
if (params.isNotEmpty) {
|
||||
params += ',';
|
||||
}
|
||||
|
||||
final prefixProperty = index.properties.last;
|
||||
final prefixName = '${paramName(prefixProperty).capitalize()}Prefix';
|
||||
params += 'String $prefixName';
|
||||
var values = joinToValues(equalProperties);
|
||||
if (values.isNotEmpty) {
|
||||
values += ',';
|
||||
}
|
||||
|
||||
return '''
|
||||
$mPrefix $name($params) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'${index.name}',
|
||||
lower: [$values $prefixName],
|
||||
upper: [$values '\$$prefixName\\u{FFFFF}'],
|
||||
));
|
||||
});
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateStringIsEmpty(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'IsEmpty');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.dropLast(1);
|
||||
var values = joinToValues(properties);
|
||||
if (values.isNotEmpty) {
|
||||
values += ',';
|
||||
}
|
||||
final params = joinToParams(properties);
|
||||
|
||||
return '''
|
||||
$mPrefix $name($params) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.equalTo(
|
||||
indexName: r'${index.name}',
|
||||
value: [$values ''],
|
||||
));
|
||||
});
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateStringIsNotEmpty(ObjectIndex index, int propertyCount) {
|
||||
final name = getMethodName(index, propertyCount, 'IsNotEmpty');
|
||||
if (!existing.add(name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final properties = index.properties.dropLast(1);
|
||||
var values = joinToValues(properties);
|
||||
if (values.isNotEmpty) {
|
||||
values += ',';
|
||||
}
|
||||
final params = joinToParams(properties);
|
||||
|
||||
return '''
|
||||
$mPrefix $name($params) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query.addWhereClause(IndexWhereClause.lessThan(
|
||||
indexName: r'${index.name}',
|
||||
upper: [''],
|
||||
)).addWhereClause(IndexWhereClause.greaterThan(
|
||||
indexName: r'${index.name}',
|
||||
lower: [''],
|
||||
));
|
||||
} else {
|
||||
return query.addWhereClause(IndexWhereClause.greaterThan(
|
||||
indexName: r'${index.name}',
|
||||
lower: [''],
|
||||
)).addWhereClause(IndexWhereClause.lessThan(
|
||||
indexName: r'${index.name}',
|
||||
upper: [''],
|
||||
));
|
||||
}
|
||||
});
|
||||
}''';
|
||||
}
|
||||
}
|
||||
|
||||
extension on List<ObjectIndexProperty> {
|
||||
bool get containsFloat =>
|
||||
last.isarType == IsarType.float || last.isarType == IsarType.floatList;
|
||||
}
|
476
lib/src/code_gen/type_adapter_generator.dart
Normal file
476
lib/src/code_gen/type_adapter_generator.dart
Normal file
@ -0,0 +1,476 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
String _prepareSerialize(
|
||||
bool nullable,
|
||||
String value,
|
||||
String Function(String) size,
|
||||
) {
|
||||
var code = '';
|
||||
if (nullable) {
|
||||
code += '''
|
||||
{
|
||||
final value = $value;
|
||||
if (value != null) {''';
|
||||
value = 'value';
|
||||
}
|
||||
code += 'bytesCount += ${size(value)};';
|
||||
if (nullable) {
|
||||
code += '}}';
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
String _prepareSerializeList(
|
||||
bool nullable,
|
||||
bool elementNullable,
|
||||
String value,
|
||||
String size, [
|
||||
String? prepare,
|
||||
]) {
|
||||
var code = '';
|
||||
if (nullable) {
|
||||
code += '''
|
||||
{
|
||||
final list = $value;
|
||||
if (list != null) {''';
|
||||
value = 'list';
|
||||
}
|
||||
code += '''
|
||||
bytesCount += 3 + $value.length * 3;
|
||||
{
|
||||
${prepare ?? ''}
|
||||
for (var i = 0; i < $value.length; i++) {
|
||||
final value = $value[i];''';
|
||||
if (elementNullable) {
|
||||
code += 'if (value != null) {';
|
||||
}
|
||||
code += 'bytesCount += $size;';
|
||||
if (elementNullable) {
|
||||
code += '}';
|
||||
}
|
||||
code += '}}';
|
||||
if (nullable) {
|
||||
code += '}}';
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
String generateEstimateSerialize(ObjectInfo object) {
|
||||
var code = '''
|
||||
int ${object.estimateSizeName}(
|
||||
${object.dartName} object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;''';
|
||||
|
||||
for (final property in object.properties) {
|
||||
final value = 'object.${property.dartName}';
|
||||
|
||||
switch (property.isarType) {
|
||||
case IsarType.string:
|
||||
final enumValue = property.isEnum ? '.${property.enumProperty}' : '';
|
||||
code += _prepareSerialize(
|
||||
property.nullable,
|
||||
value,
|
||||
(value) => '3 + $value$enumValue.length * 3',
|
||||
);
|
||||
break;
|
||||
|
||||
case IsarType.stringList:
|
||||
final enumValue = property.isEnum ? '.${property.enumProperty}' : '';
|
||||
code += _prepareSerializeList(
|
||||
property.nullable,
|
||||
property.elementNullable,
|
||||
value,
|
||||
'value$enumValue.length * 3',
|
||||
);
|
||||
break;
|
||||
|
||||
case IsarType.object:
|
||||
code += _prepareSerialize(
|
||||
property.nullable,
|
||||
value,
|
||||
(value) {
|
||||
return '3 + ${property.targetSchema}.estimateSize($value, '
|
||||
'allOffsets[${property.scalarDartType}]!, allOffsets)';
|
||||
},
|
||||
);
|
||||
break;
|
||||
|
||||
case IsarType.objectList:
|
||||
code += _prepareSerializeList(
|
||||
property.nullable,
|
||||
property.elementNullable,
|
||||
value,
|
||||
'${property.targetSchema}.estimateSize(value, offsets, allOffsets)',
|
||||
'final offsets = allOffsets[${property.scalarDartType}]!;',
|
||||
);
|
||||
break;
|
||||
|
||||
case IsarType.byteList:
|
||||
case IsarType.boolList:
|
||||
code += _prepareSerialize(
|
||||
property.nullable,
|
||||
value,
|
||||
(value) => '3 + $value.length',
|
||||
);
|
||||
break;
|
||||
case IsarType.intList:
|
||||
case IsarType.floatList:
|
||||
code += _prepareSerialize(
|
||||
property.nullable,
|
||||
value,
|
||||
(value) => '3 + $value.length * 4',
|
||||
);
|
||||
break;
|
||||
case IsarType.longList:
|
||||
case IsarType.doubleList:
|
||||
case IsarType.dateTimeList:
|
||||
code += _prepareSerialize(
|
||||
property.nullable,
|
||||
value,
|
||||
(value) => '3 + $value.length * 8',
|
||||
);
|
||||
break;
|
||||
|
||||
// ignore: no_default_cases
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return '''
|
||||
$code
|
||||
return bytesCount;
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateSerialize(ObjectInfo object) {
|
||||
var code = '''
|
||||
void ${object.serializeName}(
|
||||
${object.dartName} object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {''';
|
||||
|
||||
for (var i = 0; i < object.objectProperties.length; i++) {
|
||||
final property = object.objectProperties[i];
|
||||
var value = 'object.${property.dartName}';
|
||||
if (property.isEnum) {
|
||||
final nOp = property.nullable ? '?' : '';
|
||||
final elNOp = property.elementNullable ? '?' : '';
|
||||
value = property.isarType.isList
|
||||
? '$value$nOp.map((e) => e$elNOp.${property.enumProperty}).toList()'
|
||||
: '$value$nOp.${property.enumProperty}';
|
||||
}
|
||||
|
||||
switch (property.isarType) {
|
||||
case IsarType.bool:
|
||||
code += 'writer.writeBool(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.byte:
|
||||
code += 'writer.writeByte(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.int:
|
||||
code += 'writer.writeInt(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.float:
|
||||
code += 'writer.writeFloat(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.long:
|
||||
code += 'writer.writeLong(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.double:
|
||||
code += 'writer.writeDouble(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.dateTime:
|
||||
code += 'writer.writeDateTime(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.string:
|
||||
code += 'writer.writeString(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.object:
|
||||
code += '''
|
||||
writer.writeObject<${property.typeClassName}>(
|
||||
offsets[$i],
|
||||
allOffsets,
|
||||
${property.targetSchema}.serialize,
|
||||
$value,
|
||||
);''';
|
||||
break;
|
||||
case IsarType.byteList:
|
||||
code += 'writer.writeByteList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.boolList:
|
||||
code += 'writer.writeBoolList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.intList:
|
||||
code += 'writer.writeIntList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.longList:
|
||||
code += 'writer.writeLongList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.floatList:
|
||||
code += 'writer.writeFloatList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.doubleList:
|
||||
code += 'writer.writeDoubleList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.dateTimeList:
|
||||
code += 'writer.writeDateTimeList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.stringList:
|
||||
code += 'writer.writeStringList(offsets[$i], $value);';
|
||||
break;
|
||||
case IsarType.objectList:
|
||||
code += '''
|
||||
writer.writeObjectList<${property.typeClassName}>(
|
||||
offsets[$i],
|
||||
allOffsets,
|
||||
${property.targetSchema}.serialize,
|
||||
$value,
|
||||
);''';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return '$code}';
|
||||
}
|
||||
|
||||
String generateDeserialize(ObjectInfo object) {
|
||||
var code = '''
|
||||
${object.dartName} ${object.deserializeName}(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = ${object.dartName}(''';
|
||||
|
||||
final propertiesByMode =
|
||||
object.properties.groupBy((ObjectProperty p) => p.deserialize);
|
||||
final positional = propertiesByMode[PropertyDeser.positionalParam] ?? [];
|
||||
final sortedPositional =
|
||||
positional.sortedBy((ObjectProperty p) => p.constructorPosition!);
|
||||
for (final p in sortedPositional) {
|
||||
final index = object.objectProperties.indexOf(p);
|
||||
final deser = _deserializeProperty(object, p, 'offsets[$index]');
|
||||
code += '$deser,';
|
||||
}
|
||||
|
||||
final named = propertiesByMode[PropertyDeser.namedParam] ?? [];
|
||||
for (final p in named) {
|
||||
final index = object.objectProperties.indexOf(p);
|
||||
final deser = _deserializeProperty(object, p, 'offsets[$index]');
|
||||
code += '${p.dartName}: $deser,';
|
||||
}
|
||||
|
||||
code += ');';
|
||||
|
||||
final assign = propertiesByMode[PropertyDeser.assign] ?? [];
|
||||
for (final p in assign) {
|
||||
final index = object.objectProperties.indexOf(p);
|
||||
final deser = _deserializeProperty(object, p, 'offsets[$index]');
|
||||
code += 'object.${p.dartName} = $deser;';
|
||||
}
|
||||
|
||||
return '''
|
||||
$code
|
||||
return object;
|
||||
}''';
|
||||
}
|
||||
|
||||
String generateDeserializeProp(ObjectInfo object) {
|
||||
var code = '''
|
||||
P ${object.deserializePropName}<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type,
|
||||
List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {''';
|
||||
|
||||
for (var i = 0; i < object.objectProperties.length; i++) {
|
||||
final property = object.objectProperties[i];
|
||||
final deser = _deserializeProperty(object, property, 'offset');
|
||||
code += 'case $i: return ($deser) as P;';
|
||||
}
|
||||
|
||||
return '''
|
||||
$code
|
||||
default:
|
||||
throw IsarError('Unknown property with id \$propertyId');
|
||||
}
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String _deserializeProperty(
|
||||
ObjectInfo object,
|
||||
ObjectProperty property,
|
||||
String propertyOffset,
|
||||
) {
|
||||
if (property.isId) {
|
||||
return 'id';
|
||||
}
|
||||
|
||||
final deser = _deserialize(property, propertyOffset);
|
||||
|
||||
var defaultValue = '';
|
||||
if (!property.nullable) {
|
||||
if (property.userDefaultValue != null) {
|
||||
defaultValue = '?? ${property.userDefaultValue}';
|
||||
} else if (property.isarType == IsarType.object) {
|
||||
defaultValue = '?? ${property.typeClassName}()';
|
||||
} else if (property.isarType.isList) {
|
||||
defaultValue = '?? []';
|
||||
} else if (property.isEnum) {
|
||||
defaultValue = '?? ${property.defaultEnumElement}';
|
||||
}
|
||||
}
|
||||
|
||||
if (property.isEnum) {
|
||||
if (property.isarType.isList) {
|
||||
final elDefault =
|
||||
!property.elementNullable ? '?? ${property.defaultEnumElement}' : '';
|
||||
return '$deser?.map((e) => ${property.valueEnumMapName(object)}[e] '
|
||||
'$elDefault).toList() $defaultValue';
|
||||
} else {
|
||||
return '${property.valueEnumMapName(object)}[$deser] $defaultValue';
|
||||
}
|
||||
} else {
|
||||
return '$deser $defaultValue';
|
||||
}
|
||||
}
|
||||
|
||||
String _deserialize(ObjectProperty property, String propertyOffset) {
|
||||
final orNull =
|
||||
property.nullable || property.userDefaultValue != null || property.isEnum
|
||||
? 'OrNull'
|
||||
: '';
|
||||
final orElNull = property.elementNullable ? 'OrNull' : '';
|
||||
|
||||
switch (property.isarType) {
|
||||
case IsarType.bool:
|
||||
return 'reader.readBool$orNull($propertyOffset)';
|
||||
case IsarType.byte:
|
||||
return 'reader.readByte$orNull($propertyOffset)';
|
||||
case IsarType.int:
|
||||
return 'reader.readInt$orNull($propertyOffset)';
|
||||
case IsarType.float:
|
||||
return 'reader.readFloat$orNull($propertyOffset)';
|
||||
case IsarType.long:
|
||||
return 'reader.readLong$orNull($propertyOffset)';
|
||||
case IsarType.double:
|
||||
return 'reader.readDouble$orNull($propertyOffset)';
|
||||
case IsarType.dateTime:
|
||||
return 'reader.readDateTime$orNull($propertyOffset)';
|
||||
case IsarType.string:
|
||||
return 'reader.readString$orNull($propertyOffset)';
|
||||
case IsarType.object:
|
||||
return '''
|
||||
reader.readObjectOrNull<${property.typeClassName}>(
|
||||
$propertyOffset,
|
||||
${property.targetSchema}.deserialize,
|
||||
allOffsets,
|
||||
)''';
|
||||
case IsarType.boolList:
|
||||
return 'reader.readBool${orElNull}List($propertyOffset)';
|
||||
case IsarType.byteList:
|
||||
return 'reader.readByteList($propertyOffset)';
|
||||
case IsarType.intList:
|
||||
return 'reader.readInt${orElNull}List($propertyOffset)';
|
||||
case IsarType.floatList:
|
||||
return 'reader.readFloat${orElNull}List($propertyOffset)';
|
||||
case IsarType.longList:
|
||||
return 'reader.readLong${orElNull}List($propertyOffset)';
|
||||
case IsarType.doubleList:
|
||||
return 'reader.readDouble${orElNull}List($propertyOffset)';
|
||||
case IsarType.dateTimeList:
|
||||
return 'reader.readDateTime${orElNull}List($propertyOffset)';
|
||||
case IsarType.stringList:
|
||||
return 'reader.readString${orElNull}List($propertyOffset)';
|
||||
case IsarType.objectList:
|
||||
return '''
|
||||
reader.readObject${orElNull}List<${property.typeClassName}>(
|
||||
$propertyOffset,
|
||||
${property.targetSchema}.deserialize,
|
||||
allOffsets,
|
||||
${!property.elementNullable ? '${property.typeClassName}(),' : ''}
|
||||
)''';
|
||||
}
|
||||
}
|
||||
|
||||
String generateGetId(ObjectInfo object) {
|
||||
final defaultVal = object.idProperty.nullable ? '?? Isar.autoIncrement' : '';
|
||||
return '''
|
||||
Id ${object.getIdName}(${object.dartName} object) {
|
||||
return object.${object.idProperty.dartName} $defaultVal;
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateGetLinks(ObjectInfo object) {
|
||||
return '''
|
||||
List<IsarLinkBase<dynamic>> ${object.getLinksName}(${object.dartName} object) {
|
||||
return [${object.links.map((e) => 'object.${e.dartName}').join(',')}];
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String generateAttach(ObjectInfo object) {
|
||||
var code = '''
|
||||
void ${object.attachName}(IsarCollection<dynamic> col, Id id, ${object.dartName} object) {''';
|
||||
|
||||
if (object.idProperty.assignable) {
|
||||
code += 'object.${object.idProperty.dartName} = id;';
|
||||
}
|
||||
|
||||
for (final link in object.links) {
|
||||
// ignore: leading_newlines_in_multiline_strings
|
||||
code += '''object.${link.dartName}.attach(
|
||||
col,
|
||||
col.isar.collection<${link.targetCollectionDartName}>(),
|
||||
r'${link.isarName}',
|
||||
id
|
||||
);''';
|
||||
}
|
||||
return '$code}';
|
||||
}
|
||||
|
||||
String generateEnumMaps(ObjectInfo object) {
|
||||
var code = '';
|
||||
for (final property in object.properties) {
|
||||
final enumName = property.typeClassName;
|
||||
if (property.isEnum) {
|
||||
code += 'const ${property.enumValueMapName(object)} = {';
|
||||
for (final enumElementName in property.enumMap!.keys) {
|
||||
final value = property.enumMap![enumElementName];
|
||||
if (value is String) {
|
||||
code += "r'$enumElementName': r'$value',";
|
||||
} else {
|
||||
code += "'$enumElementName': $value,";
|
||||
}
|
||||
}
|
||||
code += '};';
|
||||
|
||||
code += 'const ${property.valueEnumMapName(object)} = {';
|
||||
for (final enumElementName in property.enumMap!.keys) {
|
||||
final value = property.enumMap![enumElementName];
|
||||
if (value is String) {
|
||||
code += "r'$value': $enumName.$enumElementName,";
|
||||
} else {
|
||||
code += '$value: $enumName.$enumElementName,';
|
||||
}
|
||||
}
|
||||
code += '};';
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
105
lib/src/collection_generator.dart
Normal file
105
lib/src/collection_generator.dart
Normal file
@ -0,0 +1,105 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/code_gen/by_index_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/collection_schema_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_distinct_by_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_filter_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_link_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_object_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_property_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_sort_by_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/query_where_generator.dart';
|
||||
import 'package:isar_generator/src/code_gen/type_adapter_generator.dart';
|
||||
import 'package:isar_generator/src/isar_analyzer.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
const ignoreLints = [
|
||||
'duplicate_ignore',
|
||||
'non_constant_identifier_names',
|
||||
'constant_identifier_names',
|
||||
'invalid_use_of_protected_member',
|
||||
'unnecessary_cast',
|
||||
'prefer_const_constructors',
|
||||
'lines_longer_than_80_chars',
|
||||
'require_trailing_commas',
|
||||
'inference_failure_on_function_invocation',
|
||||
'unnecessary_parenthesis',
|
||||
'unnecessary_raw_strings',
|
||||
'unnecessary_null_checks',
|
||||
'join_return_with_assignment',
|
||||
'prefer_final_locals',
|
||||
'avoid_js_rounded_ints',
|
||||
'avoid_positional_boolean_parameters',
|
||||
'always_specify_types',
|
||||
];
|
||||
|
||||
class IsarCollectionGenerator extends GeneratorForAnnotation<Collection> {
|
||||
@override
|
||||
Future<String> generateForAnnotatedElement(
|
||||
Element element,
|
||||
ConstantReader annotation,
|
||||
BuildStep buildStep,
|
||||
) async {
|
||||
final object = IsarAnalyzer().analyzeCollection(element);
|
||||
return '''
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: ${ignoreLints.join(', ')}
|
||||
|
||||
extension Get${object.dartName}Collection on Isar {
|
||||
IsarCollection<${object.dartName}> get ${object.accessor} => this.collection();
|
||||
}
|
||||
|
||||
${generateSchema(object)}
|
||||
|
||||
${generateEstimateSerialize(object)}
|
||||
${generateSerialize(object)}
|
||||
${generateDeserialize(object)}
|
||||
${generateDeserializeProp(object)}
|
||||
|
||||
${generateEnumMaps(object)}
|
||||
|
||||
${generateGetId(object)}
|
||||
${generateGetLinks(object)}
|
||||
${generateAttach(object)}
|
||||
|
||||
${generateByIndexExtension(object)}
|
||||
${WhereGenerator(object).generate()}
|
||||
${FilterGenerator(object).generate()}
|
||||
${generateQueryObjects(object)}
|
||||
${generateQueryLinks(object)}
|
||||
${generateSortBy(object)}
|
||||
${generateDistinctBy(object)}
|
||||
${generatePropertyQuery(object)}
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
||||
class IsarEmbeddedGenerator extends GeneratorForAnnotation<Embedded> {
|
||||
@override
|
||||
Future<String> generateForAnnotatedElement(
|
||||
Element element,
|
||||
ConstantReader annotation,
|
||||
BuildStep buildStep,
|
||||
) async {
|
||||
final object = IsarAnalyzer().analyzeEmbedded(element);
|
||||
return '''
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: ${ignoreLints.join(', ')}
|
||||
|
||||
${generateSchema(object)}
|
||||
|
||||
${generateEstimateSerialize(object)}
|
||||
${generateSerialize(object)}
|
||||
${generateDeserialize(object)}
|
||||
${generateDeserializeProp(object)}
|
||||
|
||||
${generateEnumMaps(object)}
|
||||
|
||||
${FilterGenerator(object).generate()}
|
||||
${generateQueryObjects(object)}
|
||||
''';
|
||||
}
|
||||
}
|
184
lib/src/helper.dart
Normal file
184
lib/src/helper.dart
Normal file
@ -0,0 +1,184 @@
|
||||
import 'package:analyzer/dart/constant/value.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
const TypeChecker _collectionChecker = TypeChecker.fromRuntime(Collection);
|
||||
const TypeChecker _enumeratedChecker = TypeChecker.fromRuntime(Enumerated);
|
||||
const TypeChecker _embeddedChecker = TypeChecker.fromRuntime(Embedded);
|
||||
const TypeChecker _ignoreChecker = TypeChecker.fromRuntime(Ignore);
|
||||
const TypeChecker _nameChecker = TypeChecker.fromRuntime(Name);
|
||||
const TypeChecker _indexChecker = TypeChecker.fromRuntime(Index);
|
||||
const TypeChecker _backlinkChecker = TypeChecker.fromRuntime(Backlink);
|
||||
|
||||
extension ClassElementX on ClassElement {
|
||||
bool get hasZeroArgsConstructor {
|
||||
return constructors.any(
|
||||
(ConstructorElement c) =>
|
||||
c.isPublic &&
|
||||
!c.parameters.any((ParameterElement p) => !p.isOptional),
|
||||
);
|
||||
}
|
||||
|
||||
List<PropertyInducingElement> get allAccessors {
|
||||
final ignoreFields =
|
||||
collectionAnnotation?.ignore ?? embeddedAnnotation!.ignore;
|
||||
return [
|
||||
...accessors.mapNotNull((e) => e.variable),
|
||||
if (collectionAnnotation?.inheritance ?? embeddedAnnotation!.inheritance)
|
||||
for (InterfaceType supertype in allSupertypes) ...[
|
||||
if (!supertype.isDartCoreObject)
|
||||
...supertype.accessors.mapNotNull((e) => e.variable)
|
||||
]
|
||||
]
|
||||
.where(
|
||||
(PropertyInducingElement e) =>
|
||||
e.isPublic &&
|
||||
!e.isStatic &&
|
||||
!_ignoreChecker.hasAnnotationOf(e.nonSynthetic) &&
|
||||
!ignoreFields.contains(e.name),
|
||||
)
|
||||
.distinctBy((e) => e.name)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<String> get enumConsts {
|
||||
return fields.where((e) => e.isEnumConstant).map((e) => e.name).toList();
|
||||
}
|
||||
}
|
||||
|
||||
extension PropertyElementX on PropertyInducingElement {
|
||||
bool get isLink => type.element2!.name == 'IsarLink';
|
||||
|
||||
bool get isLinks => type.element2!.name == 'IsarLinks';
|
||||
|
||||
Enumerated? get enumeratedAnnotation {
|
||||
final ann = _enumeratedChecker.firstAnnotationOfExact(nonSynthetic);
|
||||
if (ann == null) {
|
||||
return null;
|
||||
}
|
||||
final typeIndex = ann.getField('type')!.getField('index')!.toIntValue()!;
|
||||
return Enumerated(
|
||||
EnumType.values[typeIndex],
|
||||
ann.getField('property')?.toStringValue(),
|
||||
);
|
||||
}
|
||||
|
||||
Backlink? get backlinkAnnotation {
|
||||
final ann = _backlinkChecker.firstAnnotationOfExact(nonSynthetic);
|
||||
if (ann == null) {
|
||||
return null;
|
||||
}
|
||||
return Backlink(to: ann.getField('to')!.toStringValue()!);
|
||||
}
|
||||
|
||||
List<Index> get indexAnnotations {
|
||||
return _indexChecker.annotationsOfExact(nonSynthetic).map((DartObject ann) {
|
||||
final rawComposite = ann.getField('composite')!.toListValue();
|
||||
final composite = <CompositeIndex>[];
|
||||
if (rawComposite != null) {
|
||||
for (final c in rawComposite) {
|
||||
final indexTypeField = c.getField('type')!;
|
||||
IndexType? indexType;
|
||||
if (!indexTypeField.isNull) {
|
||||
final indexTypeIndex =
|
||||
indexTypeField.getField('index')!.toIntValue()!;
|
||||
indexType = IndexType.values[indexTypeIndex];
|
||||
}
|
||||
composite.add(
|
||||
CompositeIndex(
|
||||
c.getField('property')!.toStringValue()!,
|
||||
type: indexType,
|
||||
caseSensitive: c.getField('caseSensitive')!.toBoolValue(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
final indexTypeField = ann.getField('type')!;
|
||||
IndexType? indexType;
|
||||
if (!indexTypeField.isNull) {
|
||||
final indexTypeIndex = indexTypeField.getField('index')!.toIntValue()!;
|
||||
indexType = IndexType.values[indexTypeIndex];
|
||||
}
|
||||
return Index(
|
||||
name: ann.getField('name')!.toStringValue(),
|
||||
composite: composite,
|
||||
unique: ann.getField('unique')!.toBoolValue()!,
|
||||
replace: ann.getField('replace')!.toBoolValue()!,
|
||||
type: indexType,
|
||||
caseSensitive: ann.getField('caseSensitive')!.toBoolValue(),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
extension ElementX on Element {
|
||||
String get isarName {
|
||||
final ann = _nameChecker.firstAnnotationOfExact(nonSynthetic);
|
||||
late String name;
|
||||
if (ann == null) {
|
||||
name = displayName;
|
||||
} else {
|
||||
name = ann.getField('name')!.toStringValue()!;
|
||||
}
|
||||
checkIsarName(name, this);
|
||||
return name;
|
||||
}
|
||||
|
||||
Collection? get collectionAnnotation {
|
||||
final ann = _collectionChecker.firstAnnotationOfExact(nonSynthetic);
|
||||
if (ann == null) {
|
||||
return null;
|
||||
}
|
||||
return Collection(
|
||||
inheritance: ann.getField('inheritance')!.toBoolValue()!,
|
||||
accessor: ann.getField('accessor')!.toStringValue(),
|
||||
ignore: ann
|
||||
.getField('ignore')!
|
||||
.toSetValue()!
|
||||
.map((e) => e.toStringValue()!)
|
||||
.toSet(),
|
||||
);
|
||||
}
|
||||
|
||||
String get collectionAccessor {
|
||||
var accessor = collectionAnnotation?.accessor;
|
||||
if (accessor != null) {
|
||||
return accessor;
|
||||
}
|
||||
|
||||
accessor = displayName.decapitalize();
|
||||
if (!accessor.endsWith('s')) {
|
||||
accessor += 's';
|
||||
}
|
||||
|
||||
return accessor;
|
||||
}
|
||||
|
||||
Embedded? get embeddedAnnotation {
|
||||
final ann = _embeddedChecker.firstAnnotationOfExact(nonSynthetic);
|
||||
if (ann == null) {
|
||||
return null;
|
||||
}
|
||||
return Embedded(
|
||||
inheritance: ann.getField('inheritance')!.toBoolValue()!,
|
||||
ignore: ann
|
||||
.getField('ignore')!
|
||||
.toSetValue()!
|
||||
.map((e) => e.toStringValue()!)
|
||||
.toSet(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void checkIsarName(String name, Element element) {
|
||||
if (name.isBlank || name.startsWith('_')) {
|
||||
err('Names must not be blank or start with "_".', element);
|
||||
}
|
||||
}
|
||||
|
||||
Never err(String msg, [Element? element]) {
|
||||
throw InvalidGenerationSourceError(msg, element: element);
|
||||
}
|
502
lib/src/isar_analyzer.dart
Normal file
502
lib/src/isar_analyzer.dart
Normal file
@ -0,0 +1,502 @@
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/nullability_suffix.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
import 'package:isar_generator/src/helper.dart';
|
||||
import 'package:isar_generator/src/isar_type.dart';
|
||||
import 'package:isar_generator/src/object_info.dart';
|
||||
|
||||
class IsarAnalyzer {
|
||||
ObjectInfo analyzeCollection(Element element) {
|
||||
final constructor = _checkValidClass(element);
|
||||
final modelClass = element as ClassElement;
|
||||
|
||||
final properties = <ObjectProperty>[];
|
||||
final links = <ObjectLink>[];
|
||||
for (final propertyElement in modelClass.allAccessors) {
|
||||
if (propertyElement.isLink || propertyElement.isLinks) {
|
||||
final link = analyzeObjectLink(propertyElement);
|
||||
links.add(link);
|
||||
} else {
|
||||
final property = analyzeObjectProperty(propertyElement, constructor);
|
||||
properties.add(property);
|
||||
}
|
||||
}
|
||||
_checkValidPropertiesConstructor(properties, constructor);
|
||||
if (links.map((e) => e.isarName).distinct().length != links.length) {
|
||||
err('Two or more links have the same name.', modelClass);
|
||||
}
|
||||
|
||||
final indexes = <ObjectIndex>[];
|
||||
for (final propertyElement in modelClass.allAccessors) {
|
||||
indexes.addAll(analyzeObjectIndex(properties, propertyElement));
|
||||
}
|
||||
if (indexes.map((e) => e.name).distinct().length != indexes.length) {
|
||||
err('Two or more indexes have the same name.', modelClass);
|
||||
}
|
||||
|
||||
final idProperties = properties.where((it) => it.isId);
|
||||
if (idProperties.isEmpty) {
|
||||
err(
|
||||
'No id property defined. Use the "Id" type for your id property.',
|
||||
modelClass,
|
||||
);
|
||||
} else if (idProperties.length > 1) {
|
||||
err('Two or more properties with type "Id" defined.', modelClass);
|
||||
}
|
||||
|
||||
return ObjectInfo(
|
||||
dartName: modelClass.displayName,
|
||||
isarName: modelClass.isarName,
|
||||
accessor: modelClass.collectionAccessor,
|
||||
properties: properties,
|
||||
embeddedDartNames: _getEmbeddedDartNames(element),
|
||||
indexes: indexes,
|
||||
links: links,
|
||||
);
|
||||
}
|
||||
|
||||
ObjectInfo analyzeEmbedded(Element element) {
|
||||
final constructor = _checkValidClass(element);
|
||||
final modelClass = element as ClassElement;
|
||||
|
||||
if (constructor.parameters.any((e) => e.isRequired)) {
|
||||
err(
|
||||
'Constructors of embedded objects must not have required parameters.',
|
||||
constructor,
|
||||
);
|
||||
}
|
||||
|
||||
final properties = <ObjectProperty>[];
|
||||
for (final propertyElement in modelClass.allAccessors) {
|
||||
if (propertyElement.isLink || propertyElement.isLinks) {
|
||||
err('Embedded objects must not contain links', propertyElement);
|
||||
} else {
|
||||
final property = analyzeObjectProperty(propertyElement, constructor);
|
||||
properties.add(property);
|
||||
}
|
||||
}
|
||||
_checkValidPropertiesConstructor(properties, constructor);
|
||||
|
||||
final hasIndex = modelClass.allAccessors.any(
|
||||
(it) => it.indexAnnotations.isNotEmpty,
|
||||
);
|
||||
if (hasIndex) {
|
||||
err('Embedded objects must not have indexes.', modelClass);
|
||||
}
|
||||
|
||||
final hasIdProperty = properties.any((it) => it.isId);
|
||||
if (hasIdProperty) {
|
||||
err('Embedded objects must not define an id.', modelClass);
|
||||
}
|
||||
|
||||
return ObjectInfo(
|
||||
dartName: modelClass.displayName,
|
||||
isarName: modelClass.isarName,
|
||||
properties: properties,
|
||||
);
|
||||
}
|
||||
|
||||
ConstructorElement _checkValidClass(Element modelClass) {
|
||||
if (modelClass is! ClassElement ||
|
||||
modelClass is EnumElement ||
|
||||
modelClass is MixinElement) {
|
||||
err(
|
||||
'Only classes may be annotated with @Collection or @Embedded.',
|
||||
modelClass,
|
||||
);
|
||||
}
|
||||
|
||||
if (modelClass.isAbstract) {
|
||||
err('Class must not be abstract.', modelClass);
|
||||
}
|
||||
|
||||
if (!modelClass.isPublic) {
|
||||
err('Class must be public.', modelClass);
|
||||
}
|
||||
|
||||
final constructor = modelClass.constructors
|
||||
.firstOrNullWhere((ConstructorElement c) => c.periodOffset == null);
|
||||
if (constructor == null) {
|
||||
err('Class needs an unnamed constructor.', modelClass);
|
||||
}
|
||||
|
||||
final hasCollectionSupertype = modelClass.allSupertypes.any((type) {
|
||||
return type.element.collectionAnnotation != null ||
|
||||
type.element.embeddedAnnotation != null;
|
||||
});
|
||||
if (hasCollectionSupertype) {
|
||||
err(
|
||||
'Class must not have a supertype annotated with @Collection or '
|
||||
'@Embedded.',
|
||||
modelClass,
|
||||
);
|
||||
}
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
||||
void _checkValidPropertiesConstructor(
|
||||
List<ObjectProperty> properties,
|
||||
ConstructorElement constructor,
|
||||
) {
|
||||
if (properties.map((e) => e.isarName).distinct().length !=
|
||||
properties.length) {
|
||||
err(
|
||||
'Two or more properties have the same name.',
|
||||
constructor.enclosingElement,
|
||||
);
|
||||
}
|
||||
|
||||
final unknownConstructorParameter = constructor.parameters.firstOrNullWhere(
|
||||
(p) => p.isRequired && properties.none((e) => e.dartName == p.name),
|
||||
);
|
||||
if (unknownConstructorParameter != null) {
|
||||
err(
|
||||
'Constructor parameter does not match a property.',
|
||||
unknownConstructorParameter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> _getEmbeddedDartNames(ClassElement element) {
|
||||
void _fillNames(Map<String, String> names, ClassElement element) {
|
||||
for (final property in element.allAccessors) {
|
||||
final type = property.type.scalarType.element;
|
||||
if (type is ClassElement && type.embeddedAnnotation != null) {
|
||||
final isarName = type.isarName;
|
||||
if (!names.containsKey(isarName)) {
|
||||
names[type.isarName] = type.displayName;
|
||||
_fillNames(names, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final names = <String, String>{};
|
||||
_fillNames(names, element);
|
||||
return names;
|
||||
}
|
||||
|
||||
ObjectProperty analyzeObjectProperty(
|
||||
PropertyInducingElement property,
|
||||
ConstructorElement constructor,
|
||||
) {
|
||||
final dartType = property.type;
|
||||
final scalarDartType = dartType.scalarType;
|
||||
Map<String, dynamic>? enumMap;
|
||||
String? enumPropertyName;
|
||||
String? defaultEnumElement;
|
||||
|
||||
late final IsarType isarType;
|
||||
if (scalarDartType.element is EnumElement) {
|
||||
final enumeratedAnn = property.enumeratedAnnotation;
|
||||
if (enumeratedAnn == null) {
|
||||
err('Enum property must be annotated with @enumerated.', property);
|
||||
}
|
||||
|
||||
final enumClass = scalarDartType.element! as EnumElement;
|
||||
final enumElements =
|
||||
enumClass.fields.where((f) => f.isEnumConstant).toList();
|
||||
defaultEnumElement = '${enumClass.name}.${enumElements.first.name}';
|
||||
|
||||
if (enumeratedAnn.type == EnumType.ordinal) {
|
||||
isarType = dartType.isDartCoreList ? IsarType.byteList : IsarType.byte;
|
||||
enumMap = {
|
||||
for (var i = 0; i < enumElements.length; i++) enumElements[i].name: i,
|
||||
};
|
||||
enumPropertyName = 'index';
|
||||
} else if (enumeratedAnn.type == EnumType.ordinal32) {
|
||||
isarType = dartType.isDartCoreList ? IsarType.intList : IsarType.int;
|
||||
|
||||
enumMap = {
|
||||
for (var i = 0; i < enumElements.length; i++) enumElements[i].name: i,
|
||||
};
|
||||
enumPropertyName = 'index';
|
||||
} else if (enumeratedAnn.type == EnumType.name) {
|
||||
isarType =
|
||||
dartType.isDartCoreList ? IsarType.stringList : IsarType.string;
|
||||
enumMap = {
|
||||
for (final value in enumElements) value.name: value.name,
|
||||
};
|
||||
enumPropertyName = 'name';
|
||||
} else {
|
||||
enumPropertyName = enumeratedAnn.property;
|
||||
if (enumPropertyName == null) {
|
||||
err(
|
||||
'Enums with type EnumType.value must specify which property '
|
||||
'should be used.',
|
||||
property,
|
||||
);
|
||||
}
|
||||
final enumProperty = enumClass.getField(enumPropertyName);
|
||||
if (enumProperty == null || enumProperty.isEnumConstant) {
|
||||
err('Enum property "$enumProperty" does not exist.', property);
|
||||
} else if (enumProperty.nonSynthetic is PropertyAccessorElement) {
|
||||
err('Only fields are supported for enum properties', enumProperty);
|
||||
}
|
||||
|
||||
final enumIsarType = enumProperty.type.isarType;
|
||||
if (enumIsarType != IsarType.byte &&
|
||||
enumIsarType != IsarType.int &&
|
||||
enumIsarType != IsarType.long &&
|
||||
enumIsarType != IsarType.string) {
|
||||
err('Unsupported enum property type.', enumProperty);
|
||||
}
|
||||
|
||||
isarType =
|
||||
dartType.isDartCoreList ? enumIsarType!.listType : enumIsarType!;
|
||||
enumMap = {};
|
||||
for (final element in enumElements) {
|
||||
final property =
|
||||
element.computeConstantValue()!.getField(enumPropertyName)!;
|
||||
final propertyValue = property.toBoolValue() ??
|
||||
property.toIntValue() ??
|
||||
property.toDoubleValue() ??
|
||||
property.toStringValue();
|
||||
if (propertyValue == null) {
|
||||
err(
|
||||
'Null values are not supported for enum properties.',
|
||||
enumProperty,
|
||||
);
|
||||
}
|
||||
|
||||
if (enumMap.values.contains(propertyValue)) {
|
||||
err(
|
||||
'Enum property has duplicate values.',
|
||||
enumProperty,
|
||||
);
|
||||
}
|
||||
enumMap[element.name] = propertyValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (dartType.isarType != null) {
|
||||
isarType = dartType.isarType!;
|
||||
} else {
|
||||
err(
|
||||
'Unsupported type. Please annotate the property with @ignore.',
|
||||
property,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final nullable = dartType.nullabilitySuffix != NullabilitySuffix.none;
|
||||
final elementNullable = isarType.isList &&
|
||||
dartType.scalarType.nullabilitySuffix != NullabilitySuffix.none;
|
||||
|
||||
if ((isarType == IsarType.byte && nullable) ||
|
||||
(isarType == IsarType.byteList && elementNullable)) {
|
||||
err('Bytes must not be nullable.', property);
|
||||
}
|
||||
|
||||
final constructorParameter =
|
||||
constructor.parameters.firstOrNullWhere((p) => p.name == property.name);
|
||||
int? constructorPosition;
|
||||
late PropertyDeser deserialize;
|
||||
if (constructorParameter != null) {
|
||||
if (constructorParameter.type != property.type) {
|
||||
err(
|
||||
'Constructor parameter type does not match property type',
|
||||
constructorParameter,
|
||||
);
|
||||
}
|
||||
deserialize = constructorParameter.isNamed
|
||||
? PropertyDeser.namedParam
|
||||
: PropertyDeser.positionalParam;
|
||||
constructorPosition =
|
||||
constructor.parameters.indexOf(constructorParameter);
|
||||
} else {
|
||||
deserialize =
|
||||
property.setter == null ? PropertyDeser.none : PropertyDeser.assign;
|
||||
}
|
||||
|
||||
return ObjectProperty(
|
||||
dartName: property.displayName,
|
||||
isarName: property.isarName,
|
||||
typeClassName: dartType.scalarType.element!.name!,
|
||||
targetIsarName: isarType.containsObject
|
||||
? dartType.scalarType.element!.isarName
|
||||
: null,
|
||||
isarType: isarType,
|
||||
isId: dartType.isIsarId,
|
||||
enumMap: enumMap,
|
||||
enumProperty: enumPropertyName,
|
||||
defaultEnumElement: defaultEnumElement,
|
||||
nullable: nullable,
|
||||
elementNullable: elementNullable,
|
||||
userDefaultValue: constructorParameter?.defaultValueCode,
|
||||
deserialize: deserialize,
|
||||
assignable: property.setter != null,
|
||||
constructorPosition: constructorPosition,
|
||||
);
|
||||
}
|
||||
|
||||
ObjectLink analyzeObjectLink(PropertyInducingElement property) {
|
||||
if (property.type.nullabilitySuffix != NullabilitySuffix.none) {
|
||||
err('Link properties must not be nullable.', property);
|
||||
} else if (property.isLate) {
|
||||
err('Link properties must not be late.', property);
|
||||
}
|
||||
|
||||
final type = property.type as ParameterizedType;
|
||||
final linkType = type.typeArguments[0];
|
||||
if (linkType.nullabilitySuffix != NullabilitySuffix.none) {
|
||||
err('Links type must not be nullable.', property);
|
||||
}
|
||||
|
||||
final targetCol = linkType.element! as ClassElement;
|
||||
if (targetCol.collectionAnnotation == null) {
|
||||
err('Link target is not annotated with @collection');
|
||||
}
|
||||
|
||||
final backlinkAnn = property.backlinkAnnotation;
|
||||
String? targetLinkIsarName;
|
||||
if (backlinkAnn != null) {
|
||||
final targetProperty = targetCol.allAccessors
|
||||
.firstOrNullWhere((e) => e.displayName == backlinkAnn.to);
|
||||
if (targetProperty == null) {
|
||||
err('Target of Backlink does not exist', property);
|
||||
} else if (targetProperty.backlinkAnnotation != null) {
|
||||
err('Target of Backlink is also a backlink', property);
|
||||
}
|
||||
|
||||
if (!targetProperty.isLink && !targetProperty.isLinks) {
|
||||
err('Target of backlink is not a link', property);
|
||||
}
|
||||
|
||||
final targetLink = analyzeObjectLink(targetProperty);
|
||||
targetLinkIsarName = targetLink.isarName;
|
||||
}
|
||||
|
||||
return ObjectLink(
|
||||
dartName: property.displayName,
|
||||
isarName: property.isarName,
|
||||
targetLinkIsarName: targetLinkIsarName,
|
||||
targetCollectionDartName: linkType.element!.name!,
|
||||
targetCollectionIsarName: targetCol.isarName,
|
||||
isSingle: property.isLink,
|
||||
);
|
||||
}
|
||||
|
||||
Iterable<ObjectIndex> analyzeObjectIndex(
|
||||
List<ObjectProperty> properties,
|
||||
PropertyInducingElement element,
|
||||
) sync* {
|
||||
final property =
|
||||
properties.firstOrNullWhere((it) => it.dartName == element.name);
|
||||
if (property == null || property.isId) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final index in element.indexAnnotations) {
|
||||
final indexProperties = <ObjectIndexProperty>[];
|
||||
final isString = property.isarType == IsarType.string ||
|
||||
property.isarType == IsarType.stringList;
|
||||
final defaultType = property.isarType.isList || isString
|
||||
? IndexType.hash
|
||||
: IndexType.value;
|
||||
|
||||
indexProperties.add(
|
||||
ObjectIndexProperty(
|
||||
property: property,
|
||||
type: index.type ?? defaultType,
|
||||
caseSensitive: index.caseSensitive ?? isString,
|
||||
),
|
||||
);
|
||||
for (final c in index.composite) {
|
||||
final compositeProperty =
|
||||
properties.firstOrNullWhere((it) => it.dartName == c.property);
|
||||
if (compositeProperty == null) {
|
||||
err('Property does not exist: "${c.property}".', element);
|
||||
} else if (compositeProperty.isId) {
|
||||
err('Ids cannot be indexed', element);
|
||||
} else {
|
||||
final isString = compositeProperty.isarType == IsarType.string ||
|
||||
compositeProperty.isarType == IsarType.stringList;
|
||||
final defaultType = compositeProperty.isarType.isList || isString
|
||||
? IndexType.hash
|
||||
: IndexType.value;
|
||||
indexProperties.add(
|
||||
ObjectIndexProperty(
|
||||
property: compositeProperty,
|
||||
type: c.type ?? defaultType,
|
||||
caseSensitive: c.caseSensitive ?? isString,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final name = index.name ??
|
||||
indexProperties.map((e) => e.property.isarName).join('_');
|
||||
checkIsarName(name, element);
|
||||
|
||||
final objectIndex = ObjectIndex(
|
||||
name: name,
|
||||
properties: indexProperties,
|
||||
unique: index.unique,
|
||||
replace: index.replace,
|
||||
);
|
||||
_verifyObjectIndex(objectIndex, element);
|
||||
|
||||
yield objectIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void _verifyObjectIndex(ObjectIndex index, Element element) {
|
||||
final properties = index.properties;
|
||||
|
||||
if (properties.map((it) => it.property.isarName).distinct().length !=
|
||||
properties.length) {
|
||||
err('Composite index contains duplicate properties.', element);
|
||||
}
|
||||
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
final property = properties[i];
|
||||
if (property.isarType.isList &&
|
||||
property.type != IndexType.hash &&
|
||||
properties.length > 1) {
|
||||
err('Composite indexes do not support non-hashed lists.', element);
|
||||
}
|
||||
if (property.isarType.containsFloat && i != properties.lastIndex) {
|
||||
err(
|
||||
'Only the last property of a composite index may be a '
|
||||
'double value.',
|
||||
element,
|
||||
);
|
||||
}
|
||||
if (property.isarType == IsarType.string) {
|
||||
if (property.type != IndexType.hash && i != properties.lastIndex) {
|
||||
err(
|
||||
'Only the last property of a composite index may be a '
|
||||
'non-hashed String.',
|
||||
element,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (property.isarType.containsObject) {
|
||||
err(
|
||||
'Embedded objects may not be indexed.',
|
||||
element,
|
||||
);
|
||||
}
|
||||
if (property.type != IndexType.value) {
|
||||
if (!property.isarType.isList && property.isarType != IsarType.string) {
|
||||
err('Only Strings and Lists may be hashed.', element);
|
||||
} else if (property.isarType.containsFloat) {
|
||||
err('List<double> may must not be hashed.', element);
|
||||
}
|
||||
}
|
||||
if (property.isarType != IsarType.stringList &&
|
||||
property.type == IndexType.hashElements) {
|
||||
err('Only String lists may have hashed elements.', element);
|
||||
}
|
||||
}
|
||||
|
||||
if (!index.unique && index.replace) {
|
||||
err('Only unique indexes can replace.', element);
|
||||
}
|
||||
}
|
||||
}
|
107
lib/src/isar_type.dart
Normal file
107
lib/src/isar_type.dart
Normal file
@ -0,0 +1,107 @@
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:isar_generator/src/helper.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
const TypeChecker _dateTimeChecker = TypeChecker.fromRuntime(DateTime);
|
||||
bool _isDateTime(Element element) => _dateTimeChecker.isExactly(element);
|
||||
|
||||
extension DartTypeX on DartType {
|
||||
IsarType? get _primitiveIsarType {
|
||||
if (isDartCoreBool) {
|
||||
return IsarType.bool;
|
||||
} else if (isDartCoreInt) {
|
||||
if (alias?.element.name == 'byte') {
|
||||
return IsarType.byte;
|
||||
} else if (alias?.element.name == 'short') {
|
||||
return IsarType.int;
|
||||
} else {
|
||||
return IsarType.long;
|
||||
}
|
||||
} else if (isDartCoreDouble) {
|
||||
if (alias?.element.name == 'float') {
|
||||
return IsarType.float;
|
||||
} else {
|
||||
return IsarType.double;
|
||||
}
|
||||
} else if (isDartCoreString) {
|
||||
return IsarType.string;
|
||||
} else if (_isDateTime(element2!)) {
|
||||
return IsarType.dateTime;
|
||||
} else if (element2!.embeddedAnnotation != null) {
|
||||
return IsarType.object;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool get isIsarId {
|
||||
return alias?.element.name == 'Id';
|
||||
}
|
||||
|
||||
DartType get scalarType {
|
||||
if (isDartCoreList) {
|
||||
final parameterizedType = this as ParameterizedType;
|
||||
final typeArguments = parameterizedType.typeArguments;
|
||||
if (typeArguments.isNotEmpty) {
|
||||
return typeArguments[0];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
IsarType? get isarType {
|
||||
final primitiveType = _primitiveIsarType;
|
||||
if (primitiveType != null) {
|
||||
return primitiveType;
|
||||
}
|
||||
|
||||
if (isDartCoreList) {
|
||||
switch (scalarType._primitiveIsarType) {
|
||||
case IsarType.bool:
|
||||
return IsarType.boolList;
|
||||
case IsarType.byte:
|
||||
return IsarType.byteList;
|
||||
case IsarType.int:
|
||||
return IsarType.intList;
|
||||
case IsarType.float:
|
||||
return IsarType.floatList;
|
||||
case IsarType.long:
|
||||
return IsarType.longList;
|
||||
case IsarType.double:
|
||||
return IsarType.doubleList;
|
||||
case IsarType.dateTime:
|
||||
return IsarType.dateTimeList;
|
||||
case IsarType.string:
|
||||
return IsarType.stringList;
|
||||
case IsarType.object:
|
||||
return IsarType.objectList;
|
||||
// ignore: no_default_cases
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
extension IsarTypeX on IsarType {
|
||||
bool get containsBool => this == IsarType.bool || this == IsarType.boolList;
|
||||
|
||||
bool get containsFloat =>
|
||||
this == IsarType.float ||
|
||||
this == IsarType.floatList ||
|
||||
this == IsarType.double ||
|
||||
this == IsarType.doubleList;
|
||||
|
||||
bool get containsDate =>
|
||||
this == IsarType.dateTime || this == IsarType.dateTimeList;
|
||||
|
||||
bool get containsString =>
|
||||
this == IsarType.string || this == IsarType.stringList;
|
||||
|
||||
bool get containsObject =>
|
||||
this == IsarType.object || this == IsarType.objectList;
|
||||
}
|
211
lib/src/object_info.dart
Normal file
211
lib/src/object_info.dart
Normal file
@ -0,0 +1,211 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
import 'package:xxh3/xxh3.dart';
|
||||
|
||||
class ObjectInfo {
|
||||
ObjectInfo({
|
||||
required this.dartName,
|
||||
required this.isarName,
|
||||
this.accessor,
|
||||
required List<ObjectProperty> properties,
|
||||
this.embeddedDartNames = const {},
|
||||
this.indexes = const [],
|
||||
this.links = const [],
|
||||
}) {
|
||||
this.properties = properties.sortedBy((e) => e.isarName).toList();
|
||||
}
|
||||
|
||||
final String dartName;
|
||||
final String isarName;
|
||||
final String? accessor;
|
||||
late final List<ObjectProperty> properties;
|
||||
final Map<String, String> embeddedDartNames;
|
||||
final List<ObjectIndex> indexes;
|
||||
final List<ObjectLink> links;
|
||||
|
||||
int get id => xxh3(utf8.encode(isarName) as Uint8List);
|
||||
|
||||
bool get isEmbedded => accessor == null;
|
||||
|
||||
ObjectProperty get idProperty => properties.firstWhere((it) => it.isId);
|
||||
|
||||
List<ObjectProperty> get objectProperties =>
|
||||
properties.where((it) => !it.isId).toList();
|
||||
|
||||
String get getIdName => '_${dartName.decapitalize()}GetId';
|
||||
String get getLinksName => '_${dartName.decapitalize()}GetLinks';
|
||||
String get attachName => '_${dartName.decapitalize()}Attach';
|
||||
|
||||
String get estimateSizeName => '_${dartName.decapitalize()}EstimateSize';
|
||||
String get serializeName => '_${dartName.decapitalize()}Serialize';
|
||||
String get deserializeName => '_${dartName.decapitalize()}Deserialize';
|
||||
String get deserializePropName =>
|
||||
'_${dartName.decapitalize()}DeserializeProp';
|
||||
}
|
||||
|
||||
enum PropertyDeser {
|
||||
none,
|
||||
assign,
|
||||
positionalParam,
|
||||
namedParam,
|
||||
}
|
||||
|
||||
class ObjectProperty {
|
||||
ObjectProperty({
|
||||
required this.dartName,
|
||||
required this.isarName,
|
||||
required this.typeClassName,
|
||||
this.targetIsarName,
|
||||
required this.isarType,
|
||||
required this.isId,
|
||||
required this.enumMap,
|
||||
required this.enumProperty,
|
||||
required this.defaultEnumElement,
|
||||
required this.nullable,
|
||||
required this.elementNullable,
|
||||
this.userDefaultValue,
|
||||
required this.deserialize,
|
||||
required this.assignable,
|
||||
this.constructorPosition,
|
||||
});
|
||||
|
||||
final String dartName;
|
||||
final String isarName;
|
||||
final String typeClassName;
|
||||
final String? targetIsarName;
|
||||
|
||||
final bool isId;
|
||||
final IsarType isarType;
|
||||
final Map<String, dynamic>? enumMap;
|
||||
final String? enumProperty;
|
||||
final String? defaultEnumElement;
|
||||
|
||||
final bool nullable;
|
||||
final bool elementNullable;
|
||||
final String? userDefaultValue;
|
||||
|
||||
final PropertyDeser deserialize;
|
||||
final bool assignable;
|
||||
final int? constructorPosition;
|
||||
|
||||
bool get isEnum => enumMap != null;
|
||||
|
||||
String get scalarDartType {
|
||||
if (isId) {
|
||||
return 'Id';
|
||||
} else if (isEnum) {
|
||||
return typeClassName;
|
||||
}
|
||||
|
||||
switch (isarType) {
|
||||
case IsarType.bool:
|
||||
case IsarType.boolList:
|
||||
return 'bool';
|
||||
case IsarType.byte:
|
||||
case IsarType.byteList:
|
||||
case IsarType.int:
|
||||
case IsarType.intList:
|
||||
case IsarType.long:
|
||||
case IsarType.longList:
|
||||
return 'int';
|
||||
case IsarType.float:
|
||||
case IsarType.floatList:
|
||||
case IsarType.double:
|
||||
case IsarType.doubleList:
|
||||
return 'double';
|
||||
case IsarType.dateTime:
|
||||
case IsarType.dateTimeList:
|
||||
return 'DateTime';
|
||||
case IsarType.object:
|
||||
case IsarType.objectList:
|
||||
return typeClassName;
|
||||
case IsarType.string:
|
||||
case IsarType.stringList:
|
||||
return 'String';
|
||||
}
|
||||
}
|
||||
|
||||
String get nScalarDartType => isarType.isList
|
||||
? '$scalarDartType${elementNullable ? '?' : ''}'
|
||||
: '$scalarDartType${nullable ? '?' : ''}';
|
||||
|
||||
String get dartType => isarType.isList
|
||||
? 'List<$nScalarDartType>${nullable ? '?' : ''}'
|
||||
: nScalarDartType;
|
||||
|
||||
String get targetSchema => '${scalarDartType.capitalize()}Schema';
|
||||
|
||||
String enumValueMapName(ObjectInfo object) {
|
||||
return '_${object.dartName}${dartName}EnumValueMap';
|
||||
}
|
||||
|
||||
String valueEnumMapName(ObjectInfo object) {
|
||||
return '_${object.dartName}${dartName}ValueEnumMap';
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectIndexProperty {
|
||||
const ObjectIndexProperty({
|
||||
required this.property,
|
||||
required this.type,
|
||||
required this.caseSensitive,
|
||||
});
|
||||
|
||||
final ObjectProperty property;
|
||||
final IndexType type;
|
||||
final bool caseSensitive;
|
||||
|
||||
IsarType get isarType => property.isarType;
|
||||
|
||||
bool get isMultiEntry => isarType.isList && type != IndexType.hash;
|
||||
}
|
||||
|
||||
class ObjectIndex {
|
||||
ObjectIndex({
|
||||
required this.name,
|
||||
required this.properties,
|
||||
required this.unique,
|
||||
required this.replace,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final List<ObjectIndexProperty> properties;
|
||||
final bool unique;
|
||||
final bool replace;
|
||||
|
||||
late final id = xxh3(utf8.encode(name) as Uint8List);
|
||||
}
|
||||
|
||||
class ObjectLink {
|
||||
const ObjectLink({
|
||||
required this.dartName,
|
||||
required this.isarName,
|
||||
this.targetLinkIsarName,
|
||||
required this.targetCollectionDartName,
|
||||
required this.targetCollectionIsarName,
|
||||
required this.isSingle,
|
||||
});
|
||||
|
||||
final String dartName;
|
||||
final String isarName;
|
||||
|
||||
// isar name of the original link (only for backlinks)
|
||||
final String? targetLinkIsarName;
|
||||
final String targetCollectionDartName;
|
||||
final String targetCollectionIsarName;
|
||||
final bool isSingle;
|
||||
|
||||
bool get isBacklink => targetLinkIsarName != null;
|
||||
|
||||
int id(String objectIsarName) {
|
||||
final col = isBacklink ? targetCollectionIsarName : objectIsarName;
|
||||
final colId = xxh3(utf8.encode(col) as Uint8List, seed: isBacklink ? 1 : 0);
|
||||
|
||||
final name = targetLinkIsarName ?? isarName;
|
||||
return xxh3(utf8.encode(name) as Uint8List, seed: colId);
|
||||
}
|
||||
}
|
26
pubspec.yaml
Normal file
26
pubspec.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
name: isar_generator
|
||||
description: Code generator for the Isar Database. Finds classes annotated with @Collection.
|
||||
version: 3.1.0+1
|
||||
repository: https://github.com/isar/isar
|
||||
homepage: https://isar.dev
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
analyzer: ">=4.6.0 <6.0.0"
|
||||
build: ^2.3.0
|
||||
dart_style: ^2.2.3
|
||||
dartx: ^1.1.0
|
||||
glob: ^2.0.2
|
||||
isar:
|
||||
path: ../isar
|
||||
path: ^1.8.1
|
||||
source_gen: ^1.2.2
|
||||
xxh3: ^1.0.1
|
||||
|
||||
dev_dependencies:
|
||||
build_test: ^2.1.5
|
||||
matcher: ^0.12.12
|
||||
test: ^1.21.0
|
||||
very_good_analysis: ^3.0.1
|
33
test/error_test.dart
Normal file
33
test/error_test.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:build/build.dart';
|
||||
import 'package:build_test/build_test.dart';
|
||||
import 'package:isar_generator/isar_generator.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('Error case', () {
|
||||
for (final file in Directory('test/errors').listSync(recursive: true)) {
|
||||
if (file is! File || !file.path.endsWith('.dart')) continue;
|
||||
|
||||
test(file.path, () async {
|
||||
final content = await file.readAsLines();
|
||||
|
||||
final errorMessage = content.first.split('//').last.trim();
|
||||
|
||||
var error = '';
|
||||
try {
|
||||
await testBuilder(
|
||||
getIsarGenerator(BuilderOptions.empty),
|
||||
{'a|${file.path}': content.join('\n')},
|
||||
reader: await PackageAssetReader.currentIsolate(),
|
||||
);
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
}
|
||||
|
||||
expect(error.toLowerCase(), contains(errorMessage.toLowerCase()));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
8
test/errors/class/abstract.dart
Normal file
8
test/errors/class/abstract.dart
Normal file
@ -0,0 +1,8 @@
|
||||
// must not be abstract
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
abstract class Model {
|
||||
Id? id;
|
||||
}
|
19
test/errors/class/collection_supertype.dart
Normal file
19
test/errors/class/collection_supertype.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// supertype annotated with @collection
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Supertype {
|
||||
Id? id;
|
||||
}
|
||||
|
||||
class Subtype implements Supertype {
|
||||
@override
|
||||
Id? id;
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model implements Subtype {
|
||||
@override
|
||||
Id? id;
|
||||
}
|
10
test/errors/class/constructor_named.dart
Normal file
10
test/errors/class/constructor_named.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// unnamed constructor
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Model.create();
|
||||
|
||||
Id? id;
|
||||
}
|
13
test/errors/class/constructor_unknown_parameter.dart
Normal file
13
test/errors/class/constructor_unknown_parameter.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// constructor parameter does not match a property
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
Model(this.prop1, String somethingElse);
|
||||
|
||||
Id? id;
|
||||
|
||||
final String prop1;
|
||||
}
|
13
test/errors/class/constructor_wrong_parameter.dart
Normal file
13
test/errors/class/constructor_wrong_parameter.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// constructor parameter type does not match property type
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
Model(int prop1);
|
||||
|
||||
Id? id;
|
||||
|
||||
String prop1 = '5';
|
||||
}
|
7
test/errors/class/enum.dart
Normal file
7
test/errors/class/enum.dart
Normal file
@ -0,0 +1,7 @@
|
||||
// only classes
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
// ignore: invalid_annotation_target
|
||||
@collection
|
||||
enum Test { a, b, c }
|
8
test/errors/class/invalid_name.dart
Normal file
8
test/errors/class/invalid_name.dart
Normal file
@ -0,0 +1,8 @@
|
||||
// must not be abstract
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
abstract class Model {
|
||||
Id? id;
|
||||
}
|
7
test/errors/class/mixin.dart
Normal file
7
test/errors/class/mixin.dart
Normal file
@ -0,0 +1,7 @@
|
||||
// only classes
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
// ignore: invalid_annotation_target
|
||||
@collection
|
||||
mixin Test {}
|
9
test/errors/class/private.dart
Normal file
9
test/errors/class/private.dart
Normal file
@ -0,0 +1,9 @@
|
||||
// must be public
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
// ignore: unused_element
|
||||
class _Model {
|
||||
Id? id;
|
||||
}
|
7
test/errors/class/variable.dart
Normal file
7
test/errors/class/variable.dart
Normal file
@ -0,0 +1,7 @@
|
||||
// only classes
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
// ignore: invalid_annotation_target
|
||||
@collection
|
||||
const t = 'hello';
|
10
test/errors/id/duplicate.dart
Normal file
10
test/errors/id/duplicate.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// two or more properties with type "Id" defined
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Test {
|
||||
Id? id1;
|
||||
|
||||
Id? id2;
|
||||
}
|
10
test/errors/id/missing.dart
Normal file
10
test/errors/id/missing.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// no id property defined
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Test {
|
||||
late int id;
|
||||
|
||||
late String name;
|
||||
}
|
13
test/errors/index/composite_double_not_last.dart
Normal file
13
test/errors/index/composite_double_not_last.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// only the last property of a composite index may be a double value
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(composite: [CompositeIndex('val2')])
|
||||
double? val1;
|
||||
|
||||
String? val2;
|
||||
}
|
13
test/errors/index/composite_non_hashed_list.dart
Normal file
13
test/errors/index/composite_non_hashed_list.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// composite indexes do not support non-hashed lists
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(composite: [CompositeIndex('str')], type: IndexType.value)
|
||||
List<int>? list;
|
||||
|
||||
String? str;
|
||||
}
|
13
test/errors/index/composite_string_value_not_last.dart
Normal file
13
test/errors/index/composite_string_value_not_last.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// last property of a composite index may be a non-hashed string
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(composite: [CompositeIndex('str2')], type: IndexType.value)
|
||||
String? str1;
|
||||
|
||||
String? str2;
|
||||
}
|
11
test/errors/index/contains_id.dart
Normal file
11
test/errors/index/contains_id.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// ids cannot be indexed
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(composite: [CompositeIndex('id')])
|
||||
String? str;
|
||||
}
|
11
test/errors/index/double_list_hashed.dart
Normal file
11
test/errors/index/double_list_hashed.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// list<double> may must not be hashed
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(type: IndexType.hash)
|
||||
List<double>? list;
|
||||
}
|
14
test/errors/index/duplicate_name.dart
Normal file
14
test/errors/index/duplicate_name.dart
Normal file
@ -0,0 +1,14 @@
|
||||
// same name
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(name: 'myindex')
|
||||
String? prop1;
|
||||
|
||||
@Index(name: 'myindex')
|
||||
String? prop2;
|
||||
}
|
13
test/errors/index/duplicate_property.dart
Normal file
13
test/errors/index/duplicate_property.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// composite index contains duplicate properties
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(composite: [CompositeIndex('str1')], type: IndexType.value)
|
||||
String? str1;
|
||||
|
||||
String? str2;
|
||||
}
|
11
test/errors/index/invalid_name.dart
Normal file
11
test/errors/index/invalid_name.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// names must not be blank or start with "_"
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(name: '_index')
|
||||
String? str;
|
||||
}
|
11
test/errors/index/non_string_hashed.dart
Normal file
11
test/errors/index/non_string_hashed.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// only strings and lists may be hashed
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(type: IndexType.hash)
|
||||
int? val;
|
||||
}
|
11
test/errors/index/non_string_list_hashed_elements.dart
Normal file
11
test/errors/index/non_string_list_hashed_elements.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// only string lists may have hashed elements
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(type: IndexType.hashElements)
|
||||
List<int>? list;
|
||||
}
|
11
test/errors/index/non_unique_replace.dart
Normal file
11
test/errors/index/non_unique_replace.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// only unique indexes can replace
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(replace: true)
|
||||
String? str;
|
||||
}
|
14
test/errors/index/object_hashed.dart
Normal file
14
test/errors/index/object_hashed.dart
Normal file
@ -0,0 +1,14 @@
|
||||
// objects may not be indexed
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index()
|
||||
EmbeddedModel? obj;
|
||||
}
|
||||
|
||||
@embedded
|
||||
class EmbeddedModel {}
|
14
test/errors/index/object_list_hashed.dart
Normal file
14
test/errors/index/object_list_hashed.dart
Normal file
@ -0,0 +1,14 @@
|
||||
// objects may not be indexed
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(type: IndexType.hash)
|
||||
List<EmbeddedModel>? list;
|
||||
}
|
||||
|
||||
@embedded
|
||||
class EmbeddedModel {}
|
11
test/errors/index/property_does_not_exist.dart
Normal file
11
test/errors/index/property_does_not_exist.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// property does not exist
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Index(composite: [CompositeIndex('myProp')])
|
||||
String? str;
|
||||
}
|
16
test/errors/link/backlink_target_does_no_exist.dart
Normal file
16
test/errors/link/backlink_target_does_no_exist.dart
Normal file
@ -0,0 +1,16 @@
|
||||
// target of backlink does not exist
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model1 {
|
||||
Id? id;
|
||||
|
||||
@Backlink(to: 'abc')
|
||||
final IsarLink<Model2> link = IsarLink();
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
}
|
19
test/errors/link/backlink_target_is_backlink.dart
Normal file
19
test/errors/link/backlink_target_is_backlink.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// target of backlink is also a backlink
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model1 {
|
||||
Id? id;
|
||||
|
||||
@Backlink(to: 'link')
|
||||
final IsarLink<Model2> link = IsarLink();
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
|
||||
@Backlink(to: 'link')
|
||||
final IsarLink<Model1> link = IsarLink();
|
||||
}
|
18
test/errors/link/backlink_target_not_a_link.dart
Normal file
18
test/errors/link/backlink_target_not_a_link.dart
Normal file
@ -0,0 +1,18 @@
|
||||
// target of backlink is not a link
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model1 {
|
||||
Id? id;
|
||||
|
||||
@Backlink(to: 'str')
|
||||
final IsarLink<Model2> link = IsarLink();
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
|
||||
String? str;
|
||||
}
|
18
test/errors/link/duplicate_name.dart
Normal file
18
test/errors/link/duplicate_name.dart
Normal file
@ -0,0 +1,18 @@
|
||||
// same name
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
final IsarLink<Model2> prop1 = IsarLink();
|
||||
|
||||
@Name('prop1')
|
||||
final IsarLinks<Model2> prop2 = IsarLinks();
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
}
|
16
test/errors/link/invalid_name.dart
Normal file
16
test/errors/link/invalid_name.dart
Normal file
@ -0,0 +1,16 @@
|
||||
// names must not be blank or start with
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Name('_link')
|
||||
final IsarLink<Model2> link = IsarLink();
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
}
|
15
test/errors/link/late.dart
Normal file
15
test/errors/link/late.dart
Normal file
@ -0,0 +1,15 @@
|
||||
// must not be late
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
late IsarLink<Model2> link;
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
}
|
15
test/errors/link/nullable.dart
Normal file
15
test/errors/link/nullable.dart
Normal file
@ -0,0 +1,15 @@
|
||||
// must not be nullable
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
IsarLink<Model2>? link;
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
}
|
10
test/errors/link/target_not_a_collection.dart
Normal file
10
test/errors/link/target_not_a_collection.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// link target is not annotated with @collection
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
final IsarLink<int> link = IsarLink();
|
||||
}
|
15
test/errors/link/type_nullable.dart
Normal file
15
test/errors/link/type_nullable.dart
Normal file
@ -0,0 +1,15 @@
|
||||
// links type must not be nullable
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
final IsarLink<Model2?> link = IsarLink();
|
||||
}
|
||||
|
||||
@collection
|
||||
class Model2 {
|
||||
Id? id;
|
||||
}
|
13
test/errors/property/duplicate_name.dart
Normal file
13
test/errors/property/duplicate_name.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// same name
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
String? prop1;
|
||||
|
||||
@Name('prop1')
|
||||
String? prop2;
|
||||
}
|
17
test/errors/property/enum_bool_type.dart
Normal file
17
test/errors/property/enum_bool_type.dart
Normal file
@ -0,0 +1,17 @@
|
||||
// unsupported enum property type
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Enumerated(EnumType.value, 'value')
|
||||
late MyEnum field;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
optionA;
|
||||
|
||||
final bool value = true;
|
||||
}
|
17
test/errors/property/enum_double_type.dart
Normal file
17
test/errors/property/enum_double_type.dart
Normal file
@ -0,0 +1,17 @@
|
||||
// unsupported enum property type
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Enumerated(EnumType.value, 'value')
|
||||
late MyEnum field;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
optionA;
|
||||
|
||||
final double value = 5.5;
|
||||
}
|
21
test/errors/property/enum_duplicate.dart
Normal file
21
test/errors/property/enum_duplicate.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// has duplicate values
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Enumerated(EnumType.value, 'value')
|
||||
late MyEnum field;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
option1(1),
|
||||
option2(2),
|
||||
option3(1);
|
||||
|
||||
const MyEnum(this.value);
|
||||
|
||||
final int value;
|
||||
}
|
17
test/errors/property/enum_float_type.dart
Normal file
17
test/errors/property/enum_float_type.dart
Normal file
@ -0,0 +1,17 @@
|
||||
// unsupported enum property type
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Enumerated(EnumType.value, 'value')
|
||||
late MyEnum field;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
optionA;
|
||||
|
||||
final float value = 5.5;
|
||||
}
|
17
test/errors/property/enum_list_type.dart
Normal file
17
test/errors/property/enum_list_type.dart
Normal file
@ -0,0 +1,17 @@
|
||||
// unsupported enum property type
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Enumerated(EnumType.value, 'value')
|
||||
late MyEnum prop;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
optionA;
|
||||
|
||||
final List<String> value = [];
|
||||
}
|
14
test/errors/property/enum_not_annotated.dart
Normal file
14
test/errors/property/enum_not_annotated.dart
Normal file
@ -0,0 +1,14 @@
|
||||
// enum property must be annotated with @enumerated
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
late MyEnum? prop;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
a;
|
||||
}
|
17
test/errors/property/enum_null_value.dart
Normal file
17
test/errors/property/enum_null_value.dart
Normal file
@ -0,0 +1,17 @@
|
||||
// null values are not supported
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Enumerated(EnumType.value, 'value')
|
||||
late MyEnum prop;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
optionA;
|
||||
|
||||
final String? value = null;
|
||||
}
|
20
test/errors/property/enum_object_type.dart
Normal file
20
test/errors/property/enum_object_type.dart
Normal file
@ -0,0 +1,20 @@
|
||||
// unsupported enum property type
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Enumerated(EnumType.value, 'value')
|
||||
late MyEnum prop;
|
||||
}
|
||||
|
||||
enum MyEnum {
|
||||
optionA;
|
||||
|
||||
final value = EmbeddedModel();
|
||||
}
|
||||
|
||||
@embedded
|
||||
class EmbeddedModel {}
|
11
test/errors/property/invalid_name.dart
Normal file
11
test/errors/property/invalid_name.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// names must not be blank or start with "_"
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
@Name('_prop')
|
||||
String? prop;
|
||||
}
|
10
test/errors/property/null_byte.dart
Normal file
10
test/errors/property/null_byte.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// bytes must not be nullable
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
late byte? prop;
|
||||
}
|
10
test/errors/property/null_byte_element.dart
Normal file
10
test/errors/property/null_byte_element.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// bytes must not be nullable
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
late List<byte?> prop;
|
||||
}
|
10
test/errors/property/unsupported_type.dart
Normal file
10
test/errors/property/unsupported_type.dart
Normal file
@ -0,0 +1,10 @@
|
||||
// unsupported type
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@collection
|
||||
class Model {
|
||||
Id? id;
|
||||
|
||||
late Set<String>? prop;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user