refactor: added variadic parameter support
This commit is contained in:
parent
433da78695
commit
dee0cbd03a
5 changed files with 252 additions and 14 deletions
|
@ -1072,16 +1072,24 @@ class Container {
|
||||||
|
|
||||||
// Resolve each parameter
|
// Resolve each parameter
|
||||||
for (var param in methodParams) {
|
for (var param in methodParams) {
|
||||||
// If a value was provided for this parameter position, use it
|
// Handle variadic parameters
|
||||||
if (paramIndex < parameters.length) {
|
if (param.isVariadic) {
|
||||||
var value = parameters[paramIndex++];
|
// Collect all remaining parameters into a list
|
||||||
// If null was provided and we can resolve from container, do so
|
var variadicArgs = parameters.skip(paramIndex).toList();
|
||||||
if (value == null && has(param.type.reflectedType)) {
|
resolvedParams.add(variadicArgs);
|
||||||
resolvedParams.add(make(param.type.reflectedType));
|
break; // Variadic parameter must be last
|
||||||
} else {
|
} else {
|
||||||
resolvedParams.add(value);
|
// If a value was provided for this parameter position, use it
|
||||||
|
if (paramIndex < parameters.length) {
|
||||||
|
var value = parameters[paramIndex++];
|
||||||
|
// If null was provided and we can resolve from container, do so
|
||||||
|
if (value == null && has(param.type.reflectedType)) {
|
||||||
|
resolvedParams.add(make(param.type.reflectedType));
|
||||||
|
} else {
|
||||||
|
resolvedParams.add(value);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise try to resolve from container
|
// Otherwise try to resolve from container
|
||||||
|
|
|
@ -284,13 +284,15 @@ class ReflectedParameter {
|
||||||
final ReflectedType type;
|
final ReflectedType type;
|
||||||
final bool isRequired;
|
final bool isRequired;
|
||||||
final bool isNamed;
|
final bool isNamed;
|
||||||
|
final bool isVariadic;
|
||||||
|
|
||||||
const ReflectedParameter(
|
const ReflectedParameter(
|
||||||
this.name, this.annotations, this.type, this.isRequired, this.isNamed);
|
this.name, this.annotations, this.type, this.isRequired, this.isNamed,
|
||||||
|
{this.isVariadic = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
hashObjects([name, annotations, type, isRequired, isNamed]);
|
hashObjects([name, annotations, type, isRequired, isNamed, isVariadic]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) =>
|
bool operator ==(other) =>
|
||||||
|
@ -300,7 +302,8 @@ class ReflectedParameter {
|
||||||
.equals(other.annotations, annotations) &&
|
.equals(other.annotations, annotations) &&
|
||||||
other.type == type &&
|
other.type == type &&
|
||||||
other.isRequired == isRequired &&
|
other.isRequired == isRequired &&
|
||||||
other.isNamed == isNamed;
|
other.isNamed == isNamed &&
|
||||||
|
other.isVariadic == isVariadic;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReflectedTypeParameter {
|
class ReflectedTypeParameter {
|
||||||
|
|
|
@ -112,9 +112,11 @@ class MockParameter implements ReflectedParameter {
|
||||||
final bool isRequired;
|
final bool isRequired;
|
||||||
@override
|
@override
|
||||||
final bool isNamed;
|
final bool isNamed;
|
||||||
|
final bool isVariadic;
|
||||||
final Type paramType;
|
final Type paramType;
|
||||||
|
|
||||||
MockParameter(this.name, this.paramType, this.isRequired, this.isNamed);
|
MockParameter(this.name, this.paramType, this.isRequired, this.isNamed,
|
||||||
|
{this.isVariadic = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ReflectedInstance> get annotations => [];
|
List<ReflectedInstance> get annotations => [];
|
||||||
|
|
|
@ -117,9 +117,11 @@ class MockParameter implements ReflectedParameter {
|
||||||
final bool isRequired;
|
final bool isRequired;
|
||||||
@override
|
@override
|
||||||
final bool isNamed;
|
final bool isNamed;
|
||||||
|
final bool isVariadic;
|
||||||
final Type paramType;
|
final Type paramType;
|
||||||
|
|
||||||
MockParameter(this.name, this.paramType, this.isRequired, this.isNamed);
|
MockParameter(this.name, this.paramType, this.isRequired, this.isNamed,
|
||||||
|
{this.isVariadic = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ReflectedInstance> get annotations => [];
|
List<ReflectedInstance> get annotations => [];
|
||||||
|
|
223
incubation/container/container/test/variadic_parameter_test.dart
Normal file
223
incubation/container/container/test/variadic_parameter_test.dart
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
import 'package:platformed_container/container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
String log(String prefix, String message) => '$prefix: $message';
|
||||||
|
String logMany(String prefix, List<String> messages) =>
|
||||||
|
'$prefix: ${messages.join(", ")}';
|
||||||
|
String format(String message, {List<String> tags = const []}) =>
|
||||||
|
'$message [${tags.join(", ")}]';
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflector extends Reflector {
|
||||||
|
@override
|
||||||
|
String? getName(Symbol symbol) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass? reflectClass(Type clazz) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? reflectType(Type type) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance? reflectInstance(Object? instance) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? reflectFunction(Function function) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType reflectFutureOf(Type type) => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Type? findTypeByName(String name) {
|
||||||
|
if (name == 'Logger') return Logger;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedFunction? findInstanceMethod(Object instance, String methodName) {
|
||||||
|
if (instance is Logger) {
|
||||||
|
switch (methodName) {
|
||||||
|
case 'log':
|
||||||
|
return MockMethod('log', (invocation) {
|
||||||
|
var args = invocation.positionalArguments;
|
||||||
|
if (args.length < 2) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Method log requires prefix and message parameters');
|
||||||
|
}
|
||||||
|
return MockReflectedInstance(
|
||||||
|
instance.log(args[0] as String, args[1] as String));
|
||||||
|
}, [
|
||||||
|
MockParameter('prefix', String, true, false),
|
||||||
|
MockParameter('message', String, true, false)
|
||||||
|
]);
|
||||||
|
case 'logMany':
|
||||||
|
return MockMethod('logMany', (invocation) {
|
||||||
|
var args = invocation.positionalArguments;
|
||||||
|
if (args.isEmpty) {
|
||||||
|
throw ArgumentError('Method logMany requires a prefix parameter');
|
||||||
|
}
|
||||||
|
var prefix = args[0] as String;
|
||||||
|
var messages = args.length > 1
|
||||||
|
? args.skip(1).map((e) => e.toString()).toList()
|
||||||
|
: <String>[];
|
||||||
|
return MockReflectedInstance(instance.logMany(prefix, messages));
|
||||||
|
}, [
|
||||||
|
MockParameter('prefix', String, true, false),
|
||||||
|
MockParameter('messages', List<String>, true, false,
|
||||||
|
isVariadic: true)
|
||||||
|
]);
|
||||||
|
case 'format':
|
||||||
|
return MockMethod('format', (invocation) {
|
||||||
|
var args = invocation.positionalArguments;
|
||||||
|
var namedArgs = invocation.namedArguments;
|
||||||
|
if (args.isEmpty) {
|
||||||
|
throw ArgumentError('Method format requires a message parameter');
|
||||||
|
}
|
||||||
|
var tags = (namedArgs[#tags] as List?)
|
||||||
|
?.map((e) => e.toString())
|
||||||
|
.toList() ??
|
||||||
|
const <String>[];
|
||||||
|
return MockReflectedInstance(
|
||||||
|
instance.format(args[0] as String, tags: tags));
|
||||||
|
}, [
|
||||||
|
MockParameter('message', String, true, false),
|
||||||
|
MockParameter('tags', List<String>, false, true, isVariadic: true)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockMethod implements ReflectedFunction {
|
||||||
|
final String methodName;
|
||||||
|
final ReflectedInstance Function(Invocation) handler;
|
||||||
|
final List<ReflectedParameter> _parameters;
|
||||||
|
|
||||||
|
MockMethod(this.methodName, this.handler,
|
||||||
|
[List<ReflectedParameter>? parameters])
|
||||||
|
: _parameters = parameters ?? [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedInstance> get annotations => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isGetter => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isSetter => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => methodName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedParameter> get parameters => _parameters;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType? get returnType => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedTypeParameter> get typeParameters => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance invoke(Invocation invocation) => handler(invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockParameter implements ReflectedParameter {
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final bool isRequired;
|
||||||
|
@override
|
||||||
|
final bool isNamed;
|
||||||
|
final Type paramType;
|
||||||
|
final bool isVariadic;
|
||||||
|
|
||||||
|
MockParameter(this.name, this.paramType, this.isRequired, this.isNamed,
|
||||||
|
{this.isVariadic = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedInstance> get annotations => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType get type => MockReflectedType(paramType);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedType implements ReflectedType {
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final Type reflectedType;
|
||||||
|
|
||||||
|
MockReflectedType(this.reflectedType) : name = reflectedType.toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ReflectedTypeParameter> get typeParameters => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAssignableTo(ReflectedType? other) => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance newInstance(
|
||||||
|
String constructorName, List positionalArguments,
|
||||||
|
[Map<String, dynamic> namedArguments = const {},
|
||||||
|
List<Type> typeArguments = const []]) =>
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockReflectedInstance implements ReflectedInstance {
|
||||||
|
final dynamic value;
|
||||||
|
|
||||||
|
MockReflectedInstance(this.value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedClass get clazz => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance getField(String name) => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
dynamic get reflectee => value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedType get type => throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = Container(MockReflector());
|
||||||
|
container.registerSingleton(Logger());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Variadic Parameter Tests', () {
|
||||||
|
test('can call method with variadic positional parameters', () {
|
||||||
|
var result = container.call('Logger@logMany',
|
||||||
|
['INFO', 'first message', 'second message', 'third message']);
|
||||||
|
expect(result,
|
||||||
|
equals('INFO: [first message, second message, third message]'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can call method with variadic named parameters', () {
|
||||||
|
var result = container.call('Logger@format', [
|
||||||
|
'Hello world'
|
||||||
|
], {
|
||||||
|
#tags: ['info', 'debug', 'test']
|
||||||
|
});
|
||||||
|
expect(result, equals('Hello world [info, debug, test]'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('variadic parameters are optional', () {
|
||||||
|
var result = container.call('Logger@format', ['Hello world']);
|
||||||
|
expect(result, equals('Hello world []'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can mix regular and variadic parameters', () {
|
||||||
|
var result =
|
||||||
|
container.call('Logger@logMany', ['DEBUG', 'single message']);
|
||||||
|
expect(result, equals('DEBUG: [single message]'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue