int jsonRpc_call()

in libs/dfi/src/json_rpc.c [95:200]


int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* request, char** out) {
    int status = OK;

    json_error_t error;
    json_auto_t* js_request = json_loads(request, 0, &error);
    if (js_request == NULL) {
        celix_err_pushf("Got json error: %s", error.text);
        return ERROR;
    }
    json_t* arguments = NULL;
    const char* sig;
    if (json_unpack(js_request, "{s:s}", "m", &sig) != 0) {
        celix_err_push("Error getting method signature");
        return ERROR;
    }
    arguments = json_object_get(js_request, "a");
    if (arguments == NULL || !json_is_array(arguments)) {
        celix_err_pushf("Error getting arguments array for %s", sig);
        return ERROR;
    }

    const struct method_entry* method = dynInterface_findMethod(intf, sig);
    if (method == NULL) {
        celix_err_pushf("Cannot find method with sig '%s'", sig);
        return ERROR;
    }

    struct generic_service_layout* serv = service;
    const struct dyn_function_arguments_head* dynArgs = dynFunction_arguments(method->dynFunc);
    const dyn_function_argument_type* last = TAILQ_LAST(dynArgs, dyn_function_arguments_head);
    int nrOfArgs = dynFunction_nrOfArguments(method->dynFunc);
    if (nrOfArgs > CELIX_JSON_RPC_MAX_ARGS) {
        celix_err_pushf("Too many arguments for %s: %d > %d", sig, nrOfArgs, CELIX_JSON_RPC_MAX_ARGS);
        return ERROR;
    }
    void* ptr = NULL;
    void* ptrToPtr = &ptr;
    celix_auto(celix_rpc_args_t) rpcArgs = { dynArgs, {0} };

    rpcArgs.args[0] = &serv->handle;
    --nrOfArgs;
    if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) {
        const dyn_type *subType = dynType_typedPointer_getTypedType(dynType_realType(last->type));
        rpcArgs.args[last->index] = &ptr;
        if ((status = dynType_alloc(subType, &ptr)) != OK) {
            celix_err_pushf("Error allocating memory for pre-allocated output argument of %s", sig);
            return ERROR;
        }
        --nrOfArgs;
    } else if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) {
        rpcArgs.args[last->index] = &ptrToPtr;
        --nrOfArgs;
    }
    if ((size_t)nrOfArgs != json_array_size(arguments)) {
        celix_err_pushf("Wrong number of standard arguments for %s. Expected %d, got %zu",
                        sig, nrOfArgs, json_array_size(arguments));
        return ERROR;
    }
    //setup and deserialize input
    dyn_function_argument_type* entry = NULL;
    TAILQ_FOREACH(entry, dynArgs, entries) {
        if (entry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__STD) {
            continue;
        }
        status = jsonSerializer_deserializeJson(entry->type, json_array_get(arguments, entry->index-1), &(rpcArgs.args[entry->index]));
        if (status != OK) {
            celix_err_pushf("Error deserializing argument %d for %s", entry->index, sig);
            return status;
        }
    }
    ffi_sarg returnVal = 1;
    (void)dynFunction_call(method->dynFunc, serv->methods[method->index], (void *) &returnVal, rpcArgs.args);

    int funcCallStatus = (int)returnVal;
    //serialize output
    json_auto_t* jsonResult = NULL;
    if (funcCallStatus == 0) {
        const dyn_type* argType = dynType_realType(last->type);
        if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) {
            status = jsonSerializer_serializeJson(argType, rpcArgs.args[last->index], &jsonResult);
        } else if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) {
            status = jsonSerializer_serializeJson(dynType_typedPointer_getTypedType(argType), (void*) &ptr, &jsonResult);
        }
        if (status != OK) {
            celix_err_pushf("Error serializing result for %s", sig);
            return status;
        }
    }
    celix_rpcArgs_cleanup(&rpcArgs);

    json_auto_t* payload = json_object();
    if (funcCallStatus == 0) {
        if (jsonResult != NULL) {
            status = json_object_set_new_nocheck(payload, "r", celix_steal_ptr(jsonResult));
        }
    } else {
        status = json_object_set_new_nocheck(payload, "e", json_integer(funcCallStatus));
    }
    if (status != 0)  {
        celix_err_pushf("Error generating response payload for %s", sig);
        return ERROR;
    }
    //use JSON_COMPACT to reduce the size of the JSON string.
    *out = json_dumps(payload, JSON_COMPACT | JSON_ENCODE_ANY);
    return (*out != NULL) ? OK : ERROR;
}