From 7dbaa59c01b0c7355ce7bf090f1caa5cb5d6e67a Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Fri, 23 May 2025 16:03:29 +0300 Subject: [PATCH] refactor inject generator --- .../lib/inject_generator.dart | 139 ++++++++++++------ 1 file changed, 90 insertions(+), 49 deletions(-) diff --git a/cherrypick_generator/lib/inject_generator.dart b/cherrypick_generator/lib/inject_generator.dart index 000fe51..b421d2d 100644 --- a/cherrypick_generator/lib/inject_generator.dart +++ b/cherrypick_generator/lib/inject_generator.dart @@ -11,7 +11,10 @@ class InjectGenerator extends GeneratorForAnnotation { @override FutureOr generateForAnnotatedElement( - Element element, ConstantReader annotation, BuildStep buildStep) { + Element element, + ConstantReader annotation, + BuildStep buildStep, + ) { if (element is! ClassElement) { throw InvalidGenerationSourceError( '@injectable() can only be applied to classes.', @@ -23,61 +26,99 @@ class InjectGenerator extends GeneratorForAnnotation { final className = classElement.name; final mixinName = '_\$$className'; - final injectedFields = classElement.fields.where((field) => field.metadata - .any((m) => - m.computeConstantValue()?.type?.getDisplayString() == 'inject')); + final buffer = StringBuffer() + ..writeln('mixin $mixinName {') + ..writeln(' void _inject($className instance) {'); - final buffer = StringBuffer(); - buffer.writeln('mixin $mixinName {'); - buffer.writeln(' void _inject($className instance) {'); + // Collect and process all @inject fields + final injectFields = + classElement.fields.where(_isInjectField).map(_parseInjectField); - for (final field in injectedFields) { - String? scopeName; - String? namedValue; - - for (final m in field.metadata) { - final DartObject? obj = m.computeConstantValue(); - final type = obj?.type?.getDisplayString(); - if (type == 'scope') { - scopeName = obj?.getField('name')?.toStringValue(); - } else if (type == 'named') { - namedValue = obj?.getField('value')?.toStringValue(); - } - } - - // --- Фикс: определяем resolveAsync для Future --- - final DartType type = field.type; - String genericType; - String resolveMethod; - - if (type.isDartAsyncFuture) { - // Если поле Future - final typeArg = (type as ParameterizedType).typeArguments.first; - genericType = typeArg.getDisplayString(); - resolveMethod = 'resolveAsync<$genericType>'; - } else { - genericType = type.getDisplayString(); - resolveMethod = 'resolve<$genericType>'; - } - - // Вызываем openScope или openRootScope - String accessor = (scopeName != null && scopeName.isNotEmpty) - ? "CherryPick.openScope(scopeName: '$scopeName').$resolveMethod" - : "CherryPick.openRootScope().$resolveMethod"; - - // Аргументы resolve - String params = (namedValue != null && namedValue.isNotEmpty) - ? "(named: '$namedValue')" - : '()'; - - buffer.writeln(" instance.${field.name} = $accessor$params;"); + for (final parsedField in injectFields) { + buffer.writeln(_generateInjectionLine(parsedField)); } - buffer.writeln(' }'); - buffer.writeln('}'); + buffer + ..writeln(' }') + ..writeln('}'); return buffer.toString(); } + + // Checks if a field has @inject annotation + static bool _isInjectField(FieldElement field) { + return field.metadata.any( + (m) => m.computeConstantValue()?.type?.getDisplayString() == 'inject', + ); + } + + // Parsed structure storage + static _ParsedInjectField _parseInjectField(FieldElement field) { + String? scopeName; + String? namedValue; + + for (final meta in field.metadata) { + final DartObject? obj = meta.computeConstantValue(); + final type = obj?.type?.getDisplayString(); + if (type == 'scope') { + scopeName = obj?.getField('name')?.toStringValue(); + } else if (type == 'named') { + namedValue = obj?.getField('value')?.toStringValue(); + } + } + + final DartType dartType = field.type; + String coreTypeName; + bool isFuture; + + if (dartType.isDartAsyncFuture) { + final ParameterizedType paramType = dartType as ParameterizedType; + coreTypeName = paramType.typeArguments.first.getDisplayString(); + isFuture = true; + } else { + coreTypeName = dartType.getDisplayString(); + isFuture = false; + } + + return _ParsedInjectField( + fieldName: field.name, + coreType: coreTypeName, + isFuture: isFuture, + scopeName: scopeName, + namedValue: namedValue, + ); + } + + // Generates the injection invocation line for the field + String _generateInjectionLine(_ParsedInjectField field) { + final methodName = field.isFuture + ? 'resolveAsync<${field.coreType}>' + : 'resolve<${field.coreType}>'; + final openCall = (field.scopeName != null && field.scopeName!.isNotEmpty) + ? "CherryPick.openScope(scopeName: '${field.scopeName}')" + : "CherryPick.openRootScope()"; + final params = (field.namedValue != null && field.namedValue!.isNotEmpty) + ? "(named: '${field.namedValue}')" + : '()'; + + return " instance.${field.fieldName} = $openCall.$methodName$params;"; + } +} + +class _ParsedInjectField { + final String fieldName; + final String coreType; + final bool isFuture; + final String? scopeName; + final String? namedValue; + + _ParsedInjectField({ + required this.fieldName, + required this.coreType, + required this.isFuture, + this.scopeName, + this.namedValue, + }); } Builder injectBuilder(BuilderOptions options) =>