Files
didactyl/src/json_to_markdown.c
T

143 lines
4.8 KiB
C

#define _POSIX_C_SOURCE 200809L
#include "json_to_markdown.h"
#include "cjson/cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int append_text(char** buf, size_t* cap, size_t* used, const char* s) {
if (!buf || !cap || !used || !s) return -1;
size_t n = strlen(s);
if (*used + n + 1U > *cap) {
size_t next = *cap;
while (*used + n + 1U > next) {
next = (next == 0U) ? 256U : (next * 2U);
}
char* grown = (char*)realloc(*buf, next);
if (!grown) return -1;
*buf = grown;
*cap = next;
}
memcpy(*buf + *used, s, n);
*used += n;
(*buf)[*used] = '\0';
return 0;
}
static void print_indent(int indent_level, char** buf, size_t* cap, size_t* used) {
for (int i = 0; i < indent_level; i++) {
append_text(buf, cap, used, " ");
}
}
static void render_json_node(cJSON* node, int indent_level, int is_list_item, char** buf, size_t* cap, size_t* used, int max_array_items) {
if (!node) return;
if (cJSON_IsString(node)) {
if (is_list_item) {
print_indent(indent_level, buf, cap, used);
append_text(buf, cap, used, "- ");
}
append_text(buf, cap, used, node->valuestring ? node->valuestring : "");
append_text(buf, cap, used, "\n");
} else if (cJSON_IsNumber(node)) {
if (is_list_item) {
print_indent(indent_level, buf, cap, used);
append_text(buf, cap, used, "- ");
}
char num_str[64];
if (node->valuedouble == (double)node->valueint) {
snprintf(num_str, sizeof(num_str), "%d", node->valueint);
} else {
snprintf(num_str, sizeof(num_str), "%f", node->valuedouble);
}
append_text(buf, cap, used, num_str);
append_text(buf, cap, used, "\n");
} else if (cJSON_IsBool(node)) {
if (is_list_item) {
print_indent(indent_level, buf, cap, used);
append_text(buf, cap, used, "- ");
}
append_text(buf, cap, used, cJSON_IsTrue(node) ? "true\n" : "false\n");
} else if (cJSON_IsNull(node)) {
if (is_list_item) {
print_indent(indent_level, buf, cap, used);
append_text(buf, cap, used, "- null\n");
}
} else if (cJSON_IsObject(node)) {
cJSON* child = node->child;
int first = 1;
while (child) {
if (is_list_item && first) {
print_indent(indent_level, buf, cap, used);
append_text(buf, cap, used, "- **");
first = 0;
} else {
print_indent(indent_level + (is_list_item ? 1 : 0), buf, cap, used);
append_text(buf, cap, used, "- **");
}
append_text(buf, cap, used, child->string ? child->string : "unknown");
append_text(buf, cap, used, "**: ");
if (cJSON_IsObject(child) || cJSON_IsArray(child)) {
append_text(buf, cap, used, "\n");
render_json_node(child, indent_level + (is_list_item ? 2 : 1), 0, buf, cap, used, max_array_items);
} else {
render_json_node(child, 0, 0, buf, cap, used, max_array_items);
}
child = child->next;
}
} else if (cJSON_IsArray(node)) {
int count = cJSON_GetArraySize(node);
int limit = (max_array_items > 0 && count > max_array_items) ? max_array_items : count;
for (int i = 0; i < limit; i++) {
cJSON* item = cJSON_GetArrayItem(node, i);
render_json_node(item, indent_level, 1, buf, cap, used, max_array_items);
}
if (count > limit) {
print_indent(indent_level, buf, cap, used);
char trunc_msg[64];
snprintf(trunc_msg, sizeof(trunc_msg), "- *(... and %d more items)*\n", count - limit);
append_text(buf, cap, used, trunc_msg);
}
}
}
char* json_to_markdown(const char* json_string, int max_array_items) {
if (!json_string) return NULL;
cJSON* root = cJSON_Parse(json_string);
if (!root) {
// If it's not valid JSON, just return a copy of the string
return strdup(json_string);
}
// If it's a plain string, return it without markdown formatting
if (cJSON_IsString(root)) {
char* out = strdup(root->valuestring ? root->valuestring : "");
cJSON_Delete(root);
return out;
}
size_t cap = 1024;
size_t used = 0;
char* buf = (char*)malloc(cap);
if (!buf) {
cJSON_Delete(root);
return NULL;
}
buf[0] = '\0';
render_json_node(root, 0, 0, &buf, &cap, &used, max_array_items);
cJSON_Delete(root);
// Trim trailing newline if present
if (used > 0 && buf[used - 1] == '\n') {
buf[used - 1] = '\0';
}
return buf;
}