Add JSON output format and a separate interpreter binary
deck-engine gains --format json (language-independent, slug-based) so a new dist/interpreter-cli can score how significant a day's transits are without linking the engine itself — it depends only on the JSON shape, via its own minimal parser. Also documents the full reading pipeline end to end (CLAUDE.md, README, docs/) and adds docs/reading.md, a non-technical explanation of what a daily reading contains and means.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
#include "reading_io.h"
|
||||
#include "json.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Mirrors astro.c's own k_body_slug/k_aspect_slug (its i18n lookup-key
|
||||
* tables), duplicated here rather than linking astro.c into this
|
||||
* otherwise header-only, independently testable module - same policy
|
||||
* and same reasoning as significance.c's max_orb_for_pair(). Keep in
|
||||
* sync if those slugs ever change. */
|
||||
static const char *const k_body_slug[NUM_BODIES] = {
|
||||
"sun", "moon", "mercury", "venus", "mars",
|
||||
"jupiter", "saturn", "uranus", "neptune", "pluto",
|
||||
};
|
||||
|
||||
static const char *const k_aspect_slug[5] = {
|
||||
"conjunction", "sextile", "square", "trine", "opposition",
|
||||
};
|
||||
|
||||
static bool body_from_slug(const char *slug, Body *out) {
|
||||
for (int i = 0; i < NUM_BODIES; i++) {
|
||||
if (strcmp(slug, k_body_slug[i]) == 0) {
|
||||
*out = (Body)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool aspect_type_from_slug(const char *slug, AspectType *out) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (strcmp(slug, k_aspect_slug[i]) == 0) {
|
||||
*out = (AspectType)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool reading_load_json(const char *text, size_t length, DailyReading *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
JsonValue *root = json_parse(text, length);
|
||||
if (!root) return false;
|
||||
|
||||
const JsonValue *transits = json_object_get(root, "transits");
|
||||
const JsonValue *aspects = transits ? json_object_get(transits, "aspects") : NULL;
|
||||
if (!aspects) {
|
||||
json_free(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
int filled = 0;
|
||||
int count = json_array_count(aspects);
|
||||
for (int i = 0; i < count && filled < MAX_ASPECTS; i++) {
|
||||
const JsonValue *item = json_array_get(aspects, i);
|
||||
const char *transiting_slug = json_as_string(json_object_get(item, "transiting_planet"));
|
||||
const char *natal_slug = json_as_string(json_object_get(item, "natal_planet"));
|
||||
const char *type_slug = json_as_string(json_object_get(item, "type"));
|
||||
const JsonValue *orb_value = json_object_get(item, "orb");
|
||||
|
||||
Body transiting, natal;
|
||||
AspectType type;
|
||||
if (!body_from_slug(transiting_slug, &transiting) ||
|
||||
!body_from_slug(natal_slug, &natal) ||
|
||||
!aspect_type_from_slug(type_slug, &type) ||
|
||||
!orb_value) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
out->transits.aspects[filled].transiting_planet = transiting;
|
||||
out->transits.aspects[filled].natal_planet = natal;
|
||||
out->transits.aspects[filled].type = type;
|
||||
out->transits.aspects[filled].orb = json_as_number(orb_value);
|
||||
filled++;
|
||||
}
|
||||
out->transits.aspect_count = filled;
|
||||
|
||||
json_free(root);
|
||||
return ok;
|
||||
}
|
||||
Reference in New Issue
Block a user