143 lines
4.8 KiB
C
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;
|
|
}
|