writeHostApi method
void
writeHostApi(
- GObjectOptions generatorOptions,
- Root root,
- Indent indent,
- Api api, {
- required String dartPackageName,
override
Writes a single Host Api to indent
.
Implementation
@override
void writeHostApi(
GObjectOptions generatorOptions,
Root root,
Indent indent,
Api api, {
required String dartPackageName,
}) {
final String module = _getModule(generatorOptions, dartPackageName);
final String className = _getClassName(module, api.name);
final String methodPrefix = _getMethodPrefix(module, api.name);
final String vtableName = _getVTableName(module, api.name);
final String codecClassName = _getClassName(module, _codecBaseName);
final String codecMethodPrefix = _getMethodPrefix(module, _codecBaseName);
final bool hasAsyncMethod =
api.methods.any((Method method) => method.isAsynchronous);
if (hasAsyncMethod) {
indent.newln();
_writeObjectStruct(indent, module, '${api.name}ResponseHandle', () {
indent.writeln('FlBasicMessageChannel* channel;');
indent.writeln('FlBasicMessageChannelResponseHandle* response_handle;');
});
indent.newln();
_writeDefineType(indent, module, '${api.name}ResponseHandle');
indent.newln();
_writeDispose(indent, module, '${api.name}ResponseHandle', () {
_writeCastSelf(indent, module, '${api.name}ResponseHandle', 'object');
indent.writeln('g_clear_object(&self->channel);');
indent.writeln('g_clear_object(&self->response_handle);');
});
indent.newln();
_writeInit(indent, module, '${api.name}ResponseHandle', () {});
indent.newln();
_writeClassInit(indent, module, '${api.name}ResponseHandle', () {});
indent.newln();
indent.writeScoped(
'static ${className}ResponseHandle* ${methodPrefix}_response_handle_new(FlBasicMessageChannel* channel, FlBasicMessageChannelResponseHandle* response_handle) {',
'}', () {
_writeObjectNew(indent, module, '${api.name}ResponseHandle');
indent.writeln(
'self->channel = FL_BASIC_MESSAGE_CHANNEL(g_object_ref(channel));');
indent.writeln(
'self->response_handle = FL_BASIC_MESSAGE_CHANNEL_RESPONSE_HANDLE(g_object_ref(response_handle));');
indent.writeln('return self;');
});
}
for (final Method method in api.methods) {
final String responseName = _getResponseName(api.name, method.name);
final String responseClassName = _getClassName(module, responseName);
final String responseMethodPrefix =
_getMethodPrefix(module, responseName);
if (method.isAsynchronous) {
indent.newln();
_writeDeclareFinalType(indent, module, responseName);
}
indent.newln();
_writeObjectStruct(indent, module, responseName, () {
indent.writeln('FlValue* value;');
});
indent.newln();
_writeDefineType(indent, module, responseName);
indent.newln();
_writeDispose(indent, module, responseName, () {
_writeCastSelf(indent, module, responseName, 'object');
indent.writeln('g_clear_pointer(&self->value, fl_value_unref);');
});
indent.newln();
_writeInit(indent, module, responseName, () {});
indent.newln();
_writeClassInit(indent, module, responseName, () {});
final String returnType = _getType(module, method.returnType);
indent.newln();
final List<String> constructorArgs = <String>[
if (returnType != 'void') '$returnType return_value',
if (_isNumericListType(method.returnType)) 'size_t return_value_length'
];
indent.writeScoped(
"${method.isAsynchronous ? 'static ' : ''}$responseClassName* ${responseMethodPrefix}_new(${constructorArgs.join(', ')}) {",
'}', () {
_writeObjectNew(indent, module, responseName);
indent.writeln('self->value = fl_value_new_list();');
indent.writeln(
"fl_value_append_take(self->value, ${_makeFlValue(root, module, method.returnType, 'return_value', lengthVariableName: 'return_value_length')});");
indent.writeln('return self;');
});
indent.newln();
indent.writeScoped(
'${method.isAsynchronous ? 'static ' : ''}$responseClassName* ${responseMethodPrefix}_new_error(const gchar* code, const gchar* message, FlValue* details) {',
'}', () {
_writeObjectNew(indent, module, responseName);
indent.writeln('self->value = fl_value_new_list();');
indent.writeln(
'fl_value_append_take(self->value, fl_value_new_string(code));');
indent.writeln(
'fl_value_append_take(self->value, fl_value_new_string(message != nullptr ? message : ""));');
indent.writeln(
'fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) : fl_value_new_null());');
indent.writeln('return self;');
});
}
indent.newln();
_writeObjectStruct(indent, module, api.name, () {
indent.writeln('const ${className}VTable* vtable;');
indent.writeln('gpointer user_data;');
indent.writeln('GDestroyNotify user_data_free_func;');
});
indent.newln();
_writeDefineType(indent, module, api.name);
indent.newln();
_writeDispose(indent, module, api.name, () {
_writeCastSelf(indent, module, api.name, 'object');
indent.writeScoped('if (self->user_data != nullptr) {', '}', () {
indent.writeln('self->user_data_free_func(self->user_data);');
});
indent.writeln('self->user_data = nullptr;');
});
indent.newln();
_writeInit(indent, module, api.name, () {});
indent.newln();
_writeClassInit(indent, module, api.name, () {});
indent.newln();
indent.writeScoped(
'static $className* ${methodPrefix}_new(const $vtableName* vtable, gpointer user_data, GDestroyNotify user_data_free_func) {',
'}', () {
_writeObjectNew(indent, module, api.name);
indent.writeln('self->vtable = vtable;');
indent.writeln('self->user_data = user_data;');
indent.writeln('self->user_data_free_func = user_data_free_func;');
indent.writeln('return self;');
});
for (final Method method in api.methods) {
final String methodName = _getMethodName(method.name);
final String responseName = _getResponseName(api.name, method.name);
final String responseClassName = _getClassName(module, responseName);
indent.newln();
indent.writeScoped(
'static void ${methodPrefix}_${methodName}_cb(FlBasicMessageChannel* channel, FlValue* message_, FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) {',
'}', () {
_writeCastSelf(indent, module, api.name, 'user_data');
indent.newln();
indent.writeScoped(
'if (self->vtable == nullptr || self->vtable->$methodName == nullptr) {',
'}', () {
indent.writeln('return;');
});
indent.newln();
final List<String> methodArgs = <String>[];
for (int i = 0; i < method.parameters.length; i++) {
final Parameter param = method.parameters[i];
final String paramName = _snakeCaseFromCamelCase(param.name);
final String paramType = _getType(module, param.type);
indent.writeln(
'FlValue* value$i = fl_value_get_list_value(message_, $i);');
if (_isNullablePrimitiveType(param.type)) {
final String primitiveType =
_getType(module, param.type, primitive: true);
indent.writeln('$paramType $paramName = nullptr;');
indent.writeln('$primitiveType ${paramName}_value;');
indent.writeScoped(
'if (fl_value_get_type(value$i) != FL_VALUE_TYPE_NULL) {', '}',
() {
final String paramValue =
_fromFlValue(module, method.parameters[i].type, 'value$i');
indent.writeln('${paramName}_value = $paramValue;');
indent.writeln('$paramName = &${paramName}_value;');
});
} else {
final String paramValue =
_fromFlValue(module, method.parameters[i].type, 'value$i');
indent.writeln('$paramType $paramName = $paramValue;');
}
methodArgs.add(paramName);
if (_isNumericListType(method.parameters[i].type)) {
indent.writeln(
'size_t ${paramName}_length = fl_value_get_length(value$i);');
methodArgs.add('${paramName}_length');
}
}
if (method.isAsynchronous) {
final List<String> vfuncArgs = <String>[];
vfuncArgs.addAll(methodArgs);
vfuncArgs.addAll(<String>['handle', 'self->user_data']);
indent.writeln(
'g_autoptr(${className}ResponseHandle) handle = ${methodPrefix}_response_handle_new(channel, response_handle);');
indent.writeln("self->vtable->$methodName(${vfuncArgs.join(', ')});");
} else {
final List<String> vfuncArgs = <String>[];
vfuncArgs.addAll(methodArgs);
vfuncArgs.add('self->user_data');
indent.writeln(
"g_autoptr($responseClassName) response = self->vtable->$methodName(${vfuncArgs.join(', ')});");
indent.writeScoped('if (response == nullptr) {', '}', () {
indent.writeln(
'g_warning("No response returned to %s.%s", "${api.name}", "${method.name}");');
indent.writeln('return;');
});
indent.newln();
indent.writeln('g_autoptr(GError) error = NULL;');
indent.writeScoped(
'if (!fl_basic_message_channel_respond(channel, response_handle, response->value, &error)) {',
'}', () {
indent.writeln(
'g_warning("Failed to send response to %s.%s: %s", "${api.name}", "${method.name}", error->message);');
});
}
});
}
indent.newln();
indent.writeScoped(
'void ${methodPrefix}_set_method_handlers(FlBinaryMessenger* messenger, const gchar* suffix, const $vtableName* vtable, gpointer user_data, GDestroyNotify user_data_free_func) {',
'}', () {
indent.writeln(
'g_autofree gchar* dot_suffix = suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup("");');
indent.writeln(
'g_autoptr($className) api_data = ${methodPrefix}_new(vtable, user_data, user_data_free_func);');
indent.newln();
indent.writeln(
'g_autoptr($codecClassName) codec = ${codecMethodPrefix}_new();');
for (final Method method in api.methods) {
final String methodName = _getMethodName(method.name);
final String channelName =
makeChannelName(api, method, dartPackageName);
indent.writeln(
'g_autofree gchar* ${methodName}_channel_name = g_strdup_printf("$channelName%s", dot_suffix);');
indent.writeln(
'g_autoptr(FlBasicMessageChannel) ${methodName}_channel = fl_basic_message_channel_new(messenger, ${methodName}_channel_name, FL_MESSAGE_CODEC(codec));');
indent.writeln(
'fl_basic_message_channel_set_message_handler(${methodName}_channel, ${methodPrefix}_${methodName}_cb, g_object_ref(api_data), g_object_unref);');
}
});
indent.newln();
indent.writeScoped(
'void ${methodPrefix}_clear_method_handlers(FlBinaryMessenger* messenger, const gchar* suffix) {',
'}', () {
indent.writeln(
'g_autofree gchar* dot_suffix = suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup("");');
indent.newln();
indent.writeln(
'g_autoptr($codecClassName) codec = ${codecMethodPrefix}_new();');
for (final Method method in api.methods) {
final String methodName = _getMethodName(method.name);
final String channelName =
makeChannelName(api, method, dartPackageName);
indent.writeln(
'g_autofree gchar* ${methodName}_channel_name = g_strdup_printf("$channelName%s", dot_suffix);');
indent.writeln(
'g_autoptr(FlBasicMessageChannel) ${methodName}_channel = fl_basic_message_channel_new(messenger, ${methodName}_channel_name, FL_MESSAGE_CODEC(codec));');
indent.writeln(
'fl_basic_message_channel_set_message_handler(${methodName}_channel, nullptr, nullptr, nullptr);');
}
});
for (final Method method
in api.methods.where((Method method) => method.isAsynchronous)) {
final String returnType = _getType(module, method.returnType);
final String methodName = _getMethodName(method.name);
final String responseName = _getResponseName(api.name, method.name);
final String responseClassName = _getClassName(module, responseName);
final String responseMethodPrefix =
_getMethodPrefix(module, responseName);
indent.newln();
final List<String> respondArgs = <String>[
'${className}ResponseHandle* response_handle',
if (returnType != 'void') '$returnType return_value',
if (_isNumericListType(method.returnType)) 'size_t return_value_length'
];
indent.writeScoped(
"void ${methodPrefix}_respond_$methodName(${respondArgs.join(', ')}) {",
'}', () {
final List<String> returnArgs = <String>[
if (returnType != 'void') 'return_value',
if (_isNumericListType(method.returnType)) 'return_value_length'
];
indent.writeln(
'g_autoptr($responseClassName) response = ${responseMethodPrefix}_new(${returnArgs.join(', ')});');
indent.writeln('g_autoptr(GError) error = nullptr;');
indent.writeScoped(
'if (!fl_basic_message_channel_respond(response_handle->channel, response_handle->response_handle, response->value, &error)) {',
'}', () {
indent.writeln(
'g_warning("Failed to send response to %s.%s: %s", "${api.name}", "${method.name}", error->message);');
});
});
indent.newln();
final List<String> respondErrorArgs = <String>[
'${className}ResponseHandle* response_handle',
'const gchar* code',
'const gchar* message',
'FlValue* details'
];
indent.writeScoped(
"void ${methodPrefix}_respond_error_$methodName(${respondErrorArgs.join(', ')}) {",
'}', () {
indent.writeln(
'g_autoptr($responseClassName) response = ${responseMethodPrefix}_new_error(code, message, details);');
indent.writeln('g_autoptr(GError) error = nullptr;');
indent.writeScoped(
'if (!fl_basic_message_channel_respond(response_handle->channel, response_handle->response_handle, response->value, &error)) {',
'}', () {
indent.writeln(
'g_warning("Failed to send response to %s.%s: %s", "${api.name}", "${method.name}", error->message);');
});
});
}
}