1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
//! Compile-time-checked, typed SurrealQL for Rust.
//!
//! `surrealguard-rs` runs the [SurrealGuard](https://github.com/DrewRidley/surrealguard)
//! analyzer against your schema *during compilation*. A wrong table, unknown
//! field, bad function arity, or kind mismatch becomes a `cargo check` error,
//! the result type is generated from the inferred response, and the query's
//! parameters are type-checked against the kinds their uses imply — no build
//! script, no language server, no runtime schema fetch.
//!
//! ```rust,ignore
//! use surrealguard_rs::query;
//!
//! let adults = query!("SELECT name, age FROM user WHERE age >= $min", min = 18)
//! .fetch_all(&db)
//! .await?;
//!
//! for row in adults {
//! println!("{} is {}", row.name, row.age); // String, i64 — inferred
//! }
//! ```
//!
//! # The macros
//!
//! This crate re-exports the proc-macros from
//! [`surrealguard-macros`](https://crates.io/crates/surrealguard-macros) and
//! provides the runtime types their output refers to, so depending on
//! `surrealguard-rs` alone is enough.
//!
//! - [`query!`] checks a query **and** returns a typed [`Query<T>`](Query),
//! where `T` is the inferred result rendered as a *nameless* struct. You get
//! nested field access without ever writing a type — the same ergonomics as
//! sqlx's anonymous `query!` record.
//! - [`query_file!`] is [`query!`] with the SurrealQL read from a file at
//! compile time, resolved relative to the crate root (as `sqlx::query_file!`
//! does).
//! - [`surql!`] checks a query and expands to its validated text as a
//! `&'static str` — the lighter form when you only want validation.
//!
//! # Executing
//!
//! With the default `runtime` feature, a [`Query<T>`](Query) runs against the
//! official `surrealdb` SDK. The verbs mirror sqlx:
//!
//! | method | returns | available when |
//! |--------------------|------------------|------------------------------------|
//! | [`fetch`] | `T` | always — `T` is the analyzed shape |
//! | [`fetch_all`] | `Vec<T::Row>` | the query yields a row set |
//! | [`fetch_one`] | `T::Row` | the query yields a row set |
//! | [`fetch_optional`] | `Option<T::Row>` | the query yields a row set |
//! | [`execute`] | `()` | always |
//!
//! [`fetch`]: Query::fetch
//! [`fetch_all`]: Query::fetch_all
//! [`fetch_one`]: Query::fetch_one
//! [`fetch_optional`]: Query::fetch_optional
//! [`execute`]: Query::execute
//!
//! [`fetch`](Query::fetch) is the honest one: it hands back exactly the type
//! the analyzer inferred, whatever its shape. The row-set verbs are gated on
//! the [`Rows`] trait, which is implemented for the shapes a row set can take —
//! `Vec<R>` (a plain `SELECT`) and `Option<R>` (`SELECT … FROM ONLY …`). A
//! query that returns a scalar therefore *cannot* call `fetch_all`; that is a
//! compile error, not a runtime surprise:
//!
//! ```rust,ignore
//! query!("RETURN 1 + 1").fetch_all(&db).await?;
//! // error: this query does not return a set of rows
//! // note: use `.fetch(&db)` to get the value this query actually returns
//! ```
//!
//! # Parameters
//!
//! Parameters are supplied by name and checked against the kind the analyzer
//! inferred for each use site. There is no unchecked bind: the macro rejects a
//! missing parameter, an unknown one, and a value whose Rust type cannot become
//! the inferred kind.
//!
//! ```rust,ignore
//! query!("SELECT name FROM user WHERE age > $min", min = 18) // ok
//! query!("SELECT name FROM user WHERE age > $min", min = "18") // compile error
//! query!("SELECT name FROM user WHERE age > $min") // compile error: missing `min`
//! query!("SELECT name FROM user", limit = 10) // compile error: no `$limit`
//! ```
//!
//! # Multiple statements
//!
//! When more than one statement in a query responds, `T` is a tuple of the
//! responding statements' types, in source order. Non-responding statements
//! (a bare `LET`, a `DEFINE`) are skipped — they still occupy a slot in the
//! SDK's response, and this crate accounts for that so the tuple lines up with
//! what you wrote.
//!
//! ```rust,ignore
//! let (users, posts) = query!("SELECT name FROM user; SELECT title FROM post;")
//! .fetch(&db)
//! .await?;
//! ```
//!
//! Because a tuple is not a row set, `fetch_all` on a multi-statement query is
//! a compile error — you must use [`fetch`](Query::fetch) and destructure.
//!
//! # Schema awareness
//!
//! The macros resolve your schema at compile time from, in order:
//!
//! 1. the `SURREALGUARD_SCHEMA` environment variable — a `.surql` file or a
//! directory, relative to `CARGO_MANIFEST_DIR` unless absolute;
//! 2. otherwise a convention path under the crate root, tried in turn:
//! `schema/`, then `migrations/`, then `schema.surql`.
//!
//! A directory contributes every `.surql`/`.surrealql` file **sorted by name**,
//! so zero-padded migrations (`0001_*.surql`, `0002_*.surql`, …) apply in
//! order. Each schema file is tracked with `include_bytes!`, so editing it
//! forces a rebuild of the crate that calls the macro. With no schema
//! configured, queries are still checked for everything that does not depend on
//! one (syntax, function arity, operators, …).
//!
//! # `Kind` → Rust mapping
//!
//! Generated types implement [`surrealdb_types::SurrealValue`], which is what
//! the SDK's `take` actually requires — *not* `serde::Deserialize`. The mapping
//! is chosen so every generated type decodes the `Value` the server really
//! sends: the SDK performs no coercion whatsoever, so an approximate mapping is
//! a runtime failure rather than a lossy read.
//!
//! | SurrealQL kind | Rust type |
//! |---------------------|------------------------------------------|
//! | `bool` | `bool` |
//! | `int` | `i64` |
//! | `float` | `f64` |
//! | `decimal` | [`Decimal`](surrealdb_types::Decimal) |
//! | `number` | [`Number`](surrealdb_types::Number) |
//! | `string` | `String` |
//! | `datetime` | [`Datetime`](surrealdb_types::Datetime) |
//! | `duration` | [`Duration`](surrealdb_types::Duration) |
//! | `uuid` | [`Uuid`](surrealdb_types::Uuid) |
//! | `bytes` | [`Bytes`](surrealdb_types::Bytes) |
//! | `regex` | [`Regex`](surrealdb_types::Regex) |
//! | `record<t>` | [`RecordId`](surrealdb_types::RecordId) |
//! | `geometry` | [`Geometry`](surrealdb_types::Geometry) |
//! | `option<T>` | `Option<T>` |
//! | `array<T>` / `set` | `Vec<T>` |
//! | closed `object` | a nested, nameless struct |
//! | `any` / open object | [`Value`](surrealdb_types::Value) |
//!
//! # Feature flags
//!
//! - **`runtime`** (default) — pulls in the `surrealdb` SDK and enables the
//! `fetch*` / `execute` methods. Turn it off (`default-features = false`) to
//! get checking and typing with no SDK dependency; [`Query::decode`] still
//! decodes a response obtained some other way.
//!
//! The SDK is depended on with `default-features = false`, so it contributes no
//! engine or protocol of its own — the executing crate picks those through its
//! own `surrealdb` dependency, and Cargo unifies the two.
use fmt;
use PhantomData;
pub use surrealdb_types;
use ;
pub use ;
/// The result of running a compile-time-checked query.
pub type Result<T> = Result;
/// What went wrong running a [`Query`].
///
/// Unlike the SDK's error (and unlike `sqlx::Error`), this carries the text of
/// the query that produced it, so a failure names itself without the caller
/// having to correlate it back to a call site.
/// Boxed so that `Result<T, Error>` — which every method on [`Query`] returns —
/// stays pointer-sized. The SDK's own error is several words wide, and this
/// wraps it.
;
/// The specific failure behind an [`Error`].
/// A result shape that is a set of rows.
///
/// Implemented for the shapes a SurrealQL row set can take: `Vec<R>` for a
/// plain `SELECT`, and `Option<R>` for `SELECT … FROM ONLY …`. The row-set
/// verbs on [`Query`] are bounded by this, so calling [`Query::fetch_all`] on a
/// query that returns a scalar — or on a multi-statement query, whose result is
/// a tuple — is a compile error.
/// A compile-time-checked query paired with its inferred result type `T`.
///
/// [`query!`] expands to one of these, carrying the already-validated query
/// text, the parameters bound at the call site, and a `T` that is the inferred
/// response rendered as a nameless struct.
/// The row-set verbs.
///
/// `T: Rows` is a bound on each *method* rather than on the impl block on
/// purpose: an unsatisfied method bound is an E0277, which honours the
/// `#[diagnostic::on_unimplemented]` note on [`Rows`], whereas an unsatisfied
/// impl-block bound is an E0599 that reports only `i64: Rows` and no guidance.