isar_generator/lib/src/helper.dart
2025-03-19 19:41:35 +01:00

185 lines
5.7 KiB
Dart

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);
}