From 2cba7f2675cfe5e64d4b5a6fbcb48a05de46f154 Mon Sep 17 00:00:00 2001 From: yarashevich_kv Date: Mon, 19 May 2025 21:02:33 +0300 Subject: [PATCH 1/5] impr: add binding resolver class. --- cherrypick/example/bin/main.dart | 37 ++++--- cherrypick/lib/cherrypick.dart | 1 + cherrypick/lib/src/binding.dart | 125 ++++------------------ cherrypick/lib/src/binding_resolver.dart | 129 +++++++++++++++++++++++ cherrypick/lib/src/scope.dart | 68 ++++-------- cherrypick/test/src/binding_test.dart | 95 +++++++---------- cherrypick/test/src/scope_test.dart | 6 +- 7 files changed, 237 insertions(+), 224 deletions(-) create mode 100644 cherrypick/lib/src/binding_resolver.dart diff --git a/cherrypick/example/bin/main.dart b/cherrypick/example/bin/main.dart index d5bc17c..5d48e66 100644 --- a/cherrypick/example/bin/main.dart +++ b/cherrypick/example/bin/main.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:cherrypick/cherrypick.dart'; +import 'package:meta/meta.dart'; class AppModule extends Module { @override @@ -17,19 +18,28 @@ class FeatureModule extends Module { @override void builder(Scope currentScope) { // Using toProvideAsync for async initialization - bind().withName("networkRepo").toProvideAsync(() async { + bind() + .withName("networkRepo") + .toProvideWithParams((params) async { + print('REPO PARAMS: $params'); + final client = await Future.delayed( - Duration(milliseconds: 100), - () => currentScope.resolve( - named: isMock ? "apiClientMock" : "apiClientImpl")); + Duration(milliseconds: 1000), + () => currentScope.resolve( + named: isMock ? "apiClientMock" : "apiClientImpl", + ), + ); + return NetworkDataRepository(client); }).singleton(); // Asynchronous initialization of DataBloc - bind().toProvideAsync( + bind().toProvide( () async { final repo = await currentScope.resolveAsync( - named: "networkRepo"); + named: "networkRepo", + params: 'Some params', + ); return DataBloc(repo); }, ); @@ -38,18 +48,19 @@ class FeatureModule extends Module { Future main() async { try { - final scope = openRootScope().installModules([ - AppModule(), - ]); + final scope = openRootScope().installModules([AppModule()]); final subScope = scope .openSubScope("featureScope") .installModules([FeatureModule(isMock: true)]); - // Asynchronous instance resolution - final dataBloc = await subScope.resolveAsync(); - dataBloc.data.listen((d) => print('Received data: $d'), - onError: (e) => print('Error: $e'), onDone: () => print('DONE')); + // Asynchronous instance resolution + final dataBloc = await subScope.resolveAsync(); + dataBloc.data.listen( + (d) => print('Received data: $d'), + onError: (e) => print('Error: $e'), + onDone: () => print('DONE'), + ); await dataBloc.fetchData(); } catch (e) { diff --git a/cherrypick/lib/cherrypick.dart b/cherrypick/lib/cherrypick.dart index e779f9d..9532298 100644 --- a/cherrypick/lib/cherrypick.dart +++ b/cherrypick/lib/cherrypick.dart @@ -13,6 +13,7 @@ library; // limitations under the License. // +export 'package:cherrypick/src/binding_resolver.dart'; export 'package:cherrypick/src/binding.dart'; export 'package:cherrypick/src/cycle_detector.dart'; export 'package:cherrypick/src/global_cycle_detector.dart'; diff --git a/cherrypick/lib/src/binding.dart b/cherrypick/lib/src/binding.dart index fe248c5..a319d88 100644 --- a/cherrypick/lib/src/binding.dart +++ b/cherrypick/lib/src/binding.dart @@ -11,45 +11,21 @@ // limitations under the License. // -enum Mode { simple, instance, providerInstance, providerInstanceWithParams } - -typedef Provider = T? Function(); - -typedef ProviderWithParams = T Function(dynamic params); - -typedef AsyncProvider = Future Function(); - -typedef AsyncProviderWithParams = Future Function(dynamic params); +import 'package:cherrypick/src/binding_resolver.dart'; /// RU: Класс Binding настраивает параметры экземпляра. /// ENG: The Binding class configures the settings for the instance. /// class Binding { - late Mode _mode; late Type _key; - late String _name; - T? _instance; - Future? _instanceAsync; - Provider? _provider; - ProviderWithParams? _providerWithParams; + String? _name; - AsyncProvider? asyncProvider; - AsyncProviderWithParams? asyncProviderWithParams; - - late bool _isSingleton = false; - late bool _isNamed = false; + BindingResolver? _resolver; Binding() { - _mode = Mode.simple; _key = T; } - /// RU: Метод возвращает [Mode] экземпляра. - /// ENG: The method returns the [Mode] of the instance. - /// - /// return [Mode] - Mode get mode => _mode; - /// RU: Метод возвращает тип экземпляра. /// ENG: The method returns the type of the instance. /// @@ -60,19 +36,21 @@ class Binding { /// ENG: The method returns the name of the instance. /// /// return [String] - String get name => _name; - - /// RU: Метод проверяет сингелтон экземпляр или нет. - /// ENG: The method checks the singleton instance or not. - /// - /// return [bool] - bool get isSingleton => _isSingleton; + String? get name => _name; /// RU: Метод проверяет именован экземпляр или нет. /// ENG: The method checks whether the instance is named or not. /// /// return [bool] - bool get isNamed => _isNamed; + bool get isNamed => _name != null; + + /// RU: Метод проверяет сингелтон экземпляр или нет. + /// ENG: The method checks the singleton instance or not. + /// + /// return [bool] + bool get isSingleton => _resolver?.isSingleton ?? false; + + BindingResolver? get resolver => _resolver; /// RU: Добавляет имя для экземляпя [value]. /// ENG: Added name for instance [value]. @@ -80,7 +58,6 @@ class Binding { /// return [Binding] Binding withName(String name) { _name = name; - _isNamed = true; return this; } @@ -88,21 +65,9 @@ class Binding { /// ENG: Initialization instance [value]. /// /// return [Binding] - Binding toInstance(T value) { - _mode = Mode.instance; - _instance = value; - _isSingleton = true; - return this; - } + Binding toInstance(Instance value) { + _resolver = InstanceResolver(value); - /// RU: Инициализация экземляпяра [value]. - /// ENG: Initialization instance [value]. - /// - /// return [Binding] - Binding toInstanceAsync(Future value) { - _mode = Mode.instance; - _instanceAsync = value; - _isSingleton = true; return this; } @@ -111,18 +76,8 @@ class Binding { /// /// return [Binding] Binding toProvide(Provider value) { - _mode = Mode.providerInstance; - _provider = value; - return this; - } + _resolver = ProviderResolver((_) => value.call(), withParams: false); - /// RU: Инициализация экземляпяра  через провайдер [value]. - /// ENG: Initialization instance via provider [value]. - /// - /// return [Binding] - Binding toProvideAsync(AsyncProvider provider) { - _mode = Mode.providerInstance; - asyncProvider = provider; return this; } @@ -131,18 +86,8 @@ class Binding { /// /// return [Binding] Binding toProvideWithParams(ProviderWithParams value) { - _mode = Mode.providerInstanceWithParams; - _providerWithParams = value; - return this; - } + _resolver = ProviderResolver(value, withParams: true); - /// RU: Инициализация экземляра через асинхронный провайдер [value] с динамическим параметром. - /// ENG: Initializes the instance via async provider [value] with a dynamic param. - /// - /// return [Binding] - Binding toProvideAsyncWithParams(AsyncProviderWithParams provider) { - _mode = Mode.providerInstanceWithParams; - asyncProviderWithParams = provider; return this; } @@ -151,40 +96,16 @@ class Binding { /// /// return [Binding] Binding singleton() { - _isSingleton = true; + _resolver?.toSingleton(); + return this; } - /// RU: Поиск экземпляра. - /// ENG: Resolve instance. - /// - /// return [T] - T? get instance => _instance; - - /// RU: Поиск экземпляра. - /// ENG: Resolve instance. - /// - /// return [T] - Future? get instanceAsync => _instanceAsync; - - /// RU: Поиск экземпляра. - /// ENG: Resolve instance. - /// - /// return [T] - T? get provider { - if (_isSingleton) { - _instance ??= _provider?.call(); - return _instance; - } - return _provider?.call(); + T? resolveSync([dynamic params]) { + return resolver?.resolveSync(params); } - /// RU: Поиск экземпляра с параметром. - /// - /// ENG: Resolve instance with [params]. - /// - /// return [T] - T? providerWithParams(dynamic params) { - return _providerWithParams?.call(params); + Future? resolveAsync([dynamic params]) { + return resolver?.resolveAsync(params); } } diff --git a/cherrypick/lib/src/binding_resolver.dart b/cherrypick/lib/src/binding_resolver.dart new file mode 100644 index 0000000..d6b69ee --- /dev/null +++ b/cherrypick/lib/src/binding_resolver.dart @@ -0,0 +1,129 @@ +import 'dart:async'; + +typedef Instance = FutureOr; + +/// RU: Синхронный или асинхронный провайдер без параметров, возвращающий [T] или [Future]. +/// ENG: Synchronous or asynchronous provider without parameters, returning [T] or [Future]. +typedef Provider = FutureOr Function(); + +/// RU: Провайдер с динамическим параметром, возвращающий [T] или [Future] в зависимости от реализации. +/// ENG: Provider with dynamic parameter, returning [T] or [Future] depending on implementation. +typedef ProviderWithParams = FutureOr Function(dynamic); + +/// RU: Абстрактный интерфейс для классов, которые разрешают зависимости типа [T]. +/// ENG: Abstract interface for classes that resolve dependencies of type [T]. +abstract class BindingResolver { + /// RU: Синхронное разрешение зависимости с параметром [params]. + /// ENG: Synchronous resolution of the dependency with [params]. + T? resolveSync([dynamic params]); + + /// RU: Асинхронное разрешение зависимости с параметром [params]. + /// ENG: Asynchronous resolution of the dependency with [params]. + Future? resolveAsync([dynamic params]); + + /// RU: Помечает текущий резолвер как синглтон — результат будет закеширован. + /// ENG: Marks this resolver as singleton — result will be cached. + void toSingleton(); + + bool get isSingleton; +} + +/// RU: Резолвер, оборачивающий конкретный экземпляр [T] (или Future), без вызова провайдера. +/// ENG: Resolver that wraps a concrete instance of [T] (or Future), without provider invocation. +class InstanceResolver implements BindingResolver { + final Instance _instance; + + /// RU: Создаёт резолвер, оборачивающий значение [instance]. + /// ENG: Creates a resolver that wraps the given [instance]. + InstanceResolver(this._instance); + + @override + T resolveSync([_]) { + if (_instance is T) return _instance as T; + throw StateError( + 'Instance $_instance is Future; ' + 'use resolveAsync() instead', + ); + } + + @override + Future resolveAsync([_]) { + if (_instance is Future) return _instance as Future; + if (_instance is T) return Future.value(_instance as T); + throw StateError('Unexpected instance type: $_instance'); + } + + @override + void toSingleton() {} + + @override + bool get isSingleton => true; +} + +/// RU: Резолвер, оборачивающий провайдер, с возможностью синглтон-кеширования. +/// ENG: Resolver that wraps a provider, with optional singleton caching. +class ProviderResolver implements BindingResolver { + final ProviderWithParams _provider; + final bool _withParams; + + FutureOr? _cache; + bool _singleton = false; + + /// RU: Создаёт резолвер из произвольной функции [raw], поддерживающей ноль или один параметр. + /// ENG: Creates a resolver from arbitrary function [raw], supporting zero or one parameter. + ProviderResolver( + ProviderWithParams provider, { + required bool withParams, + }) : _provider = provider, + _withParams = withParams; + + @override + T resolveSync([dynamic params]) { + _checkParams(params); + + final result = _cache ?? _provider(params); + + if (result is T) { + if (_singleton) { + _cache ??= result; + } + return result; + } + + throw StateError( + 'Provider [$_provider] return Future<$T>. Use resolveAsync() instead.', + ); + } + + @override + Future resolveAsync([dynamic params]) { + _checkParams(params); + + final result = _cache ?? _provider(params); + final target = result is Future ? result : Future.value(result); + + if (_singleton) { + _cache ??= target; + } + + return target; + } + + @override + void toSingleton() { + _singleton = true; + } + + @override + bool get isSingleton => _singleton; + + /// RU: Проверяет, был ли передан параметр, если провайдер требует его. + /// ENG: Checks if parameter is passed when the provider expects it. + void _checkParams(dynamic params) { + if (_withParams && params == null) { + throw StateError( + '[$T] Params is null. Maybe you forgot to pass it?', + ); + } + } +} diff --git a/cherrypick/lib/src/scope.dart b/cherrypick/lib/src/scope.dart index da7ff6c..eb157bf 100644 --- a/cherrypick/lib/src/scope.dart +++ b/cherrypick/lib/src/scope.dart @@ -13,9 +13,9 @@ import 'dart:collection'; import 'dart:math'; -import 'package:cherrypick/src/binding.dart'; import 'package:cherrypick/src/cycle_detector.dart'; import 'package:cherrypick/src/global_cycle_detector.dart'; +import 'package:cherrypick/src/binding_resolver.dart'; import 'package:cherrypick/src/module.dart'; Scope openRootScope() => Scope(null); @@ -176,33 +176,12 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin { /// RU: Внутренний метод для разрешения зависимостей без проверки циклических зависимостей. /// ENG: Internal method for dependency resolution without circular dependency checking. T? _tryResolveInternal({String? named, dynamic params}) { - // 1 Поиск зависимости по всем модулям текущего скоупа - if (_modulesList.isNotEmpty) { - for (var module in _modulesList) { - for (var binding in module.bindingSet) { - if (binding.key == T && - ((!binding.isNamed && named == null) || - (binding.isNamed && named == binding.name))) { - switch (binding.mode) { - case Mode.instance: - return binding.instance; - case Mode.providerInstance: - return binding.provider; - case Mode.providerInstanceWithParams: - if (params == null) { - throw StateError('Param is null. Maybe you forget pass it'); - } - return binding.providerWithParams(params); - default: - return null; - } - } - } - } - } + final resolver = _findBindingResolver(named); - // 2 Поиск зависимостей в родительском скоупе - return _parentScope?._tryResolveInternal(named: named, params: params); + // 1 Поиск зависимости по всем модулям текущего скоупа + return resolver?.resolveSync(params) ?? + // 2 Поиск зависимостей в родительском скоупе + _parentScope?.tryResolve(named: named, params: params); } /// RU: Асинхронно возвращает найденную зависимость, определенную параметром типа [T]. @@ -266,30 +245,25 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin { /// RU: Внутренний метод для асинхронного разрешения зависимостей без проверки циклических зависимостей. /// ENG: Internal method for async dependency resolution without circular dependency checking. Future _tryResolveAsyncInternal({String? named, dynamic params}) async { - if (_modulesList.isNotEmpty) { - for (var module in _modulesList) { - for (var binding in module.bindingSet) { - if (binding.key == T && - ((!binding.isNamed && named == null) || - (binding.isNamed && named == binding.name))) { - if (binding.instanceAsync != null) { - return await binding.instanceAsync; - } + final resolver = _findBindingResolver(named); - if (binding.asyncProvider != null) { - return await binding.asyncProvider?.call(); - } + // 1 Поиск зависимости по всем модулям текущего скоупа + return resolver?.resolveAsync(params) ?? + // 2 Поиск зависимостей в родительском скоупе + _parentScope?.tryResolveAsync(named: named, params: params); + } - if (binding.asyncProviderWithParams != null) { - if (params == null) { - throw StateError('Param is null. Maybe you forget pass it'); - } - return await binding.asyncProviderWithParams!(params); - } - } + BindingResolver? _findBindingResolver(String? named) { + for (var module in _modulesList) { + for (var binding in module.bindingSet) { + if (binding.key == T && + ((!binding.isNamed && named == null) || + (binding.isNamed && named == binding.name))) { + return binding.resolver as BindingResolver?; } } } - return _parentScope?._tryResolveAsyncInternal(named: named, params: params); + + return null; } } diff --git a/cherrypick/test/src/binding_test.dart b/cherrypick/test/src/binding_test.dart index 4d3ce11..c61cc89 100644 --- a/cherrypick/test/src/binding_test.dart +++ b/cherrypick/test/src/binding_test.dart @@ -1,4 +1,4 @@ -import 'package:cherrypick/src/binding.dart'; +import 'package:cherrypick/cherrypick.dart'; import 'package:test/test.dart'; void main() { @@ -7,12 +7,12 @@ void main() { group('Without name', () { test('Returns null by default', () { final binding = Binding(); - expect(binding.instance, null); + expect(binding.resolver, null); }); test('Sets mode to instance', () { final binding = Binding().toInstance(5); - expect(binding.mode, Mode.instance); + expect(binding.resolver, isA>()); }); test('isSingleton is true', () { @@ -22,19 +22,19 @@ void main() { test('Stores value', () { final binding = Binding().toInstance(5); - expect(binding.instance, 5); + expect(binding.resolver?.resolveSync(), 5); }); }); group('With name', () { test('Returns null by default', () { final binding = Binding().withName('n'); - expect(binding.instance, null); + expect(binding.resolver, null); }); test('Sets mode to instance', () { final binding = Binding().withName('n').toInstance(5); - expect(binding.mode, Mode.instance); + expect(binding.resolver, isA>()); }); test('Sets key', () { @@ -49,7 +49,7 @@ void main() { test('Stores value', () { final binding = Binding().withName('n').toInstance(5); - expect(binding.instance, 5); + expect(binding.resolver?.resolveSync(), 5); }); test('Sets name', () { @@ -60,45 +60,39 @@ void main() { test('Multiple toInstance calls change value', () { final binding = Binding().toInstance(1).toInstance(2); - expect(binding.instance, 2); + expect(binding.resolver?.resolveSync(), 2); }); }); // --- Instance binding (asynchronous) --- group('Async Instance Binding (toInstanceAsync)', () { test('Resolves instanceAsync with expected value', () async { - final binding = Binding().toInstanceAsync(Future.value(42)); - expect(await binding.instanceAsync, 42); - }); - - test('Does not affect instance', () { - final binding = Binding().toInstanceAsync(Future.value(5)); - expect(binding.instance, null); + final binding = Binding().toInstance(Future.value(42)); + expect(await binding.resolveAsync(), 42); }); test('Sets mode to instance', () { - final binding = Binding().toInstanceAsync(Future.value(5)); - expect(binding.mode, Mode.instance); + final binding = Binding().toInstance(Future.value(5)); + expect(binding.resolver, isA>()); }); test('isSingleton is true after toInstanceAsync', () { - final binding = Binding().toInstanceAsync(Future.value(5)); + final binding = Binding().toInstance(Future.value(5)); expect(binding.isSingleton, isTrue); }); test('Composes with withName', () async { - final binding = Binding() - .withName('asyncValue') - .toInstanceAsync(Future.value(7)); + final binding = + Binding().withName('asyncValue').toInstance(Future.value(7)); expect(binding.isNamed, isTrue); expect(binding.name, 'asyncValue'); - expect(await binding.instanceAsync, 7); + expect(await binding.resolveAsync(), 7); }); test('Keeps value after multiple awaits', () async { - final binding = Binding().toInstanceAsync(Future.value(123)); - final result1 = await binding.instanceAsync; - final result2 = await binding.instanceAsync; + final binding = Binding().toInstance(Future.value(123)); + final result1 = await binding.resolveAsync(); + final result2 = await binding.resolveAsync(); expect(result1, equals(result2)); }); }); @@ -108,12 +102,12 @@ void main() { group('Without name', () { test('Returns null by default', () { final binding = Binding(); - expect(binding.provider, null); + expect(binding.resolver, null); }); test('Sets mode to providerInstance', () { final binding = Binding().toProvide(() => 5); - expect(binding.mode, Mode.providerInstance); + expect(binding.resolver, isA>()); }); test('isSingleton is false by default', () { @@ -123,19 +117,19 @@ void main() { test('Returns provided value', () { final binding = Binding().toProvide(() => 5); - expect(binding.provider, 5); + expect(binding.resolveSync(), 5); }); }); group('With name', () { test('Returns null by default', () { final binding = Binding().withName('n'); - expect(binding.provider, null); + expect(binding.resolver, null); }); test('Sets mode to providerInstance', () { final binding = Binding().withName('n').toProvide(() => 5); - expect(binding.mode, Mode.providerInstance); + expect(binding.resolver, isA>()); }); test('Sets key', () { @@ -150,7 +144,7 @@ void main() { test('Returns provided value', () { final binding = Binding().withName('n').toProvide(() => 5); - expect(binding.provider, 5); + expect(binding.resolveSync(), 5); }); test('Sets name', () { @@ -163,14 +157,14 @@ void main() { // --- Async provider binding --- group('Async Provider Binding', () { test('Resolves asyncProvider value', () async { - final binding = Binding().toProvideAsync(() async => 5); - expect(await binding.asyncProvider?.call(), 5); + final binding = Binding().toProvide(() async => 5); + expect(await binding.resolveAsync(), 5); }); test('Resolves asyncProviderWithParams value', () async { final binding = Binding() - .toProvideAsyncWithParams((param) async => 5 + (param as int)); - expect(await binding.asyncProviderWithParams?.call(3), 8); + .toProvideWithParams((param) async => 5 + (param as int)); + expect(await binding.resolveAsync(3), 8); }); }); @@ -179,12 +173,7 @@ void main() { group('Without name', () { test('Returns null if no provider set', () { final binding = Binding().singleton(); - expect(binding.provider, null); - }); - - test('Sets mode to providerInstance', () { - final binding = Binding().toProvide(() => 5).singleton(); - expect(binding.mode, Mode.providerInstance); + expect(binding.resolver, null); }); test('isSingleton is true', () { @@ -194,7 +183,7 @@ void main() { test('Returns singleton value', () { final binding = Binding().toProvide(() => 5).singleton(); - expect(binding.provider, 5); + expect(binding.resolveSync(), 5); }); test('Returns same value each call and provider only called once', () { @@ -204,8 +193,8 @@ void main() { return counter; }).singleton(); - final first = binding.provider; - final second = binding.provider; + final first = binding.resolveSync(); + final second = binding.resolveSync(); expect(first, equals(second)); expect(counter, 1); }); @@ -214,13 +203,7 @@ void main() { group('With name', () { test('Returns null if no provider set', () { final binding = Binding().withName('n').singleton(); - expect(binding.provider, null); - }); - - test('Sets mode to providerInstance', () { - final binding = - Binding().withName('n').toProvide(() => 5).singleton(); - expect(binding.mode, Mode.providerInstance); + expect(binding.resolver, null); }); test('Sets key', () { @@ -238,7 +221,7 @@ void main() { test('Returns singleton value', () { final binding = Binding().withName('n').toProvide(() => 5).singleton(); - expect(binding.provider, 5); + expect(binding.resolveSync(), 5); }); test('Sets name', () { @@ -247,12 +230,6 @@ void main() { expect(binding.name, 'n'); }); }); - - test('Chained withName and singleton preserves mode', () { - final binding = - Binding().toProvide(() => 3).withName("named").singleton(); - expect(binding.mode, Mode.providerInstance); - }); }); // --- WithName / Named binding, isNamed, edge-cases --- @@ -265,7 +242,7 @@ void main() { test('providerWithParams returns null if not set', () { final binding = Binding(); - expect(binding.providerWithParams(123), null); + expect(binding.resolveSync(123), null); }); }); } diff --git a/cherrypick/test/src/scope_test.dart b/cherrypick/test/src/scope_test.dart index 738ae74..16f80e3 100644 --- a/cherrypick/test/src/scope_test.dart +++ b/cherrypick/test/src/scope_test.dart @@ -127,7 +127,7 @@ void main() { final scope = Scope(null) ..installModules([ _InlineModule((m, s) { - m.bind().toInstanceAsync(Future.value('async value')); + m.bind().toInstance(Future.value('async value')); }), ]); expect(await scope.resolveAsync(), "async value"); @@ -137,7 +137,7 @@ void main() { final scope = Scope(null) ..installModules([ _InlineModule((m, s) { - m.bind().toProvideAsync(() async => 7); + m.bind().toProvide(() async => 7); }), ]); expect(await scope.resolveAsync(), 7); @@ -147,7 +147,7 @@ void main() { final scope = Scope(null) ..installModules([ _InlineModule((m, s) { - m.bind().toProvideAsyncWithParams((x) async => (x as int) * 3); + m.bind().toProvideWithParams((x) async => (x as int) * 3); }), ]); expect(await scope.resolveAsync(params: 2), 6); From 9f0a8a84aacb87d23b2f7e43d0f7ade086e9ede0 Mon Sep 17 00:00:00 2001 From: yarashevich_kv Date: Tue, 29 Jul 2025 09:39:35 +0300 Subject: [PATCH 2/5] impr: fix after rebase. --- cherrypick/example/bin/main.dart | 1 - cherrypick/lib/src/binding_resolver.dart | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cherrypick/example/bin/main.dart b/cherrypick/example/bin/main.dart index 5d48e66..dff6cda 100644 --- a/cherrypick/example/bin/main.dart +++ b/cherrypick/example/bin/main.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'package:cherrypick/cherrypick.dart'; -import 'package:meta/meta.dart'; class AppModule extends Module { @override diff --git a/cherrypick/lib/src/binding_resolver.dart b/cherrypick/lib/src/binding_resolver.dart index d6b69ee..37c196b 100644 --- a/cherrypick/lib/src/binding_resolver.dart +++ b/cherrypick/lib/src/binding_resolver.dart @@ -39,7 +39,7 @@ class InstanceResolver implements BindingResolver { @override T resolveSync([_]) { - if (_instance is T) return _instance as T; + if (_instance is T) return _instance; throw StateError( 'Instance $_instance is Future; ' 'use resolveAsync() instead', @@ -48,9 +48,9 @@ class InstanceResolver implements BindingResolver { @override Future resolveAsync([_]) { - if (_instance is Future) return _instance as Future; - if (_instance is T) return Future.value(_instance as T); - throw StateError('Unexpected instance type: $_instance'); + if (_instance is Future) return _instance; + + return Future.value(_instance); } @override From a74c34876d2f8cf45bd34b622abefc97587c7b1b Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Thu, 31 Jul 2025 14:26:59 +0300 Subject: [PATCH 3/5] feat(binding): add deprecated proxy async methods for backward compatibility and highlight transition to modern API --- cherrypick/lib/src/binding.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cherrypick/lib/src/binding.dart b/cherrypick/lib/src/binding.dart index a319d88..e1669fb 100644 --- a/cherrypick/lib/src/binding.dart +++ b/cherrypick/lib/src/binding.dart @@ -91,6 +91,21 @@ class Binding { return this; } + @Deprecated('Use toInstance instead of toInstanceAsync') + Binding toInstanceAsync(Instance value) { + return this.toInstance(value); + } + + @Deprecated('Use toProvide instead of toProvideAsync') + Binding toProvideAsync(Provider value) { + return this.toProvide(value); + } + + @Deprecated('Use toProvideWithParams instead of toProvideAsyncWithParams') + Binding toProvideAsyncWithParams(ProviderWithParams value) { + return this.toProvideWithParams(value); + } + /// RU: Инициализация экземляпяра  как сингелтон [value]. /// ENG: Initialization instance as a singelton [value]. /// From 63dae76ea91d0dbe558016770219d284b035ffdd Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Fri, 1 Aug 2025 08:47:55 +0300 Subject: [PATCH 4/5] Update benchmark results in README.md with latest timings --- benchmark_cherrypick/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmark_cherrypick/README.md b/benchmark_cherrypick/README.md index 219f88e..81fe8a2 100644 --- a/benchmark_cherrypick/README.md +++ b/benchmark_cherrypick/README.md @@ -17,12 +17,12 @@ All scenarios use the public API capabilities of cherrypick (scope, module, bind | Scenario | RunTime (μs) | |----------------------------------------------------|---------------| -| RegisterAndResolve | 0.3407 | -| ChainSingleton (A->B->C, singleton) | 0.3777 | -| ChainFactory (A->B->C, factory) | 0.9688 | -| NamedResolve (by name) | 0.3878 | -| AsyncChain (A->B->C, async) | 1.8006 | -| ScopeOverride (child overrides parent) | 0.3477 | +| RegisterAndResolve | 0.4574 | +| ChainSingleton (A->B->C, singleton) | 0.3759 | +| ChainFactory (A->B->C, factory) | 1.3783 | +| NamedResolve (by name) | 0.5193 | +| AsyncChain (A->B->C, async) | 0.5985 | +| ScopeOverride (child overrides parent) | 0.3611 | ## How to run From 7cc0743d942482402ff2b3a4a882ad4d81163d13 Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Fri, 1 Aug 2025 08:48:27 +0300 Subject: [PATCH 5/5] Update benchmark results in README.ru.md with latest timings (RU version) --- benchmark_cherrypick/README.ru.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmark_cherrypick/README.ru.md b/benchmark_cherrypick/README.ru.md index c47fd66..86a9539 100644 --- a/benchmark_cherrypick/README.ru.md +++ b/benchmark_cherrypick/README.ru.md @@ -17,12 +17,12 @@ | Сценарий | RunTime (мкс) | |----------------------------------------------------|--------------| -| RegisterAndResolve | 0.3407 | -| ChainSingleton (A->B->C, singleton) | 0.3777 | -| ChainFactory (A->B->C, factory) | 0.9688 | -| NamedResolve (by name) | 0.3878 | -| AsyncChain (A->B->C, async) | 1.8006 | -| ScopeOverride (child overrides parent) | 0.3477 | +| RegisterAndResolve | 0.4574 | +| ChainSingleton (A->B->C, singleton) | 0.3759 | +| ChainFactory (A->B->C, factory) | 1.3783 | +| NamedResolve (by name) | 0.5193 | +| AsyncChain (A->B->C, async) | 0.5985 | +| ScopeOverride (child overrides parent) | 0.3611 | ## Как запускать