ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
graphql.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file graphql.h
6 * @brief Zero-heap GraphQL executor over a query subset (PROTOCORE_ENABLE_GRAPHQL).
7 *
8 * **The governing standard is not IETF.** GraphQL is specified by the GraphQL Foundation and
9 * published at spec.graphql.org, released by date. Every section cited in this module is the
10 * **October 2021** release. There is no RFC for GraphQL.
11 *
12 * The document source text is parsed into fixed pools (no heap) by the sec 2 grammar, the operation
13 * is executed by the sec 6 algorithms, and the result is serialized as the sec 7.1 response map in
14 * the sec 7.2.1 JSON form. The client picks the shape: sec 2.4 says an operation "selects the set of
15 * information it needs, and will receive exactly that information and nothing more".
16 *
17 * **Schema-free model.** There is no type system (sec 3). A Field carrying a SelectionSet
18 * (`obj { a b }`, sec 2.5) is completed by executing that selection set; a Field with none is a
19 * scalar leaf, completed by calling the resolver (ResolveFieldValue, sec 6.4.2). Arguments met along
20 * the path (sec 2.6) stay in scope, so a resolver for `sensor.value` reads `id` from
21 * `sensor(id: 2) { value }`. The application implements one function: the value of the scalar at
22 * this dotted path, given the arguments in scope.
23 *
24 * Supported: one operation, either the sec 2.3 query shorthand (`{...}`) or `query [Name] {...}`;
25 * nested selection sets (sec 2.4); field arguments (sec 2.6) taking Int, Float, String, Boolean and
26 * Null values (sec 2.9.1 to sec 2.9.5); comments (sec 2.1.4) and insignificant commas (sec 2.1.5).
27 * Out of scope: mutations and subscriptions (sec 2.3), fragments (sec 2.8), variables (sec 2.10),
28 * directives (sec 2.12), aliases (sec 2.7), list values (sec 2.9.7), input objects (sec 2.9.8),
29 * block strings and `\uXXXX` escapes (sec 2.9.4), and lists of objects (sec 3.11). Each parses as a
30 * request error.
31 *
32 * Two deviations from the released spec, both stated rather than hidden: Int is carried as a 64-bit
33 * value where sec 3.5.1 defines a signed 32-bit scalar, and a leaf that fails to resolve completes
34 * as null with no `errors` entry, where sec 7.1.2 says a field error should be listed.
35 *
36 * A malformed document raises a request error (sec 7.1.2): execution does not begin and the response
37 * map carries `errors` and no `data`.
38 *
39 * Bounds are compile-time (PROTOCORE_GQL_*); parsing and execution allocate nothing.
40 *
41 * The module exports one symbol, @ref GraphQL. Everything in graphql.c has internal linkage.
42 *
43 * @author Douglas Quigg (dstroy0)
44 * @date 2026
45 */
46
47#ifndef PROTOCORE_GRAPHQL_H
48#define PROTOCORE_GRAPHQL_H
49
50#include "protocore_config.h" // the entry point: protocore_types.h for the widths
51
52#if PROTOCORE_ENABLE_GRAPHQL
53
55
56/** @brief The scalar kinds a resolved leaf or an argument carries (GraphQL spec sec 3.5). */
57typedef enum PROTO_ENUM_PACKED
58{
59 PROTOCORE_GQL_NULL = 0, ///< the Null value (sec 2.9.5).
60 PROTOCORE_GQL_INT, ///< Int (sec 3.5.1), read from @c i.
61 PROTOCORE_GQL_FLOAT, ///< Float (sec 3.5.2), read from @c f.
62 PROTOCORE_GQL_BOOL, ///< Boolean (sec 3.5.4), read from @c b.
63 PROTOCORE_GQL_STR, ///< String (sec 3.5.3), read from @c s, NUL-terminated and stable for the call.
64} protocore_gql_type;
65
66/** @brief One scalar: a resolved field value, or an argument's Value (spec sec 2.9). */
67typedef struct
68{
69 protocore_gql_type type; ///< which member below holds the value.
70 long long i; ///< the Int.
71 double f; ///< the Float.
72 proto_bool b; ///< the Boolean.
73 const char *s; ///< the String.
74} protocore_gql_value;
75
76/** @brief The argument values in scope at a resolved field (spec sec 6.4.1 coercedValues). */
77struct protocore_gql_args;
78
79/**
80 * @brief ResolveFieldValue (spec sec 6.4.2): the scalar at dotted @p path, e.g. "device.uptime".
81 *
82 * Fills @p out and returns true, or returns false to complete the field as null (sec 6.4.3).
83 * @p args names the argument values in scope, read back through ::GraphQLNs::arg_int,
84 * ::GraphQLNs::arg_str and ::GraphQLNs::arg_bool.
85 */
86typedef proto_bool (*protocore_gql_resolver_fn)(const char *path, const struct protocore_gql_args *args,
87 protocore_gql_value *out);
88
89/** @brief What one execute reports. */
90typedef enum PROTO_ENUM_PACKED
91{
92 PROTOCORE_GQL_OK = 0, ///< Executed; the response map holds `data` (spec sec 7.1.1).
93 PROTOCORE_GQL_ERR_PARSE = -1, ///< Request error (sec 7.1.2): the document does not parse.
94 PROTOCORE_GQL_ERR_LIMIT = -2, ///< Request error (sec 7.1.2): a PROTOCORE_GQL_* bound was exceeded.
95 PROTOCORE_GQL_ERR_OVERFLOW = -3 ///< The serialized response did not fit the buffer.
96} protocore_gql_result;
97
98/** @brief ExecuteRequest (spec sec 6.1): the document to run and the resolver its leaves call. */
99typedef struct
100{
101 const char *document; ///< the ExecutableDocument source text (sec 2.2)
102 size_t len; ///< how many octets of it there are
103 protocore_gql_resolver_fn resolver; ///< ResolveFieldValue (sec 6.4.2); NULL completes every leaf as null
104} GraphQLRequestArgs;
105
106/** @brief Where the response map is serialized (spec sec 7.1, in the sec 7.2.1 JSON form). */
107typedef struct
108{
109 char *out; ///< the buffer the response is written into
110 size_t cap; ///< how much room it has, the NUL included
111} GraphQLResponseArgs;
112
113/** @brief One Argument read by name out of the values in scope (spec sec 2.6). */
114typedef struct
115{
116 const struct protocore_gql_args *values; ///< the argument values a resolver was handed (sec 6.4.1)
117 const char *name; ///< the argument's Name, matched case-sensitively (sec 2.1.9)
118} GraphQLArgumentArgs;
119
120/**
121 * @brief The GraphQL executor (GraphQL spec, October 2021 release; not an IETF standard).
122 *
123 * A caller sets the members a call takes, invokes it through ::GraphQL, and reads the outcome off
124 * the same handle. A resolver running inside an execute sets @c argument and reads @c ok with
125 * @c i64, @c text or @c b.
126 *
127 * No slot member: one document executes at a time, so no call names a row.
128 *
129 * @var GraphQLNs::request the document an execute runs and the resolver its leaves call (sec 6.1)
130 * @var GraphQLNs::response where an execute serializes the response map (sec 7.1)
131 * @var GraphQLNs::argument the argument an accessor reads, and the values it reads from (sec 2.6)
132 * @var GraphQLNs::ok a call's true/false outcome: an execute succeeded, or an argument was
133 * present with the type the accessor asked for
134 * @var GraphQLNs::n octets an execute wrote to @c response.out, excluding the NUL, 0 if
135 * nothing was emitted
136 * @var GraphQLNs::result an execute's outcome code
137 * @var GraphQLNs::i64 the Int an arg_int read (sec 3.5.1), 0 when @c ok is false
138 * @var GraphQLNs::text the String an arg_str read (sec 3.5.3), NULL when @c ok is false
139 * @var GraphQLNs::b the Boolean an arg_bool read (sec 3.5.4), false when @c ok is false
140 * @var GraphQLNs::execute parse @c request, execute its operation, and serialize the response map
141 * into @c response (sec 6.1)
142 * @var GraphQLNs::arg_int read @c argument as an Int
143 * @var GraphQLNs::arg_str read @c argument as a String
144 * @var GraphQLNs::arg_bool read @c argument as a Boolean
145 */
146typedef struct
147{
148 GraphQLRequestArgs request; ///< what an execute runs
149 GraphQLResponseArgs response; ///< where its response lands
150 GraphQLArgumentArgs argument; ///< what an accessor reads
151 proto_bool ok;
152 size_t n;
153 protocore_gql_result result;
154 long long i64;
155 const char *text;
156 proto_bool b;
157} GraphQLVars;
158
159/** @brief The operands and the outcome. */
160extern GraphQLVars GraphQLV;
161
162/** @brief The entries. */
163typedef struct
164{
165 void (*const execute)(uint8_t *work);
166 void (*const arg_int)(uint8_t *work);
167 void (*const arg_str)(uint8_t *work);
168 void (*const arg_bool)(uint8_t *work);
169} GraphQLNs;
170
171// What the table binds, defined once in the .c and taking one parameter each: everything
172// else an entry needs is an operand in GraphQLV or a region of the borrow at a fixed offset.
173void protocore_graph_ql_execute(uint8_t *work);
174void protocore_graph_ql_arg_int(uint8_t *work);
175void protocore_graph_ql_arg_str(uint8_t *work);
176void protocore_graph_ql_arg_bool(uint8_t *work);
177
178// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
179// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
180// `GraphQL.execute(work)` resolves to a named function and becomes a DIRECT call. An extern table
181// leaves the call indirect and the symbol live at every level, -O2 -flto included.
182static const GraphQLNs GraphQL __attribute__((unused)) = {
183 .execute = protocore_graph_ql_execute,
184 .arg_int = protocore_graph_ql_arg_int,
185 .arg_str = protocore_graph_ql_arg_str,
186 .arg_bool = protocore_graph_ql_arg_bool,
187};
188
189/**
190 * @brief The PROTOCORE_GRAPHQL_BORROW bytes this module's state lives in.
191 *
192 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
193 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
194 * walks, so the state lasts the life of the program.
195 *
196 * @return the span.
197 */
198uint8_t *protocore_graphql_span(void);
199
201
202#endif // PROTOCORE_ENABLE_GRAPHQL
203
204#endif // PROTOCORE_GRAPHQL_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97