createArgForParam function
Expression
createArgForParam(
- BaseParameterAnnotation annotation,
- ServerParam param, {
- String routePath = '',
Implementation
Expression createArgForParam(
BaseParameterAnnotation annotation,
ServerParam param, {
String routePath = '',
}) {
if (isByteStreamBodyParam(param) && annotation.type == AnnotationType.body) {
if (byteStreamBodyHasAccess(annotation)) {
throw ArgumentError(
'@Body access is not supported for Stream<List<int>> parameters',
);
}
final stream = createOriginalPayloadStreamVar();
if (annotation case HasPipe(:final pipe?)) {
return createPipe(
pipe,
param: param,
annotation: annotation,
access: stream,
);
}
return stream;
}
var variable = switch (annotation.type) {
AnnotationType.body => createBodyVar(annotation),
AnnotationType.query ||
AnnotationType.queryAll => createQueryVar(annotation, param),
AnnotationType.cookie => createCookieVar(annotation, param),
AnnotationType.param => createParamVar(
annotation,
param,
routePath: routePath,
),
AnnotationType.header ||
AnnotationType.headerAll => createHeaderVar(annotation, param),
AnnotationType.ip => createIpVar(),
AnnotationType.binds => createBindsVar(annotation, param),
AnnotationType.data => createDataVar(param),
};
if (annotation.type
case AnnotationType.headerAll || AnnotationType.queryAll) {
variable = switch (param.type.iterableType) {
IterableType.list => variable.nullSafeProperty('toList').call([]),
IterableType.set => variable.nullSafeProperty('toSet').call([]),
IterableType.iterable => variable,
null => variable,
};
}
if (annotation case HasPipe(:final pipe?)) {
return createPipe(
pipe,
param: param,
annotation: annotation,
access: variable,
);
}
final fromJson = switch (param.annotations.data) {
true => refer('data'),
false => createFromJson(param.type, refer('data')),
};
if (param.type.isDynamic) {
return fromJson ?? variable;
}
final rawType = switch (param.annotations.data) {
true => param.type.nonNullName,
false => getRawType(param.type).replaceAll('?', ''),
};
// `rawType` is the **wire** type, so a custom type deserialized from a
// string (`StringUser.fromJson(String)`) also reports `String` here. The
// coercion arms below must therefore still run the value through fromJson;
// handing back the bare `data.toString()` typed the whole switch as
// `Object` and the generated file did not compile.
final stringified = refer('data').property('toString').call([]);
final stringifiedValue = switch (param.annotations.data) {
true => stringified,
false => createFromJson(param.type, stringified) ?? stringified,
};
// Coerced query/header multi-values are `List<dynamic>` / `Set<dynamic>`.
// JSON bodies decode sets as `List`. A pattern of `List<String>` / `Set`
// never matches those at runtime, so match the unspecialized iterable and
// let [createPromotedArgValue] cast / toSet elements.
final patternType = switch (rawType) {
final type when type.startsWith('List') => 'List',
// JSON has no Set; decoded arrays are List. Iterable matches both.
final type when type.startsWith('Set') => 'Iterable',
final type when type.startsWith('Iterable') => 'Iterable',
_ => rawType,
};
return createSwitchPattern(variable, {
Block.of([
declareFinal('data', type: refer(patternType)).code,
if (rawType.startsWith('Map')) ...[
const Code('when'),
refer('data').property('isNotEmpty').code,
],
]): createPromotedArgValue(
paramType: param.type,
fromJson: fromJson,
),
// Query/header values are coerced (e.g. "5" → 5, "true" → true). When the
// parameter type is String, accept those primitives and stringify them
// instead of throwing MissingArgumentException. Do not match arbitrary
// Object (e.g. empty JSON Map `{}`) — that should fall through to the
// default / missing-argument arms.
if (rawType == 'String') ...{
Block.of([declareFinal('data', type: refer('num')).code]):
stringifiedValue,
Block.of([declareFinal('data', type: refer('bool')).code]):
stringifiedValue,
},
// Coerced ints should satisfy double parameters (`?n=5` → 5).
if (rawType == 'double')
Block.of([declareFinal('data', type: refer('num')).code]): refer(
'data',
).property('toDouble').call([]),
if (param.defaultValue case final defaultValue?)
const Code('_'): CodeExpression(Code(defaultValue))
else ...{
if (param.type.isNullable)
Block.of([
literalNull.code,
if (rawType.startsWith('Map')) ...[
const Code('||'),
const Code('Map()'),
] else if (rawType.startsWith('List')) ...[
const Code('||'),
const Code('List()'),
],
]): literalNull,
Block.of([
declareFinal('mismatched', type: refer('Object?')).code,
]): createMissingArgumentException(
key: annotation.name ?? param.name,
location: annotation.type.location,
expectedType: param.type.nonNullName,
actualType: actualTypeOf(refer('mismatched')),
).thrown,
},
});
}