1 /// Provides single result row or query execution summary.
2 module memgraph.result;
3 
4 import std.string, std.conv;
5 
6 import memgraph.mgclient, memgraph.value, memgraph.map, memgraph.detail;
7 
8 /// An object encapsulating a single result row or query execution summary. It's
9 /// lifetime is limited by lifetime of parent `mg_session`. Also, invoking
10 /// `mg_session_pull` ends the lifetime of previously returned `mg_result`.
11 /// Implements an `InputRange`.
12 struct Result {
13 	/// Returns names of columns output by the current query execution.
14 	auto columns() inout {
15 		assert(ptr_ != null);
16 		assert(*ptr_ != null);
17 		const (mg_list) *list = mg_result_columns(*ptr_);
18 		const size_t list_length = mg_list_size(list);
19 		string[] cols;
20 		cols.length = list_length;
21 		for (uint i = 0; i < list_length; ++i)
22 			cols[i] = Detail.convertString(mg_value_string(mg_list_at(list, i)));
23 		return cols;
24 	}
25 
26 	/// Returns query execution summary as a key/value `Map`.
27 	auto summary() inout {
28 		assert(ptr_ != null);
29 		assert(*ptr_ != null);
30 		return Map(mg_result_summary(*ptr_));
31 	}
32 
33 	/// Check if the `Result` is empty.
34 	/// Return: true if empty, false if there are more rows to be fetched.
35 	/// Note: part of `InputRange` interface
36 	@property bool empty() {
37 		if (values_.length == 0) {
38 			assert(ptr_ != null);
39 			immutable status = mg_session_fetch(session_, ptr_);
40 			if (status != 1)
41 				return true;
42 			const (mg_list) *list = mg_result_row(*ptr_);
43 			const size_t list_length = mg_list_size(list);
44 			Value[] data;
45 			data.length = list_length;
46 			for (uint i = 0; i < list_length; ++i)
47 				data[i] = Value(mg_list_at(list, i));
48 			values_ ~= data;
49 		}
50 		return values_.length == 0;
51 	}
52 	/// Returns the front element of the range.
53 	/// Note: part of `InputRange` interface
54 	auto front() {
55 		assert(values_.length > 0);
56 		return values_[0];
57 	}
58 	/// Pops the first element from the range, shortening the range by one element.
59 	/// Note: part of `InputRange` interface
60 	@property void popFront() {
61 		values_ = values_[1..$];
62 	}
63 
64 	~this() {
65 		import core.stdc.stdlib : free;
66 		refs_--;
67 		if (ptr_ && refs_ == 0)
68 			free(ptr_);
69 	}
70 
71 	this(this) {
72 		refs_++;
73 	}
74 
75 	auto opCast(T : bool)() const {
76 		return ptr_ != null;
77 	}
78 
79 package:
80 	/// Initial construction of a `Result` from the given `mg_session` pointer.
81 	/// Ranges in D first perform a copy of the range object on which they will
82 	/// operate. This means that the original `Result` instance could not be
83 	/// used to e.g. query the summary since it does not have the last `mg_result`.
84 	/// Allocate a reference counted `mg_result` pointer to be shared with all
85 	/// future range copies.
86 	this(mg_session *session) {
87 		assert(session != null);
88 		import core.stdc.stdlib : malloc;
89 		session_ = session;
90 		ptr_ = cast(mg_result **)malloc((mg_result *).sizeof);
91 	}
92 
93 private:
94 	/// Pointer to private `mg_session` instance.
95 	mg_session *session_;
96 	/// Pointer to private `mg_result` instance.
97 	mg_result **ptr_;
98 	/// Temporary value store.
99 	Value[][] values_;
100 	/// Reference count.
101 	uint refs_ = 1;
102 }
103 
104 unittest {
105 	import std.range.primitives : isInputRange;
106 	assert(isInputRange!Result);
107 }