fmi-rs 0.3.0

Functional Mock-up Interface (FMI) for Rust
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libxml/parser.h>
#include <libxml/xmlschemas.h>


typedef struct {
    size_t len;
    char** messages;
} Messages;

static void log_error(void* ctx, const char* msg, ...) {
    
    assert(ctx);
    assert(msg);

    Messages* messages = (Messages*)ctx;

    char** temp = (char**)realloc(messages->messages, (messages->len + 1) * sizeof(char*));

    if (!temp) return;

    messages->messages = temp;
    messages->messages[messages->len] = NULL;

    va_list args;

    va_start(args, msg);
    const size_t len = vsnprintf(NULL, 0, msg, args);
    va_end(args);

    char* message = (char*)malloc(len + 1);

    assert(message);

    va_start(args, msg);
    vsnprintf(message, len + 1, msg, args);
    va_end(args);

    messages->messages[messages->len++] = message;
}

void free_messages(int len, char** messages) {

    assert(messages);
    
    for (size_t i = 0; i < len; i++) {
        free(messages[i]);
    }
    
    free(messages);
}

int validate_model_description(const char* model_description_path, int fmi_major_version, char*** messages) {
    return 0;
}

int validate_xml_document(
    const char* document_buffer, 
    int document_buffer_size, 
    const char* schema_buffer, 
    int schema_buffer_size, 
    char*** messages,
    xmlExternalEntityLoader external_entity_loader
) {
    Messages msg = { .len = 0, .messages = NULL };

    if (external_entity_loader) {
        xmlSetExternalEntityLoader(external_entity_loader);
    }

    xmlDocPtr doc = xmlParseMemory(document_buffer, document_buffer_size);

    if (!doc) {
        log_error(&msg, "Failed to parse document.");
        goto TERMINATE;
    }

    xmlNodePtr root = xmlDocGetRootElement(doc);

    if (root == NULL) {
        log_error(&msg, "Empty document.");
        goto TERMINATE;
    }

    xmlSchemaParserCtxtPtr pctxt = xmlSchemaNewMemParserCtxt(schema_buffer, schema_buffer_size);

    if (pctxt == NULL) {
        log_error(&msg, "Empty schema.");
        goto TERMINATE;
    }

    xmlSchemaPtr schema = xmlSchemaParse(pctxt);

    if (schema == NULL) {
        log_error(&msg, "Failed to parse XSD schema.");
        goto TERMINATE;
    }

    xmlSchemaValidCtxtPtr vctxt = xmlSchemaNewValidCtxt(schema);

    if (!vctxt) {
        log_error(&msg, "Failed to create validation context.");
        goto TERMINATE;
    }

    xmlSchemaSetValidErrors(vctxt, (xmlSchemaValidityErrorFunc)log_error, NULL, &msg);

    if (xmlSchemaValidateDoc(vctxt, doc)) {
        goto TERMINATE;
    }

TERMINATE:

    *messages = msg.messages;

    return msg.len;
}