Camel/include/cyaml.h

296 lines
8.3 KiB
C
Raw Normal View History

2025-03-31 14:56:58 +00:00
#ifndef CYAML_H
#define CYAML_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
/* --- API Declarations --- */
// Forward declarations for opaque types.
typedef struct cyaml_node_t cyaml_node_t;
typedef struct cyaml_document_t cyaml_document_t;
typedef struct cyaml_emitter_t cyaml_emitter_t;
// Node types.
typedef enum {
CYAML_NODE_UNDEFINED,
CYAML_NODE_SCALAR,
CYAML_NODE_SEQUENCE,
CYAML_NODE_MAPPING
} cyaml_node_type_t;
/* Document Loading and Parsing */
// Load YAML document from a file.
// Returns a pointer to a cyaml_document_t or NULL on error.
cyaml_document_t* cyaml_load_file(const char *filename);
// Load YAML document from a string.
// Returns a pointer to a cyaml_document_t or NULL on error.
cyaml_document_t* cyaml_load_string(const char *yaml_string);
// Free the document and all associated nodes.
void cyaml_document_destroy(cyaml_document_t *doc);
// Get the root node of the document.
const cyaml_node_t* cyaml_document_get_root(const cyaml_document_t *doc);
/* Node Querying and Access */
// Get the type of a node.
cyaml_node_type_t cyaml_node_get_type(const cyaml_node_t *node);
// For scalar nodes: retrieve as a string. Returns NULL if not convertible.
const char* cyaml_node_as_string(const cyaml_node_t *node);
// For scalar nodes: retrieve as an integer.
int cyaml_node_as_int(const cyaml_node_t *node);
// For scalar nodes: retrieve as a double.
double cyaml_node_as_double(const cyaml_node_t *node);
// For mapping nodes: retrieve a child node by key. Returns NULL if not found.
const cyaml_node_t* cyaml_node_get(const cyaml_node_t *node, const char *key);
// For sequence nodes: return the number of child nodes.
size_t cyaml_node_size(const cyaml_node_t *node);
// For sequence nodes: retrieve a child node by index.
const cyaml_node_t* cyaml_node_index(const cyaml_node_t *node, size_t index);
/* Emitting YAML */
// Create an emitter object.
cyaml_emitter_t* cyaml_emitter_create(void);
// Emit a document into the emitter's internal buffer.
int cyaml_emitter_emit(cyaml_emitter_t *emitter, const cyaml_document_t *doc);
// Write the emitter's contents to a file.
// Returns 0 on success, non-zero on error.
int cyaml_emitter_to_file(cyaml_emitter_t *emitter, const char *filename);
// Retrieve a string copy of the emitter's contents.
// The caller is responsible for freeing the returned string.
char* cyaml_emitter_to_string(cyaml_emitter_t *emitter);
// Destroy the emitter and free associated memory.
void cyaml_emitter_destroy(cyaml_emitter_t *emitter);
#ifdef __cplusplus
}
#endif
/* --- Implementation --- */
/* To include the implementation, define CYAML_IMPLEMENTATION in *one* source file before including "cyaml.h". */
#ifdef CYAML_IMPLEMENTATION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Internal structures */
struct cyaml_document_t {
cyaml_node_t *root;
};
struct cyaml_node_t {
cyaml_node_type_t type;
char *scalar;
// For sequence nodes.
cyaml_node_t **sequence;
size_t sequence_size;
// For mapping nodes.
char **keys;
cyaml_node_t **values;
size_t mapping_size;
};
struct cyaml_emitter_t {
char *buffer;
size_t size;
size_t capacity;
};
#define CYAML_EMITTER_INITIAL_CAPACITY 256
/* --- Document Loading and Parsing --- */
// Loads the entire file into a buffer and then calls cyaml_load_string().
cyaml_document_t* cyaml_load_file(const char *filename) {
FILE *file = fopen(filename, "rb");
if (!file) return NULL;
fseek(file, 0, SEEK_END);
long length = ftell(file);
rewind(file);
char *content = (char*)malloc(length + 1);
if (!content) {
fclose(file);
return NULL;
}
fread(content, 1, length, file);
content[length] = '\0';
fclose(file);
cyaml_document_t *doc = cyaml_load_string(content);
free(content);
return doc;
}
// For demonstration, cyaml_load_string() creates a document with a single scalar node.
cyaml_document_t* cyaml_load_string(const char *yaml_string) {
cyaml_document_t *doc = (cyaml_document_t*)malloc(sizeof(cyaml_document_t));
if (!doc) return NULL;
doc->root = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
if (!doc->root) {
free(doc);
return NULL;
}
doc->root->type = CYAML_NODE_SCALAR;
doc->root->scalar = strdup(yaml_string);
doc->root->sequence = NULL;
doc->root->sequence_size = 0;
doc->root->keys = NULL;
doc->root->values = NULL;
doc->root->mapping_size = 0;
return doc;
}
void cyaml_document_destroy(cyaml_document_t *doc) {
if (!doc) return;
// For this demonstration, assume only a scalar root node exists.
if (doc->root) {
if (doc->root->scalar)
free(doc->root->scalar);
free(doc->root);
}
free(doc);
}
const cyaml_node_t* cyaml_document_get_root(const cyaml_document_t *doc) {
if (!doc) return NULL;
return doc->root;
}
/* --- Node Querying and Access --- */
cyaml_node_type_t cyaml_node_get_type(const cyaml_node_t *node) {
if (!node) return CYAML_NODE_UNDEFINED;
return node->type;
}
const char* cyaml_node_as_string(const cyaml_node_t *node) {
if (!node || node->type != CYAML_NODE_SCALAR) return NULL;
return node->scalar;
}
int cyaml_node_as_int(const cyaml_node_t *node) {
if (!node || node->type != CYAML_NODE_SCALAR) return 0;
return atoi(node->scalar);
}
double cyaml_node_as_double(const cyaml_node_t *node) {
if (!node || node->type != CYAML_NODE_SCALAR) return 0.0;
return atof(node->scalar);
}
// Dummy mapping lookup: searches keys in a mapping node.
const cyaml_node_t* cyaml_node_get(const cyaml_node_t *node, const char *key) {
if (!node || node->type != CYAML_NODE_MAPPING) return NULL;
for (size_t i = 0; i < node->mapping_size; i++) {
if (strcmp(node->keys[i], key) == 0) {
return node->values[i];
}
}
return NULL;
}
size_t cyaml_node_size(const cyaml_node_t *node) {
if (!node) return 0;
if (node->type == CYAML_NODE_SEQUENCE)
return node->sequence_size;
if (node->type == CYAML_NODE_MAPPING)
return node->mapping_size;
return 0;
}
const cyaml_node_t* cyaml_node_index(const cyaml_node_t *node, size_t index) {
if (!node) return NULL;
if (node->type == CYAML_NODE_SEQUENCE && index < node->sequence_size)
return node->sequence[index];
return NULL;
}
/* --- YAML Emitting --- */
cyaml_emitter_t* cyaml_emitter_create(void) {
cyaml_emitter_t *emitter = (cyaml_emitter_t*)malloc(sizeof(cyaml_emitter_t));
if (!emitter) return NULL;
emitter->buffer = (char*)malloc(CYAML_EMITTER_INITIAL_CAPACITY);
if (!emitter->buffer) {
free(emitter);
return NULL;
}
emitter->buffer[0] = '\0';
emitter->size = 0;
emitter->capacity = CYAML_EMITTER_INITIAL_CAPACITY;
return emitter;
}
// Internal helper to append text to the emitter's buffer.
static int cyaml_emitter_append(cyaml_emitter_t *emitter, const char *str) {
if (!emitter || !str) return -1;
size_t len = strlen(str);
if (emitter->size + len + 1 > emitter->capacity) {
size_t new_capacity = emitter->capacity * 2;
while (emitter->size + len + 1 > new_capacity) {
new_capacity *= 2;
}
char *new_buffer = (char*)realloc(emitter->buffer, new_capacity);
if (!new_buffer) return -1;
emitter->buffer = new_buffer;
emitter->capacity = new_capacity;
}
memcpy(emitter->buffer + emitter->size, str, len + 1);
emitter->size += len;
return 0;
}
// A simple emitter that outputs the root scalar.
int cyaml_emitter_emit(cyaml_emitter_t *emitter, const cyaml_document_t *doc) {
if (!emitter || !doc) return -1;
if (doc->root && doc->root->type == CYAML_NODE_SCALAR) {
return cyaml_emitter_append(emitter, doc->root->scalar);
}
return -1;
}
int cyaml_emitter_to_file(cyaml_emitter_t *emitter, const char *filename) {
if (!emitter || !filename) return -1;
FILE *file = fopen(filename, "w");
if (!file) return -1;
fwrite(emitter->buffer, 1, emitter->size, file);
fclose(file);
return 0;
}
char* cyaml_emitter_to_string(cyaml_emitter_t *emitter) {
if (!emitter) return NULL;
return strdup(emitter->buffer);
}
void cyaml_emitter_destroy(cyaml_emitter_t *emitter) {
if (!emitter) return;
if (emitter->buffer)
free(emitter->buffer);
free(emitter);
}
#endif // CYAML_IMPLEMENTATION
#endif // CYAML_H