doc: update documentations

This commit is contained in:
Sergey Penkovsky
2025-05-22 16:05:09 +03:00
parent c47418d922
commit 3bbecfb8ac
5 changed files with 215 additions and 35 deletions

View File

@@ -17,33 +17,57 @@ import 'package:source_gen/source_gen.dart';
import 'bind_parameters_spec.dart';
import 'metadata_utils.dart';
/// ---------------------------------------------------------------------------
/// BindSpec -- describes a binding specification generated for a dependency.
///
/// ENGLISH
/// Represents all the data necessary to generate a DI binding for a single
/// method in a module class. Each BindSpec corresponds to one public method
/// and contains information about its type, provider method, lifecycle (singleton),
/// parameters (with their annotations), binding strategy (instance/provide),
/// asynchronous mode, and named keys. It is responsible for generating the
/// correct Dart code to register this binding with the DI container, in both
/// sync and async cases, with and without named or runtime arguments.
///
/// RUSSIAN
/// Описывает параметры для создания одного биндинга зависимости (binding spec).
///
/// Каждый биндинг соответствует одному публичному методу класса-модуля.
///
/// Каждый биндинг соответствует одному публичному методу класса-модуля и
/// содержит всю информацию для генерации кода регистрации этого биндинга в
/// DI-контейнере: тип возвращаемой зависимости, имя метода, параметры, аннотации
/// (@singleton, @named, @instance, @provide), асинхронность, признак runtime
/// аргументов и др. Генерирует правильный Dart-код для регистрации биндера.
/// ---------------------------------------------------------------------------
class BindSpec {
/// The type this binding provides (e.g. SomeService)
/// Тип, который предоставляет биндинг (например, SomeService)
final String returnType;
/// Method name that implements the binding
/// Имя метода, который реализует биндинг
final String methodName;
/// Optional name for named dependency (from @named)
/// Необязательное имя, для именованной зависимости (используется с @named)
final String? named;
/// Whether the dependency is a singleton (@singleton annotation)
/// Является ли зависимость синглтоном (имеется ли аннотация @singleton)
final bool isSingleton;
/// List of method parameters to inject dependencies with
/// Список параметров, которые требуются методу для внедрения зависимостей
final List<BindParameterSpec> parameters;
/// Binding type: 'instance' or 'provide' (@instance or @provide)
final String bindingType; // 'instance' | 'provide'
/// True if the method is asynchronous and uses instance binding (Future)
final bool isAsyncInstance;
/// True if the method is asynchronous and uses provide binding (Future)
final bool isAsyncProvide;
/// True if the binding method accepts runtime "params" argument (@params)
final bool hasParams;
BindSpec({
@@ -58,10 +82,20 @@ class BindSpec {
required this.hasParams,
});
/// -------------------------------------------------------------------------
/// generateBind
///
/// ENGLISH
/// Generates a line of Dart code registering the binding with the DI framework.
/// Produces something like:
/// bind<Type>().toProvide(() => method(args)).withName('name').singleton();
/// Indent parameter allows formatted multiline output.
///
/// RUSSIAN
/// Формирует dart-код для биндинга, например:
/// bind<Type>().toProvide(() => method(args)).withName('name').singleton();
///
/// Параметр [indent] задаёт отступ для красивого форматирования кода.
/// -------------------------------------------------------------------------
String generateBind(int indent) {
final indentStr = ' ' * indent;
final provide = _generateProvideClause(indent);
@@ -72,13 +106,15 @@ class BindSpec {
'$postfix;';
}
// Internal method: decides how the provide clause should be generated by param kind.
String _generateProvideClause(int indent) {
if (hasParams) return _generateWithParamsProvideClause(indent);
return _generatePlainProvideClause(indent);
}
/// EN / RU: Supports runtime parameters (@params).
String _generateWithParamsProvideClause(int indent) {
// Безопасное имя для параметра
// Safe variable name for parameters.
const paramVar = 'args';
final fnArgs = parameters
.map((p) => p.isParams ? paramVar : p.generateArg(paramVar))
@@ -103,6 +139,7 @@ class BindSpec {
}
}
/// EN / RU: Supports only injected dependencies, not runtime (@params).
String _generatePlainProvideClause(int indent) {
final argsStr = parameters.map((p) => p.generateArg()).join(', ');
final multiLine = argsStr.length > 60 || argsStr.contains('\n');
@@ -125,24 +162,37 @@ class BindSpec {
}
}
/// EN / RU: Adds .withName and .singleton if needed.
String _generatePostfix() {
final namePart = named != null ? ".withName('$named')" : '';
final singletonPart = isSingleton ? '.singleton()' : '';
return '$namePart$singletonPart';
}
/// Создаёт спецификацию биндинга (BindSpec) из метода класса-модуля
/// -------------------------------------------------------------------------
/// fromMethod
///
/// ENGLISH
/// Creates a BindSpec from a module class method by analyzing its return type,
/// annotations, list of parameters (with their own annotations), and async-ness.
/// Throws if a method does not have the required @instance() or @provide().
///
/// RUSSIAN
/// Создаёт спецификацию биндинга (BindSpec) из метода класса-модуля, анализируя
/// возвращаемый тип, аннотации, параметры (и их аннотации), а также факт
/// асинхронности. Если нет @instance или @provide — кидает ошибку.
/// -------------------------------------------------------------------------
static BindSpec fromMethod(MethodElement method) {
var returnType = method.returnType.getDisplayString();
final methodName = method.displayName;
// Проверим, помечен ли метод аннотацией @singleton
// Check for @singleton annotation.
final isSingleton = MetadataUtils.anyMeta(method.metadata, 'singleton');
// Получаем имя из @named(), если есть
// Get @named value if present.
final named = MetadataUtils.getNamedValue(method.metadata);
// Для каждого параметра метода
// Parse each method parameter.
final params = <BindParameterSpec>[];
bool hasParams = false;
for (final p in method.parameters) {
@@ -153,18 +203,18 @@ class BindSpec {
params.add(BindParameterSpec(typeStr, paramNamed, isParams: isParams));
}
// определяем bindingType
// Determine bindingType: @instance or @provide.
final hasInstance = MetadataUtils.anyMeta(method.metadata, 'instance');
final hasProvide = MetadataUtils.anyMeta(method.metadata, 'provide');
if (!hasInstance && !hasProvide) {
throw InvalidGenerationSourceError(
'Метод $methodName класса-модуля должен быть помечен либо @instance(), либо @provide().',
'Method $methodName must be marked with @instance() or @provide().',
element: method,
);
}
final bindingType = hasInstance ? 'instance' : 'provide';
// --- Новый участок: извлекаем внутренний тип из Future<> и выставляем флаги
// -- Extract inner type for Future<T> and set async flags.
bool isAsyncInstance = false;
bool isAsyncProvide = false;
final futureInnerType = _extractFutureInnerType(returnType);
@@ -187,6 +237,7 @@ class BindSpec {
);
}
/// EN / RU: Extracts inner type from Future<T>, returns e.g. "T" or null.
static String? _extractFutureInnerType(String typeName) {
final match = RegExp(r'^Future<(.+)>$').firstMatch(typeName);
return match?.group(1)?.trim();