zuzu-rust 0.2.0

Rust implementation of ZuzuScript
Documentation
#include <ffi.h>
#include <stdint.h>
#include <stdlib.h>

#define ZUZU_FFI_VOID 0
#define ZUZU_FFI_BOOL 1
#define ZUZU_FFI_SINT64 2
#define ZUZU_FFI_UINT64 3
#define ZUZU_FFI_DOUBLE 4
#define ZUZU_FFI_POINTER 5

typedef union {
    uint8_t bool_value;
    int64_t sint64_value;
    uint64_t uint64_value;
    double double_value;
    void *pointer_value;
} ZuzuFfiValue;

typedef struct {
    int32_t type_code;
    ZuzuFfiValue value;
} ZuzuFfiArg;

typedef struct {
    int32_t type_code;
    ZuzuFfiValue value;
} ZuzuFfiResult;

static ffi_type *zuzu_ffi_type_for(int32_t type_code) {
    switch (type_code) {
    case ZUZU_FFI_VOID:
        return &ffi_type_void;
    case ZUZU_FFI_BOOL:
        return &ffi_type_uint8;
    case ZUZU_FFI_SINT64:
        return &ffi_type_sint64;
    case ZUZU_FFI_UINT64:
        return &ffi_type_uint64;
    case ZUZU_FFI_DOUBLE:
        return &ffi_type_double;
    case ZUZU_FFI_POINTER:
        return &ffi_type_pointer;
    default:
        return NULL;
    }
}

const char *zuzu_ffi_call(
    void *function,
    int32_t return_type,
    const int32_t *param_types,
    ZuzuFfiArg *args,
    size_t nargs,
    ZuzuFfiResult *result
) {
    ffi_cif cif;
    ffi_type *rtype = zuzu_ffi_type_for(return_type);
    if (function == NULL) {
        return "C function pointer is null";
    }
    if (rtype == NULL) {
        return "unsupported C return type";
    }

    ffi_type **atypes = calloc(nargs > 0 ? nargs : 1, sizeof(*atypes));
    void **avalues = calloc(nargs > 0 ? nargs : 1, sizeof(*avalues));
    if (atypes == NULL || avalues == NULL) {
        free(atypes);
        free(avalues);
        return "could not allocate libffi call frame";
    }

    for (size_t i = 0; i < nargs; i++) {
        atypes[i] = zuzu_ffi_type_for(param_types[i]);
        if (atypes[i] == NULL || param_types[i] == ZUZU_FFI_VOID) {
            free(atypes);
            free(avalues);
            return "unsupported C parameter type";
        }
        avalues[i] = &args[i].value;
    }

    ffi_status status = ffi_prep_cif(
        &cif,
        FFI_DEFAULT_ABI,
        (unsigned int)nargs,
        rtype,
        atypes
    );
    if (status != FFI_OK) {
        free(atypes);
        free(avalues);
        return "ffi_prep_cif failed";
    }

    result->type_code = return_type;
    result->value.uint64_value = 0;
    ffi_call(&cif, FFI_FN(function), &result->value, avalues);

    free(atypes);
    free(avalues);
    return NULL;
}