writeHostApi method
void
writeHostApi(
- CppOptions generatorOptions,
- Root root,
- Indent indent,
- AstHostApi api, {
- required String dartPackageName,
override
Writes a single Host Api to indent
.
Implementation
@override
void writeHostApi(
CppOptions generatorOptions,
Root root,
Indent indent,
AstHostApi api, {
required String dartPackageName,
}) {
if (getCodecClasses(api, root).isNotEmpty) {
_writeCodec(generatorOptions, root, indent, api);
}
final String codeSerializerName = getCodecClasses(api, root).isNotEmpty
? _getCodecSerializerName(api)
: _defaultCodecSerializer;
indent.writeln('/// The codec used by ${api.name}.');
_writeFunctionDefinition(indent, 'GetCodec',
scope: api.name,
returnType: 'const flutter::StandardMessageCodec&', body: () {
indent.writeln(
'return flutter::StandardMessageCodec::GetInstance(&$codeSerializerName::GetInstance());');
});
indent.writeln(
'$_commentPrefix Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`.');
_writeFunctionDefinition(
indent,
'SetUp',
scope: api.name,
returnType: _voidType,
parameters: <String>[
'flutter::BinaryMessenger* binary_messenger',
'${api.name}* api',
],
body: () {
indent.writeln('${api.name}::SetUp(binary_messenger, api, "");');
},
);
_writeFunctionDefinition(indent, 'SetUp',
scope: api.name,
returnType: _voidType,
parameters: <String>[
'flutter::BinaryMessenger* binary_messenger',
'${api.name}* api',
'const std::string& message_channel_suffix',
], body: () {
indent.writeln(
'const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "";');
for (final Method method in api.methods) {
final String channelName =
makeChannelName(api, method, dartPackageName);
indent.writeScoped('{', '}', () {
indent.writeln('BasicMessageChannel<> channel(binary_messenger, '
'"$channelName" + prepended_suffix, &GetCodec());');
indent.writeScoped('if (api != nullptr) {', '} else {', () {
indent.write(
'channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) ');
indent.addScoped('{', '});', () {
indent.writeScoped('try {', '}', () {
final List<String> methodArgument = <String>[];
if (method.parameters.isNotEmpty) {
indent.writeln(
'const auto& args = std::get<EncodableList>(message);');
enumerate(method.parameters, (int index, NamedType arg) {
final HostDatatype hostType = getHostDatatype(
arg.type,
(TypeDeclaration x) =>
_shortBaseCppTypeForBuiltinDartType(x));
final String argName = _getSafeArgumentName(index, arg);
final String encodableArgName =
'${_encodablePrefix}_$argName';
indent.writeln(
'const auto& $encodableArgName = args.at($index);');
if (!arg.type.isNullable) {
indent.writeScoped(
'if ($encodableArgName.IsNull()) {', '}', () {
indent.writeln(
'reply(WrapError("$argName unexpectedly null."));');
indent.writeln('return;');
});
}
_writeEncodableValueArgumentUnwrapping(
indent,
root,
hostType,
argName: argName,
encodableArgName: encodableArgName,
apiType: ApiType.host,
);
final String unwrapEnum =
arg.type.isEnum && arg.type.isNullable
? ' ? &(*$argName) : nullptr'
: '';
methodArgument.add('$argName$unwrapEnum');
});
}
final HostDatatype returnType = getHostDatatype(
method.returnType, _shortBaseCppTypeForBuiltinDartType);
final String returnTypeName = _hostApiReturnType(returnType);
if (method.isAsynchronous) {
methodArgument.add(
'[reply]($returnTypeName&& output) {${indent.newline}'
'${_wrapResponse(indent, root, method.returnType, prefix: '\t')}${indent.newline}'
'}',
);
}
final String call =
'api->${_makeMethodName(method)}(${methodArgument.join(', ')})';
if (method.isAsynchronous) {
indent.format('$call;');
} else {
indent.writeln('$returnTypeName output = $call;');
indent.format(_wrapResponse(indent, root, method.returnType));
}
}, addTrailingNewline: false);
indent.add(' catch (const std::exception& exception) ');
indent.addScoped('{', '}', () {
// There is a potential here for `reply` to be called twice, which
// is a violation of the API contract, because there's no way of
// knowing whether or not the plugin code called `reply` before
// throwing. Since use of `@async` suggests that the reply is
// probably not sent within the scope of the stack, err on the
// side of potential double-call rather than no call (which is
// also an API violation) so that unexpected errors have a better
// chance of being caught and handled in a useful way.
indent.writeln('reply(WrapError(exception.what()));');
});
});
});
indent.addScoped(null, '}', () {
indent.writeln('channel.SetMessageHandler(nullptr);');
});
});
}
});
_writeFunctionDefinition(indent, 'WrapError',
scope: api.name,
returnType: 'EncodableValue',
parameters: <String>['std::string_view error_message'], body: () {
indent.format('''
return EncodableValue(EncodableList{
\tEncodableValue(std::string(error_message)),
\tEncodableValue("Error"),
\tEncodableValue()
});''');
});
_writeFunctionDefinition(indent, 'WrapError',
scope: api.name,
returnType: 'EncodableValue',
parameters: <String>['const FlutterError& error'], body: () {
indent.format('''
return EncodableValue(EncodableList{
\tEncodableValue(error.code()),
\tEncodableValue(error.message()),
\terror.details()
});''');
});
}