diff options
| author | eunhhu <[email protected]> | 2026-09-27 13:14:38 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-27 13:14:38 +0100 |
| commit | 37d3a1e152707d2da587440c4cea8601b9ef0fd2 (patch) | |
| tree | 1a4cbea9d8ca636deabd8fc24b0d73cdae2bdaef /prototypes/connections/edit-module.c | |
| parent | 5372d03b5968796515966e2cfc89628d6448f0c1 (diff) | |
| download | pure-37d3a1e152707d2da587440c4cea8601b9ef0fd2.tar.gz pure-37d3a1e152707d2da587440c4cea8601b9ef0fd2.zip | |
Lay Pure out as a core with the parts selected around it
The app model, the tree store and the HTTP host lived in
prototypes/connections among the earlier document runtime, the store was
known only by its format's name, and Vega compiled six Pure sources by path.
Now, as the README's principle has it:
core/ runtime, flow and the app model (module*.c), with what the core
asks of storage (state.h, store.h)
store/ the tree store (PURETRE3, PUREREC1)
serve/ pure-serve: HTTP host, outbound delivery, JSON in place
cli/ pure-run, pure-edit-module
A top-level make builds libpure.a (app model and store, for hosts to link)
and the hosts; make check runs the store's and the app model's tests. The
earlier runtime still builds from prototypes/connections against core/ and
store/ (check, check-tree and check-core pass). Design 009 records the rule:
parts by role behind libraries; git repositories split by coupling, so the
store stays here while its contract moves with the core, and Vega stays apart.
Diffstat (limited to 'prototypes/connections/edit-module.c')
| -rw-r--r-- | prototypes/connections/edit-module.c | 179 |
1 files changed, 0 insertions, 179 deletions
diff --git a/prototypes/connections/edit-module.c b/prototypes/connections/edit-module.c deleted file mode 100644 index 3ce272e..0000000 --- a/prototypes/connections/edit-module.c +++ /dev/null @@ -1,179 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "module-store.h" -#include "store.h" -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -/* The document CLI contract of edit.c, driven by a verified Pure module - * instead of a document program. The module declares keyed messages named - * show and edit; edit has u32 fields start and remove and a bytes tail. */ -static bool number(const char *text, uint64_t limit, uint64_t *out) { - if (!*text) return false; - uint64_t value = 0; - for (; *text; ++text) { - if (*text < '0' || *text > '9') return false; - unsigned digit = (unsigned)(*text - '0'); - if (digit > limit || value > (limit - digit) / 10) return false; - value = value * 10 + digit; - } - *out = value; - return true; -} - -static const char *message(PureResult status) { - switch (status) { - case PURE_OK: return "ok"; - case PURE_NOT_FOUND: return "not_found"; - case PURE_CONFLICT: return "conflict"; - case PURE_BUSY: return "busy"; - case PURE_BUDGET: return "payload_budget"; - case PURE_LIMIT: return "limit"; - case PURE_UNCERTAIN: return "durability_unknown"; - case PURE_DENIED: return "denied"; - case PURE_ALLOCATION: return "allocation"; - case PURE_INVALID: return "invalid"; - default: return "failed"; - } -} - -static bool valid_key(const char *key) { - size_t length = strlen(key); - if (!length || length > 64) return false; - for (size_t i = 0; i < length; ++i) { - char c = key[i]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || - c == '-' || c == '_')) return false; - } - return true; -} - -static void put32(unsigned char *to, uint32_t value) { - for (unsigned i = 0; i < 4; ++i) to[i] = (unsigned char)(value >> (8 * i)); -} - -static int field(const PureModule *m, int input, const char *name, uint8_t type) { - const PureInputShape *shape = &m->input[input]; - for (uint32_t i = 0; i < shape->field_count; ++i) - if (!strcmp(m->field[shape->field_start + i].name, name) && m->field[shape->field_start + i].type == type) - return (int)(shape->field_start + i); - return -1; -} - -static bool write_span(void *context, PureSpan span) { - return fwrite(span.data, 1, span.size, context) == span.size; -} - -static bool count_span(void *context, PureSpan span) { - *(size_t *)context += span.size; - return true; -} - -int main(int argc, char **argv) { - const char *module_path = NULL, *store_path = NULL; - size_t budget = 4u * 1024u * 1024u; - int at = 1; - for (; at + 1 < argc && !strncmp(argv[at], "--", 2); at += 2) { - uint64_t value; - if (!strcmp(argv[at], "--store")) store_path = argv[at + 1]; - else if (!strcmp(argv[at], "--module")) module_path = argv[at + 1]; - else if (!strcmp(argv[at], "--budget") && number(argv[at + 1], SIZE_MAX, &value)) budget = (size_t)value; - else goto usage; - } - if (!module_path || !store_path || at >= argc) goto usage; - bool show = !strcmp(argv[at], "show"), edit = !strcmp(argv[at], "edit"); - if ((!show && !edit && strcmp(argv[at], "apply")) || - argc - at != (show ? 2 : edit ? 6 : 4)) goto usage; - uint64_t expected = 0, offset = 0, remove = 0; - if (!show && !number(argv[at + 2], INT64_MAX, &expected)) goto usage; - if (edit && (!number(argv[at + 3], UINT32_MAX, &offset) || !number(argv[at + 4], UINT32_MAX, &remove))) goto usage; - - static PureModule module; - char error[160]; - PureResult read = pure_module_read(module_path, &module, error, sizeof(error)); - if (read != PURE_OK) { - fprintf(stderr, "Module: %s\n", read == PURE_NOT_FOUND ? "cannot read module" : error); - return 2; - } - int input = pure_module_input(&module, show ? "show" : "edit"); - if (input < 0 || module.input[input].kind != PURE_KIND_MESSAGE || !module.input[input].keyed) { - fprintf(stderr, "Module: needs a keyed message named %s\n", show ? "show" : "edit"); - return 2; - } - const PureInputShape *shape = &module.input[input]; - int start_field = field(&module, input, "start", PURE_FIELD_U32); - int remove_field = field(&module, input, "remove", PURE_FIELD_U32); - size_t prefix = edit ? shape->prefix : 0; - if (edit && (start_field < 0 || remove_field < 0 || !shape->field_count || - module.field[shape->field_start + shape->field_count - 1].type != PURE_FIELD_BYTES)) { - fputs("edit requires u32 fields start and remove and trailing insertion bytes; use apply for a custom input\n", stderr); - return 2; - } - FILE *file = NULL; - size_t length = 0; - if (!show) { - file = fopen(argv[at + (edit ? 5 : 3)], "rb"); - if (!file) { perror("input"); return 2; } - long bytes; - if (fseek(file, 0, SEEK_END) || (bytes = ftell(file)) < 0 || - (uintmax_t)bytes > PURE_OBJECT_LIMIT - prefix || fseek(file, 0, SEEK_SET)) { - fputs("input must be a seekable file within the request size limit\n", stderr); - fclose(file); return 2; - } - length = (size_t)bytes; - } - if (pure_store_init(store_path) != PURE_OK) { - if (file) fclose(file); - fputs("cannot initialize store\n", stderr); return 2; - } - const char *key = argv[at + 1]; - if (!valid_key(key) || prefix + length > budget) { - if (file) fclose(file); - fprintf(stderr, "%s\n", valid_key(key) ? "payload_budget" : "invalid"); return 2; - } - unsigned char *payload = NULL; - if (!show) { - payload = calloc(1, prefix + length ? prefix + length : 1); - if (!payload) { fclose(file); fputs("allocation\n", stderr); return 2; } - if (edit) { - put32(payload + module.field[start_field].offset, (uint32_t)offset); - put32(payload + module.field[remove_field].offset, (uint32_t)remove); - } - bool valid = !length || fread(payload + prefix, 1, length, file) == length; - if (fgetc(file) != EOF || ferror(file)) valid = false; - if (fclose(file)) valid = false; - if (!valid) { - free(payload); - fputs("input changed or could not be read; nothing was applied\n", stderr); return 2; - } - } - PureApp app; - if (pure_app_start(&app, &module, pure_tree_data(store_path)) != PURE_OK) { - free(payload); - fputs("allocation\n", stderr); return 2; - } - PureResult status = pure_app_message(&app, (uint32_t)input, key, show ? -1 : (int64_t)expected, - (PureSpan){payload, prefix + length}); - size_t reply = 0; - if (status == PURE_OK) status = pure_app_reply(&app, count_span, &reply); - if (status == PURE_OK && show && reply > budget) { status = PURE_BUDGET; app.revision = 0; } - int exit_code = 0; - if (status != PURE_OK) { - fprintf(stderr, "{\"error\":\"%s\",\"revision\":\"%" PRId64 "\"}\n", message(status), app.revision); - exit_code = status == PURE_CONFLICT ? 3 : 2; - } else if (show) { - if (pure_app_reply(&app, write_span, stdout) != PURE_OK) exit_code = 2; - fprintf(stderr, "{\"revision\":\"%" PRId64 "\",\"bytes\":%zu}\n", app.revision, reply); - } else if (printf("{\"revision\":\"%" PRId64 "\",\"reply_bytes\":%zu}\n", app.revision, reply) < 0) exit_code = 2; - if (fflush(stdout)) exit_code = 2; - pure_app_stop(&app); - free(payload); - return exit_code; -usage: - fputs("pure-edit-module --module FILE --store DIRECTORY [--budget BYTES] show KEY\n" - "pure-edit-module --module FILE --store DIRECTORY [--budget BYTES] edit KEY REVISION BYTE_OFFSET REMOVE_BYTES INSERT_FILE\n" - "pure-edit-module --module FILE --store DIRECTORY [--budget BYTES] apply KEY REVISION INPUT_FILE\n" - "edit uses the module's start and remove fields; apply accepts its raw message bytes.\n", stderr); - return 2; -} |
