aboutsummaryrefslogtreecommitdiffstats
path: root/src/inserter.c
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2023-03-31 17:17:41 -0400
committerDavid Robillard <d@drobilla.net>2023-12-02 18:49:08 -0500
commitb5956c4dc6b065d664908104d5fc6752a87e3364 (patch)
tree6be1fa515891e759092bb9bea082e27c78bfb6de /src/inserter.c
parent439d6ec3d6dfbea74334beace790f500e61c9b7d (diff)
downloadserd-b5956c4dc6b065d664908104d5fc6752a87e3364.tar.gz
serd-b5956c4dc6b065d664908104d5fc6752a87e3364.tar.bz2
serd-b5956c4dc6b065d664908104d5fc6752a87e3364.zip
Add model and serd-sort utility
With all the new functionality, the complexity of the serd-pipe command-line interface is starting to push the limits of available flags. So, instead of grafting on further options to control a model, this commit adds a new tool, serd-sort, which acts somewhat like a stripped-down serd-pipe that stores statements in a model in memory. This keeps the complexity (including the user-facing complexity) of any one tool down, since other more focused tools can be used for streaming tasks in a pipeline. In other words, abandon Swissarmyknifeism, take a page from the Unix philosophy, and try to expose the model functionality to the command-line in a dedicated focused tool. The model implementation is tested by using this tool to run a subset of the usual test suites, and a special suite to test statement sorting.
Diffstat (limited to 'src/inserter.c')
-rw-r--r--src/inserter.c146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/inserter.c b/src/inserter.c
new file mode 100644
index 00000000..f3b8631b
--- /dev/null
+++ b/src/inserter.c
@@ -0,0 +1,146 @@
+// Copyright 2011-2020 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#include "memory.h"
+#include "model.h"
+#include "statement.h"
+
+#include "serd/caret.h"
+#include "serd/event.h"
+#include "serd/inserter.h"
+#include "serd/log.h"
+#include "serd/model.h"
+#include "serd/node.h"
+#include "serd/nodes.h"
+#include "serd/sink.h"
+#include "serd/statement.h"
+#include "serd/status.h"
+#include "serd/uri.h"
+#include "serd/world.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef struct {
+ SerdModel* model;
+ SerdNode* default_graph;
+} SerdInserterData;
+
+static bool
+can_insert(SerdWorld* const world, const SerdNode* const node)
+{
+ if (node) {
+ switch (serd_node_type(node)) {
+ case SERD_LITERAL:
+ return can_insert(world, serd_node_datatype(node));
+
+ case SERD_URI:
+ if (!serd_uri_string_has_scheme(serd_node_string(node))) {
+ serd_logf(world,
+ SERD_LOG_LEVEL_ERROR,
+ "attempt to insert relative URI <%s> into model",
+ serd_node_string(node));
+ return false;
+ }
+ break;
+
+ case SERD_BLANK:
+ case SERD_VARIABLE:
+ break;
+ }
+ }
+
+ return true;
+}
+
+static SerdStatus
+serd_inserter_on_statement(SerdInserterData* const data,
+ const SerdStatementFlags flags,
+ const SerdStatement* const statement)
+{
+ (void)flags;
+
+ SerdModel* const model = data->model;
+ SerdWorld* const world = model->world;
+
+ // Check that every node is expanded so it is context-free
+ for (unsigned i = 0; i < 4; ++i) {
+ if (!can_insert(world, serd_statement_node(statement, (SerdField)i))) {
+ return SERD_BAD_DATA;
+ }
+ }
+
+ const SerdNode* const s =
+ serd_nodes_intern(model->nodes, serd_statement_subject(statement));
+
+ const SerdNode* const p =
+ serd_nodes_intern(model->nodes, serd_statement_predicate(statement));
+
+ const SerdNode* const o =
+ serd_nodes_intern(model->nodes, serd_statement_object(statement));
+
+ const SerdNode* const g = serd_nodes_intern(
+ model->nodes,
+ serd_statement_graph(statement) ? serd_statement_graph(statement)
+ : data->default_graph);
+
+ const SerdCaret* const caret =
+ (data->model->flags & SERD_STORE_CARETS) ? statement->caret : NULL;
+
+ const SerdStatus st =
+ serd_model_add_with_caret(data->model, s, p, o, g, caret);
+
+ return st > SERD_FAILURE ? st : SERD_SUCCESS;
+}
+
+static SerdStatus
+serd_inserter_on_event(void* const handle, const SerdEvent* const event)
+{
+ SerdInserterData* const data = (SerdInserterData*)handle;
+
+ if (event->type == SERD_STATEMENT) {
+ return serd_inserter_on_statement(
+ data, event->statement.flags, event->statement.statement);
+ }
+
+ return SERD_SUCCESS;
+}
+
+static SerdInserterData*
+serd_inserter_data_new(SerdModel* const model,
+ const SerdNode* const default_graph)
+{
+ SerdInserterData* const data =
+ (SerdInserterData*)serd_wcalloc(model->world, 1, sizeof(SerdInserterData));
+
+ if (data) {
+ data->model = model;
+ data->default_graph = serd_node_copy(model->allocator, default_graph);
+ }
+
+ return data;
+}
+
+static void
+serd_inserter_data_free(void* const ptr)
+{
+ SerdInserterData* const data = (SerdInserterData*)ptr;
+ serd_node_free(data->model->allocator, data->default_graph);
+ serd_wfree(data->model->world, data);
+}
+
+SerdSink*
+serd_inserter_new(SerdModel* const model, const SerdNode* const default_graph)
+{
+ assert(model);
+
+ SerdEventFunc func = serd_inserter_on_event;
+ SerdInserterData* const data = serd_inserter_data_new(model, default_graph);
+
+ return data ? serd_sink_new(serd_world_allocator(model->world),
+ data,
+ func,
+ (SerdFreeFunc)serd_inserter_data_free)
+ : NULL;
+}