Updated API and made it faster, and suports more features
This commit is contained in:
parent
0ad6954f92
commit
2e2c2afe7a
217
examples/test.c
217
examples/test.c
@ -1,31 +1,198 @@
|
||||
#define CYAML_IMPLEMENTATION
|
||||
#include "../include/cyaml.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define YAML_IMPLEMENTATION
|
||||
#include "../include/cyaml.h"
|
||||
|
||||
// Define a simple User struct.
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[100];
|
||||
char email[100];
|
||||
} User;
|
||||
|
||||
#define MAX_USERS 100
|
||||
|
||||
User users[MAX_USERS];
|
||||
size_t user_count = 0;
|
||||
|
||||
// Display the list of users.
|
||||
void list_users(void) {
|
||||
printf("\n-- User Database --\n");
|
||||
if (user_count == 0) {
|
||||
printf("No users found.\n");
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < user_count; i++) {
|
||||
printf("ID %d: %s, %s\n", users[i].id, users[i].name, users[i].email);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the in-memory user database to a YAML file.
|
||||
// The output YAML file will have a mapping with the key "users"
|
||||
// which contains a sequence of mappings (each representing a user).
|
||||
int save_to_file(const char *filename) {
|
||||
// Create a new document with a mapping as root.
|
||||
YAMLDoc *doc = (YAMLDoc*)malloc(sizeof(YAMLDoc));
|
||||
if (!doc) return -1;
|
||||
doc->root = yaml_new_map();
|
||||
if (!doc->root) {
|
||||
free(doc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create a sequence to hold all user mappings.
|
||||
YAMLNode *userSeq = yaml_new_seq();
|
||||
if (!userSeq) {
|
||||
yaml_doc_destroy(doc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// For each user, create a mapping and add it to the sequence.
|
||||
for (size_t i = 0; i < user_count; i++) {
|
||||
YAMLNode *userMap = yaml_new_map();
|
||||
char idStr[20];
|
||||
snprintf(idStr, sizeof(idStr), "%d", users[i].id);
|
||||
yaml_map_set(userMap, "id", yaml_new_str(idStr));
|
||||
yaml_map_set(userMap, "name", yaml_new_str(users[i].name));
|
||||
yaml_map_set(userMap, "email", yaml_new_str(users[i].email));
|
||||
yaml_seq_append(userSeq, userMap);
|
||||
}
|
||||
yaml_map_set(doc->root, "users", userSeq);
|
||||
|
||||
// Create an emitter and write the document to the file.
|
||||
YAMLEmitter *emitter = yaml_emitter_create();
|
||||
if (!emitter) {
|
||||
yaml_doc_destroy(doc);
|
||||
return -1;
|
||||
}
|
||||
if (yaml_emit(emitter, doc) != 0) {
|
||||
yaml_emitter_destroy(emitter);
|
||||
yaml_doc_destroy(doc);
|
||||
return -1;
|
||||
}
|
||||
int ret = yaml_emit_to_file(emitter, filename);
|
||||
|
||||
// Clean up.
|
||||
yaml_emitter_destroy(emitter);
|
||||
yaml_doc_destroy(doc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Add a new user via CLI.
|
||||
void add_user(void) {
|
||||
if (user_count >= MAX_USERS) {
|
||||
printf("User database is full.\n");
|
||||
return;
|
||||
}
|
||||
User newUser;
|
||||
newUser.id = user_count + 1; // Simple sequential id assignment.
|
||||
|
||||
printf("Enter user name: ");
|
||||
if (!fgets(newUser.name, sizeof(newUser.name), stdin)) {
|
||||
printf("Error reading name.\n");
|
||||
return;
|
||||
}
|
||||
newUser.name[strcspn(newUser.name, "\n")] = '\0'; // Remove newline.
|
||||
|
||||
printf("Enter user email: ");
|
||||
if (!fgets(newUser.email, sizeof(newUser.email), stdin)) {
|
||||
printf("Error reading email.\n");
|
||||
return;
|
||||
}
|
||||
newUser.email[strcspn(newUser.email, "\n")] = '\0';
|
||||
|
||||
users[user_count++] = newUser;
|
||||
printf("User added successfully.\n");
|
||||
}
|
||||
|
||||
// Edit an existing user via CLI.
|
||||
void edit_user(void) {
|
||||
list_users();
|
||||
printf("Enter the user ID to edit: ");
|
||||
int id;
|
||||
if (scanf("%d", &id) != 1) {
|
||||
printf("Invalid input.\n");
|
||||
while(getchar() != '\n'); // Flush input buffer.
|
||||
return;
|
||||
}
|
||||
while(getchar() != '\n'); // Consume newline.
|
||||
|
||||
int found = 0;
|
||||
for (size_t i = 0; i < user_count; i++) {
|
||||
if (users[i].id == id) {
|
||||
found = 1;
|
||||
printf("Editing user (ID %d): %s, %s\n", users[i].id, users[i].name, users[i].email);
|
||||
printf("Enter new name (leave empty to keep unchanged): ");
|
||||
char newName[100];
|
||||
if (fgets(newName, sizeof(newName), stdin)) {
|
||||
newName[strcspn(newName, "\n")] = '\0';
|
||||
if (strlen(newName) > 0)
|
||||
strncpy(users[i].name, newName, sizeof(users[i].name));
|
||||
}
|
||||
printf("Enter new email (leave empty to keep unchanged): ");
|
||||
char newEmail[100];
|
||||
if (fgets(newEmail, sizeof(newEmail), stdin)) {
|
||||
newEmail[strcspn(newEmail, "\n")] = '\0';
|
||||
if (strlen(newEmail) > 0)
|
||||
strncpy(users[i].email, newEmail, sizeof(users[i].email));
|
||||
}
|
||||
printf("User updated successfully.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
printf("User with ID %d not found.\n", id);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
// Load a YAML document from a string.
|
||||
cyaml_document_t *doc = cyaml_load_string("example: Hello, cyaml!");
|
||||
if (!doc) {
|
||||
fprintf(stderr, "Failed to load YAML.\n");
|
||||
return 1;
|
||||
int running = 1;
|
||||
while (running) {
|
||||
printf("\n--- User Database CLI ---\n");
|
||||
printf("1. List users\n");
|
||||
printf("2. Add user\n");
|
||||
printf("3. Edit user\n");
|
||||
printf("4. Save database to file\n");
|
||||
printf("5. Exit\n");
|
||||
printf("Enter choice: ");
|
||||
|
||||
int choice;
|
||||
if (scanf("%d", &choice) != 1) {
|
||||
printf("Invalid input. Please enter a number.\n");
|
||||
while(getchar() != '\n'); // Clear the invalid input.
|
||||
continue;
|
||||
}
|
||||
while(getchar() != '\n'); // Consume newline.
|
||||
|
||||
switch (choice) {
|
||||
case 1:
|
||||
list_users();
|
||||
break;
|
||||
case 2:
|
||||
add_user();
|
||||
break;
|
||||
case 3:
|
||||
edit_user();
|
||||
break;
|
||||
case 4: {
|
||||
char filename[256];
|
||||
printf("Enter filename to save the database: ");
|
||||
if (fgets(filename, sizeof(filename), stdin)) {
|
||||
filename[strcspn(filename, "\n")] = '\0';
|
||||
if (save_to_file(filename) == 0)
|
||||
printf("Database saved successfully to '%s'.\n", filename);
|
||||
else
|
||||
printf("Error saving database to '%s'.\n", filename);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
running = 0;
|
||||
break;
|
||||
default:
|
||||
printf("Invalid choice. Please try again.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Get and print the root node's scalar value.
|
||||
const cyaml_node_t *root = cyaml_document_get_root(doc);
|
||||
if (root && cyaml_node_get_type(root) == CYAML_NODE_SCALAR) {
|
||||
printf("YAML Content: %s\n", cyaml_node_as_string(root));
|
||||
}
|
||||
|
||||
// Create an emitter and output the document.
|
||||
cyaml_emitter_t *emitter = cyaml_emitter_create();
|
||||
if (cyaml_emitter_emit(emitter, doc) == 0) {
|
||||
char *output = cyaml_emitter_to_string(emitter);
|
||||
printf("Emitted YAML:\n%s\n", output);
|
||||
free(output);
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
cyaml_emitter_destroy(emitter);
|
||||
cyaml_document_destroy(doc);
|
||||
return 0;
|
||||
}
|
||||
|
459
examples/units.c
459
examples/units.c
@ -2,363 +2,110 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CYAML_IMPLEMENTATION
|
||||
#include "../include/cyaml.h" // Adjust this path as needed
|
||||
#define YAML_IMPLEMENTATION
|
||||
#include "../include/cyaml.h"
|
||||
|
||||
/* ANSI color definitions for pretty terminal output */
|
||||
#define COLOR_RED "\033[31m"
|
||||
#define COLOR_GREEN "\033[32m"
|
||||
#define COLOR_YELLOW "\033[33m"
|
||||
#define COLOR_RESET "\033[0m"
|
||||
|
||||
/* Global counters for tests */
|
||||
static int tests_passed = 0;
|
||||
static int tests_run = 0;
|
||||
|
||||
/* Test assertion macro */
|
||||
#define TEST_ASSERT(cond, msg) do { \
|
||||
tests_run++; \
|
||||
if (!(cond)) { \
|
||||
printf(COLOR_RED "[FAIL]" COLOR_RESET " %s\n", msg); \
|
||||
} else { \
|
||||
tests_passed++; \
|
||||
printf(COLOR_GREEN "[PASS]" COLOR_RESET " %s\n", msg); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Utility Functions to Clean Up Manually Created Nodes
|
||||
* (These are needed because our cyaml library currently
|
||||
* only auto-frees scalar nodes from cyaml_load_* functions.)
|
||||
*---------------------------------------------------------*/
|
||||
static void free_mapping_node(cyaml_node_t *node) {
|
||||
if (node->keys) {
|
||||
for (size_t i = 0; i < node->mapping_size; i++) {
|
||||
free(node->keys[i]);
|
||||
}
|
||||
free(node->keys);
|
||||
}
|
||||
if (node->values) {
|
||||
for (size_t i = 0; i < node->mapping_size; i++) {
|
||||
if (node->values[i]) {
|
||||
if (node->values[i]->scalar)
|
||||
free(node->values[i]->scalar);
|
||||
free(node->values[i]);
|
||||
}
|
||||
}
|
||||
free(node->values);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
|
||||
static void free_sequence_node(cyaml_node_t *node) {
|
||||
if (node->sequence) {
|
||||
for (size_t i = 0; i < node->sequence_size; i++) {
|
||||
if (node->sequence[i]) {
|
||||
if (node->sequence[i]->scalar)
|
||||
free(node->sequence[i]->scalar);
|
||||
free(node->sequence[i]);
|
||||
}
|
||||
}
|
||||
free(node->sequence);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 1: Load YAML String (Scalar)
|
||||
*---------------------------------------------------------*/
|
||||
void test_load_string() {
|
||||
const char *yaml = "Hello, cyaml!";
|
||||
cyaml_document_t *doc = cyaml_load_string(yaml);
|
||||
TEST_ASSERT(doc != NULL, "cyaml_load_string should not return NULL");
|
||||
|
||||
if (doc) {
|
||||
const cyaml_node_t *root = cyaml_document_get_root(doc);
|
||||
TEST_ASSERT(root != NULL, "Root node should not be NULL");
|
||||
|
||||
if (root) {
|
||||
const char *str = cyaml_node_as_string(root);
|
||||
TEST_ASSERT(str != NULL, "Root node should be a scalar string");
|
||||
if (str) {
|
||||
TEST_ASSERT(strcmp(str, yaml) == 0, "Scalar value should match input string");
|
||||
}
|
||||
}
|
||||
cyaml_document_destroy(doc);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 2: Emitter for Scalar YAML
|
||||
*---------------------------------------------------------*/
|
||||
void test_emitter_scalar() {
|
||||
const char *yaml = "Emitter Test!";
|
||||
cyaml_document_t *doc = cyaml_load_string(yaml);
|
||||
TEST_ASSERT(doc != NULL, "cyaml_load_string for emitter test should not return NULL");
|
||||
|
||||
if (doc) {
|
||||
cyaml_emitter_t *emitter = cyaml_emitter_create();
|
||||
TEST_ASSERT(emitter != NULL, "cyaml_emitter_create should not return NULL");
|
||||
|
||||
if (emitter) {
|
||||
int emit_result = cyaml_emitter_emit(emitter, doc);
|
||||
TEST_ASSERT(emit_result == 0, "cyaml_emitter_emit should succeed");
|
||||
|
||||
char *emitted_str = cyaml_emitter_to_string(emitter);
|
||||
TEST_ASSERT(emitted_str != NULL, "cyaml_emitter_to_string should not return NULL");
|
||||
if (emitted_str) {
|
||||
TEST_ASSERT(strcmp(emitted_str, yaml) == 0, "Emitted YAML should match original scalar");
|
||||
free(emitted_str);
|
||||
}
|
||||
cyaml_emitter_destroy(emitter);
|
||||
}
|
||||
cyaml_document_destroy(doc);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 3: Load YAML from a File (Scalar)
|
||||
*---------------------------------------------------------*/
|
||||
void test_load_file() {
|
||||
const char *filename = "test.yaml";
|
||||
const char *yaml = "File Test!";
|
||||
|
||||
/* Create a temporary file with YAML content */
|
||||
FILE *file = fopen(filename, "w");
|
||||
if (!file) {
|
||||
printf(COLOR_YELLOW "Warning: Could not create temporary test file.\n" COLOR_RESET);
|
||||
return;
|
||||
}
|
||||
fputs(yaml, file);
|
||||
fclose(file);
|
||||
|
||||
cyaml_document_t *doc = cyaml_load_file(filename);
|
||||
TEST_ASSERT(doc != NULL, "cyaml_load_file should not return NULL");
|
||||
|
||||
if (doc) {
|
||||
const cyaml_node_t *root = cyaml_document_get_root(doc);
|
||||
TEST_ASSERT(root != NULL, "Root node from file should not be NULL");
|
||||
if (root) {
|
||||
const char *str = cyaml_node_as_string(root);
|
||||
TEST_ASSERT(str != NULL, "Root node from file should be a scalar string");
|
||||
if (str) {
|
||||
TEST_ASSERT(strcmp(str, yaml) == 0, "File content should match expected YAML");
|
||||
}
|
||||
}
|
||||
cyaml_document_destroy(doc);
|
||||
}
|
||||
|
||||
/* Remove the temporary file */
|
||||
remove(filename);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 4: Numeric Conversions
|
||||
*---------------------------------------------------------*/
|
||||
void test_numeric_conversion() {
|
||||
// Integer conversion test
|
||||
const char *int_str = "12345";
|
||||
cyaml_document_t *doc_int = cyaml_load_string(int_str);
|
||||
TEST_ASSERT(doc_int != NULL, "Load integer string for conversion");
|
||||
if (doc_int) {
|
||||
const cyaml_node_t *node = cyaml_document_get_root(doc_int);
|
||||
int value = cyaml_node_as_int(node);
|
||||
TEST_ASSERT(value == 12345, "Integer conversion: value should be 12345");
|
||||
cyaml_document_destroy(doc_int);
|
||||
}
|
||||
|
||||
// Double conversion test
|
||||
const char *double_str = "3.14159";
|
||||
cyaml_document_t *doc_double = cyaml_load_string(double_str);
|
||||
TEST_ASSERT(doc_double != NULL, "Load double string for conversion");
|
||||
if (doc_double) {
|
||||
const cyaml_node_t *node = cyaml_document_get_root(doc_double);
|
||||
double dvalue = cyaml_node_as_double(node);
|
||||
TEST_ASSERT(dvalue > 3.14158 && dvalue < 3.14160, "Double conversion: value should be approx 3.14159");
|
||||
cyaml_document_destroy(doc_double);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 5: Mapping Node Functionality
|
||||
*---------------------------------------------------------*/
|
||||
void test_mapping_node() {
|
||||
// Manually create a mapping node document
|
||||
cyaml_document_t *doc = (cyaml_document_t*)malloc(sizeof(cyaml_document_t));
|
||||
doc->root = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
|
||||
cyaml_node_t *map = doc->root;
|
||||
map->type = CYAML_NODE_MAPPING;
|
||||
map->mapping_size = 3;
|
||||
map->keys = (char**)malloc(sizeof(char*) * 3);
|
||||
map->values = (cyaml_node_t**)malloc(sizeof(cyaml_node_t*) * 3);
|
||||
|
||||
// Key-value pair 1: "name": "cyaml"
|
||||
map->keys[0] = strdup("name");
|
||||
map->values[0] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
|
||||
map->values[0]->type = CYAML_NODE_SCALAR;
|
||||
map->values[0]->scalar = strdup("cyaml");
|
||||
|
||||
// Key-value pair 2: "version": "1.0"
|
||||
map->keys[1] = strdup("version");
|
||||
map->values[1] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
|
||||
map->values[1]->type = CYAML_NODE_SCALAR;
|
||||
map->values[1]->scalar = strdup("1.0");
|
||||
|
||||
// Key-value pair 3: "count": "42"
|
||||
map->keys[2] = strdup("count");
|
||||
map->values[2] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
|
||||
map->values[2]->type = CYAML_NODE_SCALAR;
|
||||
map->values[2]->scalar = strdup("42");
|
||||
|
||||
TEST_ASSERT(cyaml_node_size(map) == 3, "Mapping node size should be 3");
|
||||
|
||||
const cyaml_node_t *node_name = cyaml_node_get(map, "name");
|
||||
TEST_ASSERT(node_name != NULL, "Mapping node: key 'name' should exist");
|
||||
if (node_name) {
|
||||
TEST_ASSERT(strcmp(cyaml_node_as_string(node_name), "cyaml") == 0, "Mapping node: 'name' value should be 'cyaml'");
|
||||
}
|
||||
|
||||
const cyaml_node_t *node_version = cyaml_node_get(map, "version");
|
||||
TEST_ASSERT(node_version != NULL, "Mapping node: key 'version' should exist");
|
||||
if (node_version) {
|
||||
TEST_ASSERT(strcmp(cyaml_node_as_string(node_version), "1.0") == 0, "Mapping node: 'version' value should be '1.0'");
|
||||
}
|
||||
|
||||
const cyaml_node_t *node_count = cyaml_node_get(map, "count");
|
||||
TEST_ASSERT(node_count != NULL, "Mapping node: key 'count' should exist");
|
||||
if (node_count) {
|
||||
int count = cyaml_node_as_int(node_count);
|
||||
TEST_ASSERT(count == 42, "Mapping node: 'count' value should be 42");
|
||||
}
|
||||
|
||||
// Clean up manually created mapping node
|
||||
free_mapping_node(map);
|
||||
free(doc);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 6: Sequence Node Functionality
|
||||
*---------------------------------------------------------*/
|
||||
void test_sequence_node() {
|
||||
// Manually create a sequence node document
|
||||
cyaml_document_t *doc = (cyaml_document_t*)malloc(sizeof(cyaml_document_t));
|
||||
doc->root = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
|
||||
cyaml_node_t *seq = doc->root;
|
||||
seq->type = CYAML_NODE_SEQUENCE;
|
||||
seq->sequence_size = 4;
|
||||
seq->sequence = (cyaml_node_t**)malloc(sizeof(cyaml_node_t*) * 4);
|
||||
|
||||
const char *items[] = { "one", "two", "three", "four" };
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
seq->sequence[i] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t));
|
||||
seq->sequence[i]->type = CYAML_NODE_SCALAR;
|
||||
seq->sequence[i]->scalar = strdup(items[i]);
|
||||
}
|
||||
|
||||
TEST_ASSERT(cyaml_node_size(seq) == 4, "Sequence node size should be 4");
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
const cyaml_node_t *item = cyaml_node_index(seq, i);
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "Sequence node: item %zu should not be NULL", i);
|
||||
TEST_ASSERT(item != NULL, msg);
|
||||
if (item) {
|
||||
snprintf(msg, sizeof(msg), "Sequence node: item %zu should equal '%s'", i, items[i]);
|
||||
TEST_ASSERT(strcmp(cyaml_node_as_string(item), items[i]) == 0, msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up manually created sequence node
|
||||
free_sequence_node(seq);
|
||||
free(doc);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 7: Bulk YAML String Loading (100 iterations)
|
||||
*---------------------------------------------------------*/
|
||||
void test_bulk_load_string() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "Bulk test %d", i);
|
||||
cyaml_document_t *doc = cyaml_load_string(buffer);
|
||||
char test_msg[128];
|
||||
snprintf(test_msg, sizeof(test_msg), "Bulk load string test %d: document should not be NULL", i);
|
||||
TEST_ASSERT(doc != NULL, test_msg);
|
||||
if (doc) {
|
||||
const cyaml_node_t *root = cyaml_document_get_root(doc);
|
||||
snprintf(test_msg, sizeof(test_msg), "Bulk load string test %d: root should not be NULL", i);
|
||||
TEST_ASSERT(root != NULL, test_msg);
|
||||
if (root) {
|
||||
snprintf(test_msg, sizeof(test_msg), "Bulk load string test %d: value should match", i);
|
||||
TEST_ASSERT(strcmp(cyaml_node_as_string(root), buffer) == 0, test_msg);
|
||||
}
|
||||
cyaml_document_destroy(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Test 8: Bulk File Read/Write (20 iterations)
|
||||
*---------------------------------------------------------*/
|
||||
void test_bulk_file_rw() {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
char filename[64];
|
||||
char content[64];
|
||||
snprintf(filename, sizeof(filename), "temp_test_%d.yaml", i);
|
||||
snprintf(content, sizeof(content), "File bulk test %d", i);
|
||||
|
||||
/* Write to file */
|
||||
FILE *file = fopen(filename, "w");
|
||||
if (!file) {
|
||||
printf(COLOR_YELLOW "Warning: Could not create temporary file %s.\n" COLOR_RESET, filename);
|
||||
continue;
|
||||
}
|
||||
fputs(content, file);
|
||||
fclose(file);
|
||||
|
||||
/* Read file and validate */
|
||||
cyaml_document_t *doc = cyaml_load_file(filename);
|
||||
char test_msg[128];
|
||||
snprintf(test_msg, sizeof(test_msg), "Bulk file test %d: document should not be NULL", i);
|
||||
TEST_ASSERT(doc != NULL, test_msg);
|
||||
if (doc) {
|
||||
const cyaml_node_t *root = cyaml_document_get_root(doc);
|
||||
snprintf(test_msg, sizeof(test_msg), "Bulk file test %d: root should not be NULL", i);
|
||||
TEST_ASSERT(root != NULL, test_msg);
|
||||
if (root) {
|
||||
snprintf(test_msg, sizeof(test_msg), "Bulk file test %d: content should match", i);
|
||||
TEST_ASSERT(strcmp(cyaml_node_as_string(root), content) == 0, test_msg);
|
||||
}
|
||||
cyaml_document_destroy(doc);
|
||||
}
|
||||
|
||||
/* Remove the temporary file */
|
||||
remove(filename);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Main Function: Run All Tests and Display Summary
|
||||
*---------------------------------------------------------*/
|
||||
int main(void) {
|
||||
printf("========================================\n");
|
||||
printf(" " COLOR_YELLOW "cyaml Unit Test Suite" COLOR_RESET "\n");
|
||||
printf("========================================\n\n");
|
||||
int fail_count = 0;
|
||||
|
||||
// --- Unit Test 1: Building and Emitting a YAML Document ---
|
||||
printf("Unit Test 1: Document creation and emission\n");
|
||||
|
||||
// Create a new document with a mapping as the root.
|
||||
YAMLDoc *doc = (YAMLDoc*)malloc(sizeof(YAMLDoc));
|
||||
if (!doc) {
|
||||
fprintf(stderr, "Memory allocation failure.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
doc->root = yaml_new_map();
|
||||
if (!doc->root) {
|
||||
fprintf(stderr, "Failed to create root node.\n");
|
||||
free(doc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Set "name" to a scalar value.
|
||||
if (yaml_map_set(doc->root, "name", yaml_new_str("Alice")) != 0) {
|
||||
fprintf(stderr, "Error setting key 'name'.\n");
|
||||
yaml_doc_destroy(doc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Create a sequence for "skills".
|
||||
YAMLNode *skills = yaml_new_seq();
|
||||
if (!skills) {
|
||||
fprintf(stderr, "Failed to create skills sequence.\n");
|
||||
yaml_doc_destroy(doc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
yaml_seq_append(skills, yaml_new_str("C"));
|
||||
yaml_seq_append(skills, yaml_new_str("Python"));
|
||||
if (yaml_map_set(doc->root, "skills", skills) != 0) {
|
||||
fprintf(stderr, "Error setting key 'skills'.\n");
|
||||
yaml_doc_destroy(doc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Emit the document.
|
||||
YAMLEmitter *emitter = yaml_emitter_create();
|
||||
if (!emitter) {
|
||||
fprintf(stderr, "Failed to create emitter.\n");
|
||||
yaml_doc_destroy(doc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (yaml_emit(emitter, doc) != 0) {
|
||||
fprintf(stderr, "Error emitting YAML document.\n");
|
||||
yaml_emitter_destroy(emitter);
|
||||
yaml_doc_destroy(doc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
char *output = yaml_emit_to_str(emitter);
|
||||
if (!output) {
|
||||
fprintf(stderr, "Failed to retrieve emitter string.\n");
|
||||
yaml_emitter_destroy(emitter);
|
||||
yaml_doc_destroy(doc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("Emitted YAML:\n%s\n", output);
|
||||
|
||||
// Check expected content.
|
||||
if (strstr(output, "Alice") == NULL ||
|
||||
strstr(output, "skills") == NULL ||
|
||||
strstr(output, "- Python") == NULL)
|
||||
{
|
||||
printf("Unit Test 1 Failed: Emitted YAML output is missing expected content.\n");
|
||||
fail_count++;
|
||||
} else {
|
||||
printf("Unit Test 1 Passed.\n");
|
||||
}
|
||||
|
||||
test_load_string();
|
||||
test_emitter_scalar();
|
||||
test_load_file();
|
||||
test_numeric_conversion();
|
||||
test_mapping_node();
|
||||
test_sequence_node();
|
||||
test_bulk_load_string();
|
||||
test_bulk_file_rw();
|
||||
|
||||
printf("\n========================================\n");
|
||||
printf("Test Summary:\n");
|
||||
printf("Total tests run: %d\n", tests_run);
|
||||
printf(COLOR_GREEN "Passed: %d" COLOR_RESET "\n", tests_passed);
|
||||
printf(COLOR_RED "Failed: %d" COLOR_RESET "\n", tests_run - tests_passed);
|
||||
printf("========================================\n");
|
||||
|
||||
return (tests_run - tests_passed) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
free(output);
|
||||
yaml_emitter_destroy(emitter);
|
||||
yaml_doc_destroy(doc);
|
||||
|
||||
// --- Unit Test 2: Loading a YAML Document from a String ---
|
||||
printf("\nUnit Test 2: YAML load from string\n");
|
||||
const char *yaml_input = "Hello YAML!\nThis is a test.";
|
||||
YAMLDoc *doc2 = yaml_load_str(yaml_input);
|
||||
if (!doc2) {
|
||||
fprintf(stderr, "Error loading YAML from string.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const YAMLNode *root2 = yaml_doc_root(doc2);
|
||||
if (!root2 || strcmp(yaml_as_str(root2), yaml_input) != 0) {
|
||||
printf("Unit Test 2 Failed: Loaded YAML does not match input string.\n");
|
||||
fail_count++;
|
||||
} else {
|
||||
printf("Unit Test 2 Passed.\n");
|
||||
}
|
||||
yaml_doc_destroy(doc2);
|
||||
|
||||
// --- Finalize Unit Tests ---
|
||||
if (fail_count == 0) {
|
||||
printf("\nAll unit tests passed successfully.\n");
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
printf("\n%d unit test(s) failed.\n", fail_count);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
527
include/cyaml.h
527
include/cyaml.h
@ -1,5 +1,5 @@
|
||||
#ifndef CYAML_H
|
||||
#define CYAML_H
|
||||
#ifndef YAML_H
|
||||
#define YAML_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -7,124 +7,164 @@ extern "C" {
|
||||
|
||||
#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;
|
||||
/* --- Public Types --- */
|
||||
|
||||
// Node types.
|
||||
typedef enum {
|
||||
CYAML_NODE_UNDEFINED,
|
||||
CYAML_NODE_SCALAR,
|
||||
CYAML_NODE_SEQUENCE,
|
||||
CYAML_NODE_MAPPING
|
||||
} cyaml_node_type_t;
|
||||
YAML_UNDEF, // Undefined
|
||||
YAML_STR, // Scalar string
|
||||
YAML_SEQ, // Sequence (list)
|
||||
YAML_MAP // Mapping (dictionary)
|
||||
} YAMLType;
|
||||
|
||||
/* Document Loading and Parsing */
|
||||
// Opaque types.
|
||||
typedef struct YAMLNode YAMLNode;
|
||||
typedef struct YAMLDoc YAMLDoc;
|
||||
typedef struct YAMLEmitter YAMLEmitter;
|
||||
|
||||
// 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);
|
||||
/* --- Document Loading and Parsing --- */
|
||||
|
||||
// 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);
|
||||
// Load a YAML document from a file. For demonstration, the file content is treated as a scalar.
|
||||
YAMLDoc* yaml_load_file(const char *filename);
|
||||
|
||||
// Free the document and all associated nodes.
|
||||
void cyaml_document_destroy(cyaml_document_t *doc);
|
||||
// Load a YAML document from a string. (Currently, the entire string is parsed as a scalar.)
|
||||
YAMLDoc* yaml_load_str(const char *yaml_string);
|
||||
|
||||
// Destroy the document and free all associated memory.
|
||||
void yaml_doc_destroy(YAMLDoc *doc);
|
||||
|
||||
// Get the root node of the document.
|
||||
const cyaml_node_t* cyaml_document_get_root(const cyaml_document_t *doc);
|
||||
const YAMLNode* yaml_doc_root(const YAMLDoc *doc);
|
||||
|
||||
/* Node Querying and Access */
|
||||
/* --- Node Querying and Access --- */
|
||||
|
||||
// Get the type of a node.
|
||||
cyaml_node_type_t cyaml_node_get_type(const cyaml_node_t *node);
|
||||
// Get the type of a YAML node.
|
||||
YAMLType yaml_node_type(const YAMLNode *node);
|
||||
|
||||
// For scalar nodes: retrieve as a string. Returns NULL if not convertible.
|
||||
const char* cyaml_node_as_string(const cyaml_node_t *node);
|
||||
// Retrieve the scalar value of a node (if it is a scalar), or NULL.
|
||||
const char* yaml_as_str(const YAMLNode *node);
|
||||
|
||||
// For scalar nodes: retrieve as an integer.
|
||||
int cyaml_node_as_int(const cyaml_node_t *node);
|
||||
// Convert a scalar node to int. Returns 0 if not a scalar.
|
||||
int yaml_as_int(const YAMLNode *node);
|
||||
|
||||
// For scalar nodes: retrieve as a double.
|
||||
double cyaml_node_as_double(const cyaml_node_t *node);
|
||||
// Convert a scalar node to double. Returns 0.0 if not a scalar.
|
||||
double yaml_as_double(const YAMLNode *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 mapping nodes: get a child node by key (returns NULL if not found).
|
||||
const YAMLNode* yaml_map_get(const YAMLNode *node, const char *key);
|
||||
|
||||
// For sequence nodes: return the number of child nodes.
|
||||
size_t cyaml_node_size(const cyaml_node_t *node);
|
||||
size_t yaml_seq_size(const YAMLNode *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);
|
||||
// For sequence nodes: retrieve a child node by index (returns NULL if out of range).
|
||||
const YAMLNode* yaml_seq_index(const YAMLNode *node, size_t index);
|
||||
|
||||
/* Emitting YAML */
|
||||
/* --- Node Creation and Modification --- */
|
||||
|
||||
// Create an emitter object.
|
||||
cyaml_emitter_t* cyaml_emitter_create(void);
|
||||
// Create a new scalar node with the given string value.
|
||||
YAMLNode* yaml_new_str(const char *value);
|
||||
|
||||
// Emit a document into the emitter's internal buffer.
|
||||
int cyaml_emitter_emit(cyaml_emitter_t *emitter, const cyaml_document_t *doc);
|
||||
// Create a new, empty sequence node.
|
||||
YAMLNode* yaml_new_seq(void);
|
||||
|
||||
// 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);
|
||||
// Create a new, empty mapping node.
|
||||
YAMLNode* yaml_new_map(void);
|
||||
|
||||
// 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);
|
||||
// Append a child node to a sequence node.
|
||||
int yaml_seq_append(YAMLNode *node, YAMLNode *child);
|
||||
|
||||
// Destroy the emitter and free associated memory.
|
||||
void cyaml_emitter_destroy(cyaml_emitter_t *emitter);
|
||||
// Set a key/value pair in a mapping node (the key is copied).
|
||||
int yaml_map_set(YAMLNode *node, const char *key, YAMLNode *child);
|
||||
|
||||
// Recursively free a YAML node and its children.
|
||||
void yaml_node_destroy(YAMLNode *node);
|
||||
|
||||
/* --- YAML Emitting --- */
|
||||
|
||||
// Create a new YAML emitter.
|
||||
YAMLEmitter* yaml_emitter_create(void);
|
||||
|
||||
// Emit the YAML document into the emitter's buffer.
|
||||
int yaml_emit(YAMLEmitter *emitter, const YAMLDoc *doc);
|
||||
|
||||
// Write the emitter's content to a file (returns 0 on success).
|
||||
int yaml_emit_to_file(YAMLEmitter *emitter, const char *filename);
|
||||
|
||||
// Get a string copy of the emitter's buffer (caller must free it).
|
||||
char* yaml_emit_to_str(YAMLEmitter *emitter);
|
||||
|
||||
// Destroy the emitter and free its memory.
|
||||
void yaml_emitter_destroy(YAMLEmitter *emitter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* --- Implementation --- */
|
||||
/* To include the implementation, define CYAML_IMPLEMENTATION in *one* source file before including "cyaml.h". */
|
||||
/* To compile the implementation, define YAML_IMPLEMENTATION in one source file before including "yaml.h". */
|
||||
|
||||
#ifdef CYAML_IMPLEMENTATION
|
||||
#ifdef YAML_IMPLEMENTATION
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Internal structures */
|
||||
/* --- Internal Structures --- */
|
||||
|
||||
struct cyaml_document_t {
|
||||
cyaml_node_t *root;
|
||||
struct YAMLDoc {
|
||||
YAMLNode *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.
|
||||
struct YAMLNode {
|
||||
YAMLType type;
|
||||
// For scalar nodes:
|
||||
char *str;
|
||||
// For sequence nodes:
|
||||
YAMLNode **seq;
|
||||
size_t seq_size;
|
||||
// For mapping nodes:
|
||||
char **keys;
|
||||
cyaml_node_t **values;
|
||||
size_t mapping_size;
|
||||
YAMLNode **vals;
|
||||
size_t map_size;
|
||||
};
|
||||
|
||||
struct cyaml_emitter_t {
|
||||
struct YAMLEmitter {
|
||||
char *buffer;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
#define CYAML_EMITTER_INITIAL_CAPACITY 256
|
||||
#define YAMLEMIT_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) {
|
||||
// For demonstration purposes, our "parser" treats the entire input as a scalar node.
|
||||
// In a full implementation, you would integrate a complete YAML parser.
|
||||
YAMLDoc* yaml_load_str(const char *yaml_string) {
|
||||
if (!yaml_string)
|
||||
return NULL;
|
||||
YAMLDoc *doc = (YAMLDoc*)malloc(sizeof(YAMLDoc));
|
||||
if (!doc)
|
||||
return NULL;
|
||||
doc->root = (YAMLNode*)malloc(sizeof(YAMLNode));
|
||||
if (!doc->root) {
|
||||
free(doc);
|
||||
return NULL;
|
||||
}
|
||||
doc->root->type = YAML_STR;
|
||||
doc->root->str = strdup(yaml_string);
|
||||
doc->root->seq = NULL;
|
||||
doc->root->seq_size = 0;
|
||||
doc->root->keys = NULL;
|
||||
doc->root->vals = NULL;
|
||||
doc->root->map_size = 0;
|
||||
return doc;
|
||||
}
|
||||
|
||||
YAMLDoc* yaml_load_file(const char *filename) {
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) return NULL;
|
||||
if (!file)
|
||||
return NULL;
|
||||
fseek(file, 0, SEEK_END);
|
||||
long length = ftell(file);
|
||||
rewind(file);
|
||||
@ -136,160 +176,323 @@ cyaml_document_t* cyaml_load_file(const char *filename) {
|
||||
fread(content, 1, length, file);
|
||||
content[length] = '\0';
|
||||
fclose(file);
|
||||
cyaml_document_t *doc = cyaml_load_string(content);
|
||||
YAMLDoc *doc = yaml_load_str(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);
|
||||
}
|
||||
void yaml_doc_destroy(YAMLDoc *doc) {
|
||||
if (!doc)
|
||||
return;
|
||||
if (doc->root)
|
||||
yaml_node_destroy(doc->root);
|
||||
free(doc);
|
||||
}
|
||||
|
||||
const cyaml_node_t* cyaml_document_get_root(const cyaml_document_t *doc) {
|
||||
if (!doc) return NULL;
|
||||
const YAMLNode* yaml_doc_root(const YAMLDoc *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;
|
||||
YAMLType yaml_node_type(const YAMLNode *node) {
|
||||
if (!node)
|
||||
return YAML_UNDEF;
|
||||
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;
|
||||
const char* yaml_as_str(const YAMLNode *node) {
|
||||
if (!node || node->type != YAML_STR)
|
||||
return NULL;
|
||||
return node->str;
|
||||
}
|
||||
|
||||
int cyaml_node_as_int(const cyaml_node_t *node) {
|
||||
if (!node || node->type != CYAML_NODE_SCALAR) return 0;
|
||||
return atoi(node->scalar);
|
||||
int yaml_as_int(const YAMLNode *node) {
|
||||
if (!node || node->type != YAML_STR)
|
||||
return 0;
|
||||
return atoi(node->str);
|
||||
}
|
||||
|
||||
double cyaml_node_as_double(const cyaml_node_t *node) {
|
||||
if (!node || node->type != CYAML_NODE_SCALAR) return 0.0;
|
||||
return atof(node->scalar);
|
||||
double yaml_as_double(const YAMLNode *node) {
|
||||
if (!node || node->type != YAML_STR)
|
||||
return 0.0;
|
||||
return atof(node->str);
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
const YAMLNode* yaml_map_get(const YAMLNode *node, const char *key) {
|
||||
if (!node || node->type != YAML_MAP)
|
||||
return NULL;
|
||||
for (size_t i = 0; i < node->map_size; i++) {
|
||||
if (strcmp(node->keys[i], key) == 0)
|
||||
return node->vals[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;
|
||||
size_t yaml_seq_size(const YAMLNode *node) {
|
||||
if (!node || node->type != YAML_SEQ)
|
||||
return 0;
|
||||
return node->seq_size;
|
||||
}
|
||||
|
||||
const YAMLNode* yaml_seq_index(const YAMLNode *node, size_t index) {
|
||||
if (!node || node->type != YAML_SEQ || index >= node->seq_size)
|
||||
return NULL;
|
||||
return node->seq[index];
|
||||
}
|
||||
|
||||
/* --- Node Creation and Modification --- */
|
||||
|
||||
YAMLNode* yaml_new_str(const char *value) {
|
||||
YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->type = YAML_STR;
|
||||
node->str = strdup(value ? value : "");
|
||||
node->seq = NULL;
|
||||
node->seq_size = 0;
|
||||
node->keys = NULL;
|
||||
node->vals = NULL;
|
||||
node->map_size = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
YAMLNode* yaml_new_seq(void) {
|
||||
YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->type = YAML_SEQ;
|
||||
node->str = NULL;
|
||||
node->seq = NULL;
|
||||
node->seq_size = 0;
|
||||
node->keys = NULL;
|
||||
node->vals = NULL;
|
||||
node->map_size = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
YAMLNode* yaml_new_map(void) {
|
||||
YAMLNode *node = (YAMLNode*)malloc(sizeof(YAMLNode));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->type = YAML_MAP;
|
||||
node->str = NULL;
|
||||
node->seq = NULL;
|
||||
node->seq_size = 0;
|
||||
node->keys = NULL;
|
||||
node->vals = NULL;
|
||||
node->map_size = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
int yaml_seq_append(YAMLNode *node, YAMLNode *child) {
|
||||
if (!node || node->type != YAML_SEQ)
|
||||
return -1;
|
||||
YAMLNode **new_seq = (YAMLNode**)realloc(node->seq, sizeof(YAMLNode*) * (node->seq_size + 1));
|
||||
if (!new_seq)
|
||||
return -1;
|
||||
node->seq = new_seq;
|
||||
node->seq[node->seq_size] = child;
|
||||
node->seq_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;
|
||||
int yaml_map_set(YAMLNode *node, const char *key, YAMLNode *child) {
|
||||
if (!node || node->type != YAML_MAP)
|
||||
return -1;
|
||||
// Append new key/value pair
|
||||
char **new_keys = (char**)realloc(node->keys, sizeof(char*) * (node->map_size + 1));
|
||||
if (!new_keys)
|
||||
return -1;
|
||||
node->keys = new_keys;
|
||||
node->keys[node->map_size] = strdup(key);
|
||||
YAMLNode **new_vals = (YAMLNode**)realloc(node->vals, sizeof(YAMLNode*) * (node->map_size + 1));
|
||||
if (!new_vals)
|
||||
return -1;
|
||||
node->vals = new_vals;
|
||||
node->vals[node->map_size] = child;
|
||||
node->map_size++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void yaml_node_destroy(YAMLNode *node) {
|
||||
if (!node)
|
||||
return;
|
||||
if (node->type == YAML_STR) {
|
||||
free(node->str);
|
||||
} else if (node->type == YAML_SEQ) {
|
||||
for (size_t i = 0; i < node->seq_size; i++) {
|
||||
yaml_node_destroy(node->seq[i]);
|
||||
}
|
||||
free(node->seq);
|
||||
} else if (node->type == YAML_MAP) {
|
||||
for (size_t i = 0; i < node->map_size; i++) {
|
||||
free(node->keys[i]);
|
||||
yaml_node_destroy(node->vals[i]);
|
||||
}
|
||||
free(node->keys);
|
||||
free(node->vals);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
|
||||
/* --- 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);
|
||||
YAMLEmitter* yaml_emitter_create(void) {
|
||||
YAMLEmitter *emitter = (YAMLEmitter*)malloc(sizeof(YAMLEmitter));
|
||||
if (!emitter)
|
||||
return NULL;
|
||||
emitter->buffer = (char*)malloc(YAMLEMIT_INITIAL_CAPACITY);
|
||||
if (!emitter->buffer) {
|
||||
free(emitter);
|
||||
return NULL;
|
||||
}
|
||||
emitter->buffer[0] = '\0';
|
||||
emitter->size = 0;
|
||||
emitter->capacity = CYAML_EMITTER_INITIAL_CAPACITY;
|
||||
emitter->capacity = YAMLEMIT_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;
|
||||
// Internal function: append string to emitter's buffer.
|
||||
static int yaml_emitter_append(YAMLEmitter *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;
|
||||
size_t new_cap = emitter->capacity * 2;
|
||||
while (emitter->size + len + 1 > new_cap)
|
||||
new_cap *= 2;
|
||||
char *new_buf = (char*)realloc(emitter->buffer, new_cap);
|
||||
if (!new_buf)
|
||||
return -1;
|
||||
emitter->buffer = new_buf;
|
||||
emitter->capacity = new_cap;
|
||||
}
|
||||
memcpy(emitter->buffer + emitter->size, str, len + 1);
|
||||
memcpy(emitter->buffer + emitter->size, str, len);
|
||||
emitter->size += len;
|
||||
emitter->buffer[emitter->size] = '\0';
|
||||
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);
|
||||
// Recursive function to emit a YAML node with proper indentation.
|
||||
static int yaml_emit_node(YAMLEmitter *emitter, const YAMLNode *node, int indent) {
|
||||
char indent_str[256];
|
||||
if (indent < (int)sizeof(indent_str)) {
|
||||
memset(indent_str, ' ', indent);
|
||||
indent_str[indent] = '\0';
|
||||
} else {
|
||||
indent_str[0] = '\0';
|
||||
}
|
||||
return -1;
|
||||
char line_buf[1024];
|
||||
int ret;
|
||||
if (!node)
|
||||
return -1;
|
||||
switch (node->type) {
|
||||
case YAML_STR:
|
||||
// Use block scalar style if the string contains newline(s).
|
||||
if (strchr(node->str, '\n')) {
|
||||
ret = yaml_emitter_append(emitter, "|-\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
char *copy = strdup(node->str);
|
||||
char *line = strtok(copy, "\n");
|
||||
while (line) {
|
||||
snprintf(line_buf, sizeof(line_buf), "%s%s\n", indent_str, line);
|
||||
ret = yaml_emitter_append(emitter, line_buf);
|
||||
if (ret) {
|
||||
free(copy);
|
||||
return ret;
|
||||
}
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
free(copy);
|
||||
} else {
|
||||
snprintf(line_buf, sizeof(line_buf), "%s%s\n", indent_str, node->str);
|
||||
ret = yaml_emitter_append(emitter, line_buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case YAML_SEQ:
|
||||
for (size_t i = 0; i < node->seq_size; i++) {
|
||||
snprintf(line_buf, sizeof(line_buf), "%s- ", indent_str);
|
||||
ret = yaml_emitter_append(emitter, line_buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (node->seq[i]->type == YAML_STR) {
|
||||
ret = yaml_emitter_append(emitter, node->seq[i]->str);
|
||||
ret = yaml_emitter_append(emitter, "\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ret = yaml_emitter_append(emitter, "\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = yaml_emit_node(emitter, node->seq[i], indent + 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case YAML_MAP:
|
||||
for (size_t i = 0; i < node->map_size; i++) {
|
||||
snprintf(line_buf, sizeof(line_buf), "%s%s: ", indent_str, node->keys[i]);
|
||||
ret = yaml_emitter_append(emitter, line_buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (node->vals[i]->type == YAML_STR) {
|
||||
ret = yaml_emitter_append(emitter, node->vals[i]->str);
|
||||
ret = yaml_emitter_append(emitter, "\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ret = yaml_emitter_append(emitter, "\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = yaml_emit_node(emitter, node->vals[i], indent + 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cyaml_emitter_to_file(cyaml_emitter_t *emitter, const char *filename) {
|
||||
if (!emitter || !filename) return -1;
|
||||
int yaml_emit(YAMLEmitter *emitter, const YAMLDoc *doc) {
|
||||
if (!emitter || !doc || !doc->root)
|
||||
return -1;
|
||||
return yaml_emit_node(emitter, doc->root, 0);
|
||||
}
|
||||
|
||||
int yaml_emit_to_file(YAMLEmitter *emitter, const char *filename) {
|
||||
if (!emitter || !filename)
|
||||
return -1;
|
||||
FILE *file = fopen(filename, "w");
|
||||
if (!file) return -1;
|
||||
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;
|
||||
char* yaml_emit_to_str(YAMLEmitter *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);
|
||||
void yaml_emitter_destroy(YAMLEmitter *emitter) {
|
||||
if (!emitter)
|
||||
return;
|
||||
free(emitter->buffer);
|
||||
free(emitter);
|
||||
}
|
||||
|
||||
#endif // CYAML_IMPLEMENTATION
|
||||
#endif // YAML_IMPLEMENTATION
|
||||
|
||||
#endif // CYAML_H
|
||||
#endif // YAML_H
|
||||
|
0
users.yaml
Normal file
0
users.yaml
Normal file
Loading…
Reference in New Issue
Block a user