Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions ext/rubydex/graph.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "graph.h"
#include "declaration.h"
#include "definition.h"
#include "diagnostic.h"
#include "document.h"
#include "location.h"
Expand Down Expand Up @@ -847,6 +848,84 @@ static VALUE rdxr_query_render(int argc, VALUE *argv, VALUE self) {
return output;
}

// Converts a structured result cell into a Ruby value. Node cells become real graph handles
// (Declaration / Definition / Document) built against `graph_obj`; lists recurse.
static VALUE cypher_cell_to_value(VALUE graph_obj, const struct CCell *cell) {
switch (cell->tag) {
case CCellTag_Null:
return Qnil;
case CCellTag_Bool:
return cell->payload.bool_val ? Qtrue : Qfalse;
case CCellTag_Int:
return LL2NUM(cell->payload.int_val);
case CCellTag_Str:
return cell->payload.str_val == NULL ? Qnil : rb_utf8_str_new_cstr(cell->payload.str_val);
case CCellTag_List: {
VALUE array = rb_ary_new_capa((long)cell->payload.list.len);
for (size_t i = 0; i < cell->payload.list.len; i++) {
rb_ary_push(array, cypher_cell_to_value(graph_obj, &cell->payload.list.items[i]));
}
return array;
}
case CCellTag_Node: {
VALUE argv[] = {graph_obj, ULL2NUM(cell->payload.node.id)};
VALUE klass;
switch (cell->payload.node.category) {
case CNodeCategory_Declaration:
klass = rdxi_declaration_class_for_kind((CDeclarationKind)cell->payload.node.kind);
break;
case CNodeCategory_Definition:
klass = rdxi_definition_class_for_kind((DefinitionKind)cell->payload.node.kind);
break;
case CNodeCategory_Document:
default:
klass = cDocument;
break;
}
return rb_class_new_instance(2, argv, klass);
}
default:
return Qnil;
}
}

// Rubydex::Query#run(graph) -> Array[Hash[String, Object]]
// Runs this parsed query against the given graph and returns the rows as Ruby objects: each row is a
// Hash keyed by RETURN column name. Scalar cells become String/Integer/true/false/nil, lists become
// Arrays, and node cells become Declaration / Definition / Document handles. Raises ArgumentError on
// an execution error.
static VALUE rdxr_query_run(VALUE self, VALUE graph_obj) {
void *query;
TypedData_Get_Struct(self, void *, &query_type, query);

void *graph;
TypedData_Get_Struct(graph_obj, void *, &graph_type, graph);

struct CRunRows run = rdx_query_run_rows(query, graph);

if (run.error != NULL) {
VALUE message = rb_utf8_str_new_cstr(run.error);
free_c_string(run.error);
rb_raise(rb_eArgError, "%s", StringValueCStr(message));
}

struct CResultSet *result = run.result;
VALUE rows = rb_ary_new_capa((long)result->row_count);

for (size_t r = 0; r < result->row_count; r++) {
struct CResultRow row = result->rows[r];
VALUE hash = rb_hash_new();
for (size_t c = 0; c < row.len && c < result->column_count; c++) {
VALUE key = rb_utf8_str_new_cstr(result->columns[c]);
rb_hash_aset(hash, key, cypher_cell_to_value(graph_obj, &row.cells[c]));
}
rb_ary_push(rows, hash);
}

rdx_result_set_free(result);
return rows;
}

void rdxi_initialize_graph(VALUE moduleRubydex) {
mRubydex = moduleRubydex;
cGraph = rb_define_class_under(mRubydex, "Graph", rb_cObject);
Expand Down Expand Up @@ -886,5 +965,6 @@ void rdxi_initialize_graph(VALUE moduleRubydex) {
rb_undef_alloc_func(cQuery);
rb_define_singleton_method(cQuery, "parse", rdxr_query_parse, 1);
rb_define_singleton_method(cQuery, "schema", rdxr_cypher_schema, -1);
rb_define_method(cQuery, "run", rdxr_query_run, 1);
rb_define_method(cQuery, "render", rdxr_query_render, -1);
}
3 changes: 3 additions & 0 deletions rbi/rubydex.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ class Rubydex::Query
def schema(format = :table); end
end

sig { params(graph: Rubydex::Graph).returns(T::Array[T::Hash[String, T.untyped]]) }
def run(graph); end

sig { params(graph: Rubydex::Graph, format: T.any(String, Symbol)).returns(String) }
def render(graph, format = :table); end
end
Expand Down
4 changes: 2 additions & 2 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading