aboutsummaryrefslogtreecommitdiff
path: root/op.c
diff options
context:
space:
mode:
Diffstat (limited to 'op.c')
-rw-r--r--op.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/op.c b/op.c
new file mode 100644
index 0000000..ead1ae5
--- /dev/null
+++ b/op.c
@@ -0,0 +1,183 @@
+/*
+ * nanobbs, a tiny forums software.
+ * Copyright (C) 2025-2026 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "op.h"
+#include <libweb/http.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct op
+{
+ sqlite3_stmt *stmt;
+ struct op_cfg cfg;
+};
+
+static int teardown(struct op *const op)
+{
+ int ret = 0, error;
+
+ if (!op)
+ return 0;
+ else if ((error = sqlite3_finalize(op->stmt)) != SQLITE_OK)
+ {
+ fprintf(stderr, "%s: sqlite3_finalize: %s\n", __func__,
+ sqlite3_errstr(error));
+ ret = -1;
+ }
+
+ const struct op_cfg *const cfg = &op->cfg;
+
+ if (cfg->error)
+ cfg->error(cfg->args);
+
+ free(op);
+ return ret;
+}
+
+static void run_error(void *const p)
+{
+ teardown(p);
+}
+
+static int run(const struct http_payload *const pl,
+ struct http_response *const r, void *const user, void *const args)
+{
+ struct op *const op = args;
+ sqlite3_stmt *const stmt = op->stmt;
+ const struct op_cfg *const cfg = &op->cfg;
+ int ret = -1, error = sqlite3_step(stmt);
+
+ switch (error)
+ {
+ case SQLITE_BUSY:
+ break;
+
+ case SQLITE_ROW:
+ if (!cfg->row)
+ {
+ fprintf(stderr, "%s: expected row callback\n", __func__);
+ goto failure;
+ }
+ else if ((ret = cfg->row(op->stmt, pl, r, user, cfg->args)))
+ goto failure;
+
+ break;
+
+ case SQLITE_CONSTRAINT:
+ sqlite3_reset(op->stmt);
+
+ if (!cfg->constraint)
+ {
+ fprintf(stderr, "%s: expected constraint callback\n", __func__);
+ goto failure;
+ }
+ else if ((ret = cfg->constraint(cfg->args)))
+ goto failure;
+
+ /* Fall through. */
+ case SQLITE_DONE:
+ {
+ const int error = sqlite3_finalize(op->stmt);
+
+ if (error != SQLITE_OK)
+ {
+ fprintf(stderr, "%s: sqlite3_finalize: %s\n", __func__,
+ sqlite3_errstr(error));
+ ret = -1;
+ goto failure;
+ }
+ else if (cfg->end && (ret = cfg->end(pl, r, user, cfg->args)))
+ goto failure;
+
+ free(op);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "%s: sqlite3_step: %s\n", __func__,
+ sqlite3_errstr(error));
+ sqlite3_reset(stmt);
+ goto failure;
+ }
+
+ return 0;
+
+failure:
+
+ if (teardown(op))
+ {
+ fprintf(stderr, "%s: teardown failed\n", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+void op_resume(struct op *const op, struct http_response *const r)
+{
+ *r = (const struct http_response)
+ {
+ .step.payload = run,
+ .free = run_error,
+ .args = op
+ };
+}
+
+struct op *op_run(sqlite3 *const db, const char *const query,
+ const struct op_cfg *const cfg, struct http_response *const r)
+{
+ sqlite3_stmt *stmt = NULL;
+ struct op *const ret = malloc(sizeof *ret);
+
+ if (!ret)
+ {
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
+ goto failure;
+ }
+
+ const int error = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
+
+ if (error != SQLITE_OK && error != SQLITE_BUSY)
+ {
+ fprintf(stderr, "%s: sqlite3_prepare_v2 \"%s\": %s\n", __func__,
+ query, sqlite3_errstr(error));
+ goto failure;
+ }
+
+ *r = (const struct http_response)
+ {
+ .step.payload = run,
+ .free = run_error,
+ .args = ret
+ };
+
+ *ret = (const struct op)
+ {
+ .stmt = stmt,
+ .cfg = *cfg
+ };
+
+ return ret;
+
+failure:
+ free(ret);
+ sqlite3_finalize(stmt);
+ return NULL;
+}