Skip to main content

Crate surrealguard_rs

Crate surrealguard_rs 

Source
Expand description

Compile-time-checked, typed SurrealQL for Rust.

surrealguard-rs runs the 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.

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 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>, 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> runs against the official surrealdb SDK. The verbs mirror sqlx:

methodreturnsavailable when
fetchTalways — T is the analyzed shape
fetch_allVec<T::Row>the query yields a row set
fetch_oneT::Rowthe query yields a row set
fetch_optionalOption<T::Row>the query yields a row set
execute()always

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:

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.

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.

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 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 kindRust type
boolbool
inti64
floatf64
decimalDecimal
numberNumber
stringString
datetimeDatetime
durationDuration
uuidUuid
bytesBytes
regexRegex
record<t>RecordId
geometryGeometry
option<T>Option<T>
array<T> / setVec<T>
closed objecta nested, nameless struct
any / open objectValue

§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.

Re-exports§

pub use surrealdb_types;

Macros§

query
Checks a SurrealQL string literal at compile time and expands to a surrealguard_rs::Query<T> whose T is the inferred, nameless result type.
query_file
Like [query], but reads the SurrealQL from a file at compile time.
surql
Checks a SurrealQL string literal at compile time; expands to its text.

Structs§

Error
What went wrong running a Query.
Query
A compile-time-checked query paired with its inferred result type T.

Enums§

ErrorKind
The specific failure behind an Error.

Traits§

Rows
A result shape that is a set of rows.

Type Aliases§

Result
The result of running a compile-time-checked query.