mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-23 21:13:35 +00:00
feat: add Riverpod adapter, async-chain support via FutureProvider, full DI CLI/bench integration, benchmarking, ascii performance graphs in markdown
This commit is contained in:
@@ -12,6 +12,7 @@ import 'package:benchmark_di/benchmarks/universal_chain_benchmark.dart';
|
||||
import 'package:benchmark_di/benchmarks/universal_chain_async_benchmark.dart';
|
||||
import 'package:benchmark_di/di_adapters/cherrypick_adapter.dart';
|
||||
import 'package:benchmark_di/di_adapters/get_it_adapter.dart';
|
||||
import 'package:benchmark_di/di_adapters/riverpod_adapter.dart';
|
||||
|
||||
/// Command-line interface (CLI) runner for benchmarks.
|
||||
///
|
||||
@@ -29,7 +30,11 @@ class BenchmarkCliRunner {
|
||||
for (final c in config.chainCounts) {
|
||||
for (final d in config.nestDepths) {
|
||||
BenchmarkResult benchResult;
|
||||
final di = config.di == 'getit' ? GetItAdapter() : CherrypickDIAdapter();
|
||||
final di = config.di == 'getit'
|
||||
? GetItAdapter()
|
||||
: config.di == 'riverpod'
|
||||
? RiverpodAdapter()
|
||||
: CherrypickDIAdapter();
|
||||
if (scenario == UniversalScenario.asyncChain) {
|
||||
final benchAsync = UniversalChainAsyncBenchmark(di,
|
||||
chainCount: c, nestingDepth: d, mode: mode,
|
||||
|
||||
@@ -104,7 +104,7 @@ BenchmarkCliConfig parseBenchmarkCli(List<String> args) {
|
||||
..addOption('repeat', abbr: 'r', defaultsTo: '2')
|
||||
..addOption('warmup', abbr: 'w', defaultsTo: '1')
|
||||
..addOption('format', abbr: 'f', defaultsTo: 'pretty')
|
||||
..addOption('di', defaultsTo: 'cherrypick', help: 'DI implementation: cherrypick or getit')
|
||||
..addOption('di', defaultsTo: 'cherrypick', help: 'DI implementation: cherrypick, getit or riverpod')
|
||||
..addFlag('help', abbr: 'h', negatable: false, help: 'Show help');
|
||||
final result = parser.parse(args);
|
||||
if (result['help'] == true) {
|
||||
|
||||
72
benchmark_di/lib/di_adapters/riverpod_adapter.dart
Normal file
72
benchmark_di/lib/di_adapters/riverpod_adapter.dart
Normal file
@@ -0,0 +1,72 @@
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'di_adapter.dart';
|
||||
|
||||
/// RiverpodAdapter реализует DIAdapter для универсального бенчмарка через Riverpod.
|
||||
class RiverpodAdapter implements DIAdapter {
|
||||
late ProviderContainer _container;
|
||||
late final Map<String?, ProviderBase<Object?>> _namedProviders;
|
||||
final ProviderContainer? _parent;
|
||||
|
||||
// Основной конструктор
|
||||
RiverpodAdapter() : _parent = null {
|
||||
_namedProviders = <String?, ProviderBase<Object?>>{};
|
||||
}
|
||||
|
||||
// Внутренний конструктор для дочерних скоупов
|
||||
RiverpodAdapter._child(this._container, this._namedProviders, this._parent);
|
||||
|
||||
@override
|
||||
void setupDependencies(void Function(dynamic container) registration) {
|
||||
// Для главного контейнера
|
||||
_container = _parent == null
|
||||
? ProviderContainer()
|
||||
: ProviderContainer(parent: _parent);
|
||||
registration(_namedProviders);
|
||||
}
|
||||
|
||||
/// Регистрировать провайдеры нужно по имени-сервису.
|
||||
/// Пример: container['SomeClass'] = Provider((ref) => SomeClass());
|
||||
|
||||
@override
|
||||
T resolve<T extends Object>({String? named}) {
|
||||
final provider = _namedProviders[named ?? T.toString()];
|
||||
if (provider == null) {
|
||||
throw Exception('Provider not found for $named');
|
||||
}
|
||||
return _container.read(provider) as T;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<T> resolveAsync<T extends Object>({String? named}) async {
|
||||
final provider = _namedProviders[named ?? T.toString()];
|
||||
if (provider == null) {
|
||||
throw Exception('Provider not found for $named');
|
||||
}
|
||||
// Если это FutureProvider — используем .future
|
||||
if (provider.runtimeType.toString().contains('FutureProvider')) {
|
||||
final result = await _container.read((provider as dynamic).future);
|
||||
return result as T;
|
||||
}
|
||||
return resolve<T>(named: named);
|
||||
}
|
||||
|
||||
@override
|
||||
void teardown() {
|
||||
_container.dispose();
|
||||
_namedProviders.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
DIAdapter openSubScope(String name) {
|
||||
// Создаём дочерний scope через новый контейнер с parent
|
||||
final childContainer = ProviderContainer(parent: _container);
|
||||
// Провайдеры будут унаследованы (immutable копия), но при желании можно их расширять в дочернем scope.
|
||||
return RiverpodAdapter._child(childContainer, Map.of(_namedProviders), _container);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> waitForAsyncReady() async {
|
||||
// Riverpod синхронный по умолчанию.
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import '../di_adapters/di_adapter.dart';
|
||||
import '../di_adapters/cherrypick_adapter.dart';
|
||||
import '../di_adapters/get_it_adapter.dart';
|
||||
import 'universal_chain_module.dart';
|
||||
import 'universal_service.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:riverpod/riverpod.dart' as rp;
|
||||
|
||||
/// Возвращает универсальную функцию регистрации зависимостей,
|
||||
/// подходящую под выбранный DI-адаптер.
|
||||
@@ -41,7 +39,7 @@ void Function(dynamic) getUniversalRegistration(
|
||||
final prev = level > 1
|
||||
? await getIt.getAsync<UniversalService>(instanceName: prevDepName)
|
||||
: null;
|
||||
return UniversalServiceImpl(value: depName, dependency: prev);
|
||||
return UniversalServiceImpl(value: depName, dependency: prev as UniversalService?);
|
||||
},
|
||||
instanceName: depName,
|
||||
);
|
||||
@@ -112,5 +110,62 @@ void Function(dynamic) getUniversalRegistration(
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Riverpod
|
||||
if (adapter.runtimeType.toString().contains('RiverpodAdapter')) {
|
||||
// Регистрация Provider-ов по универсальному сценарию
|
||||
return (providers) {
|
||||
// providers это Map<String, ProviderBase<Object?>>
|
||||
switch (scenario) {
|
||||
case UniversalScenario.register:
|
||||
providers['UniversalService'] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(value: 'reg', dependency: null));
|
||||
break;
|
||||
case UniversalScenario.named:
|
||||
providers['impl1'] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(value: 'impl1'));
|
||||
providers['impl2'] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(value: 'impl2'));
|
||||
break;
|
||||
case UniversalScenario.chain:
|
||||
for (int chain = 1; chain <= chainCount; chain++) {
|
||||
for (int level = 1; level <= nestingDepth; level++) {
|
||||
final prevDepName = '${chain}_${level - 1}';
|
||||
final depName = '${chain}_$level';
|
||||
providers[depName] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(
|
||||
value: depName,
|
||||
dependency: level > 1 ? ref.watch(providers[prevDepName] as rp.ProviderBase<UniversalService>) : null,
|
||||
));
|
||||
}
|
||||
}
|
||||
// Alias для последнего (универсальное имя)
|
||||
final depName = '${chainCount}_$nestingDepth';
|
||||
providers['UniversalService'] = rp.Provider<UniversalService>((ref) => ref.watch(providers[depName] as rp.ProviderBase<UniversalService>));
|
||||
break;
|
||||
case UniversalScenario.override:
|
||||
// handled at benchmark level
|
||||
break;
|
||||
case UniversalScenario.asyncChain:
|
||||
for (int chain = 1; chain <= chainCount; chain++) {
|
||||
for (int level = 1; level <= nestingDepth; level++) {
|
||||
final prevDepName = '${chain}_${level - 1}';
|
||||
final depName = '${chain}_$level';
|
||||
providers[depName] = rp.FutureProvider<UniversalService>((ref) async {
|
||||
return UniversalServiceImpl(
|
||||
value: depName,
|
||||
dependency: level > 1
|
||||
? await ref.watch(providers[prevDepName]!.future) as UniversalService?
|
||||
: null,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Alias для последнего (универсальное имя)
|
||||
final depName = '${chainCount}_$nestingDepth';
|
||||
providers['UniversalService'] = rp.FutureProvider<UniversalService>((ref) async {
|
||||
return await ref.watch(providers[depName]!.future) as UniversalService;
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
throw UnsupportedError('Unknown DIAdapter type: ${adapter.runtimeType}');
|
||||
}
|
||||
|
||||
@@ -127,14 +127,14 @@ class UniversalChainModule extends Module {
|
||||
}
|
||||
}
|
||||
// Регистрация алиаса без имени (на последний элемент цепочки)
|
||||
final depName = '${chainCount}_${nestingDepth}';
|
||||
final depName = '${chainCount}_$nestingDepth';
|
||||
bind<UniversalService>()
|
||||
.toProvide(() => currentScope.resolve<UniversalService>(named: depName))
|
||||
.singleton();
|
||||
break;
|
||||
case UniversalScenario.override:
|
||||
// handled at benchmark level, но алиас нужен прямо в этом scope!
|
||||
final depName = '${chainCount}_${nestingDepth}';
|
||||
final depName = '${chainCount}_$nestingDepth';
|
||||
bind<UniversalService>()
|
||||
.toProvide(() => currentScope.resolve<UniversalService>(named: depName))
|
||||
.singleton();
|
||||
|
||||
@@ -96,5 +96,37 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
riverpod:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: riverpod
|
||||
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
state_notifier:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: state_notifier
|
||||
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
sdks:
|
||||
dart: ">=3.6.0 <4.0.0"
|
||||
|
||||
@@ -11,6 +11,7 @@ dependencies:
|
||||
path: ../cherrypick
|
||||
args: ^2.7.0
|
||||
get_it: ^8.2.0
|
||||
riverpod: ^2.6.1
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^5.0.0
|
||||
|
||||
Reference in New Issue
Block a user