Skip to main content

cratestack/
lib.rs

1//! CrateStack server facade — Postgres (sqlx) + Axum.
2//!
3//! This crate is the server-side slice of the framework. It re-exports the
4//! shared schema / parser / policy / SQL surface plus the sqlx (Postgres)
5//! runtime, Axum HTTP bindings, and the generated Rust client runtime.
6//!
7//! It deliberately does **not** depend on `cratestack-rusqlite`. That keeps
8//! `libsqlite3-sys` out of the dep graph, so consumers can use the official
9//! `sqlx` umbrella crate (which optionally declares `sqlx-sqlite` and trips
10//! Cargo's `links = "sqlite3"` collision rule) without needing a local
11//! `sqlx-shim` workaround.
12//!
13//! For embedded / mobile / wasm targets, depend on `cratestack-sqlite`
14//! instead. The two crates are strictly disjoint by design.
15//!
16//! Schema macros emit `::cratestack::*` paths, so consumers rename this
17//! crate via Cargo's `package =` field:
18//!
19//! ```toml
20//! [dependencies]
21//! cratestack = { package = "cratestack-pg", version = "0.4" }
22//! ```
23//!
24//! `sqlx`/`cratestack-sqlx` sit behind the default-on `postgres` Cargo
25//! feature (cratestack#329). A `db = None`-only consumer
26//! (`include_server_schema!(schema, db = None)`, cratestack#328) can drop
27//! `sqlx` from its dependency graph entirely:
28//!
29//! ```toml
30//! [dependencies]
31//! cratestack = { package = "cratestack-pg", version = "0.4", default-features = false }
32//! ```
33//!
34//! See `docs/design/no-database-mode.md` for when `db = None` applies and
35//! what it gives up.
36
37// Both `cratestack_core` and `cratestack_axum` expose `codec` and
38// `transport` modules, and the facade re-exports both crates with a glob.
39// The overlap is intentional — consumers reach those via the originating
40// crate's path, not the facade root — so silence the ambiguity warning
41// rather than dropping either glob.
42#![allow(ambiguous_glob_reexports)]
43
44// Re-exported so the axum dispatch tokens `cratestack-macros` generates
45// for `@stream` procedures (`crate::axum::procedure::invoke_call`) can
46// reference `::cratestack::async_stream::stream!` without every
47// consumer adding `async-stream` to their own `Cargo.toml`. Needed
48// because the `ProcedureRegistry` trait method's `db`/`ctx` parameters
49// are borrowed (`&Cratestack`/`&CratestackContext`), and — per return-position
50// `impl Trait` in traits' default lifetime-capture rules — the returned
51// `Stream` is only valid as long as those borrows are; wrapping the
52// call in a self-contained `async_stream::stream!` generator that owns
53// `db`/`ctx`/`registry`/`args` internally is what lets the resulting
54// `Stream` outlive the dispatch function's own stack frame (needed
55// since it travels all the way into the HTTP response body). Mirrors
56// how `futures` is re-exported below for the same
57// "codegen references a fixed path, consumers shouldn't have to
58// duplicate the dependency" reason.
59pub use async_stream;
60pub use chrono;
61pub use cratestack_client_rust as client_rust;
62pub use cratestack_core::*;
63// Re-exported (renamed from the `futures-util` crate, which is what
64// actually implements it) so `@stream` procedures' generated
65// `ProcedureRegistry` trait method — `impl ::cratestack::futures::Stream<
66// Item = Result<T, CratestackError>> + Send` (see
67// `cratestack-macros/src/procedure.rs`) — has somewhere to point without
68// every consumer adding its own `futures`/`futures-core`/`futures-util`
69// dependency. Mirrors how `chrono`/`uuid` are re-exported above for the
70// same "codegen references a fixed path, consumers shouldn't have to
71// duplicate the dependency" reason.
72pub use cratestack_macros::{
73    include_client_schema, include_embedded_schema, include_server_schema,
74};
75pub use cratestack_parser::{SchemaError, parse_schema, parse_schema_file, parse_schema_named};
76pub use cratestack_policy::{
77    PolicyExpr, PolicyLiteral, ProcedureArgs, ProcedurePolicy, ProcedurePolicyExpr,
78    ProcedurePolicyLiteral, ProcedurePredicate, ReadPolicy, ReadPredicate, RelationQuantifier,
79    authorize_procedure, authorize_query,
80};
81pub use futures_util as futures;
82
83// SQL primitives shared by every backend — re-exported directly from
84// `cratestack-sql` so consumers don't transit through `cratestack-sqlx`.
85pub use cratestack_sql::{
86    CoalesceExpr, CoalesceFilter, ConflictTarget, CreateDefault, CreateDefaultType,
87    CreateModelInput, FieldRef, Filter, FilterExpr, FilterOp, IntoColumnName, IntoSqlValue,
88    JsonFilter, JsonTextPath, ModelColumn, ModelDescriptor, ModelPrimaryKey, NullOrder,
89    OrderCatalog, OrderClause, OrderRelationEdge, OrderTarget, Orderable, Projection, ReadSource,
90    RelationFilter, RelationHop, RelationInclude, ResolvedOrderTarget, SortDirection,
91    SqlColumnValue, SqlValue, Unorderable, UpdateModelInput, UpsertModelInput, VectorDistanceExpr,
92    VectorDistanceFilter, VectorMetric, ViewDescriptor, WriteSource, coalesce, is_orderable,
93    order_value_sql, resolve_order_target, wrap_filter,
94};
95/// PostGIS query surface (cratestack#842) — gated in
96/// `cratestack-sql` and forwarded through this crate's own
97/// `postgis` feature.
98#[cfg(feature = "postgis")]
99pub use cratestack_sql::{SpatialDistanceExpr, SpatialFilter, SpatialPoint, point};
100
101pub use regex;
102pub use serde;
103pub use serde_json;
104pub use tracing;
105pub use uuid;
106
107// `Json<T>` resolves to `cratestack_sqlx::Json<T>` on the server so
108// `sqlx::FromRow` decodes Postgres `jsonb` columns into it directly, using
109// the plain/untagged codec (cratestack#162) rather than going through
110// `serde_json` generically. (For `T = Value`, `Value`'s own hand-written
111// `Serialize`/`Deserialize` is untagged too, since cratestack#506 — the
112// two codecs agree on shape, they're just independently maintained.) This is
113// only possible with the `postgres` feature enabled (cratestack#329): models
114// (the only place a "Json" column is decoded from a row) can never exist
115// under `db = None` (cratestack#327's guard), so a `postgres`-disabled build
116// falls back to `cratestack-core`'s own backend-agnostic `Json<T>` newtype,
117// which is all a `db = None` schema's procedure args/returns ever need —
118// they only flow through serde (JSON/CBOR codecs), never `sqlx::FromRow`.
119#[cfg(not(feature = "postgres"))]
120pub use cratestack_core::json::Json;
121#[cfg(feature = "postgres")]
122pub use cratestack_sqlx::Json;
123
124// `Vector(n)` model fields decode/encode through `pgvector::Vector` at
125// the sqlx boundary (see `cratestack-macros`' generated `FromRow` impl
126// and `SqlValue::Vector` bind path) — re-exported so macro-emitted
127// `::cratestack::pgvector::Vector` paths resolve. Requires this
128// facade's own `pgvector` feature, which forwards to both
129// `cratestack-macros/pgvector` (the compile-time declaration gate)
130// and `cratestack-sqlx/pgvector` (the real column codec) in lockstep.
131#[cfg(feature = "pgvector")]
132pub use cratestack_sqlx::pgvector;
133
134// `Geography`/`Geometry` model fields decode through
135// `cratestack_sqlx::Ewkb` at the sqlx row boundary, so the generated
136// `::cratestack::Ewkb` path has to resolve here (cratestack#842).
137// Requires this facade's own `postgis` feature, which forwards to both
138// `cratestack-macros/postgis` (the compile-time declaration gate) and
139// `cratestack-sqlx/postgis` (the column codec) in lockstep.
140#[cfg(feature = "postgis")]
141pub use cratestack_sqlx::Ewkb;
142
143// -----------------------------------------------------------------------------
144// Server surface — axum, audit/idempotency/migrations/isolation.
145// -----------------------------------------------------------------------------
146
147pub use cratestack_axum::axum;
148pub use cratestack_axum::*;
149
150// Disambiguate the `rpc` module path. Both `cratestack_core` (wire shapes)
151// and `cratestack_axum` (binding helpers) expose an `rpc` module, so the
152// two `pub use ..::*` globs collide on the name and `::cratestack::rpc::*`
153// resolves non-deterministically. Macro-emitted code in `transport rpc`
154// schemas references symbols like `encode_rpc_error`,
155// `convert_handler_error_response`, `response_to_frame`, and
156// `RPC_BINDING_CAPABILITIES` — all of which live in `cratestack-axum::rpc`.
157// An explicit `pub use` re-export takes precedence over the globs, pinning
158// `::cratestack::rpc` to the axum module (which itself re-exports the wire
159// types from `cratestack-core::rpc`).
160pub use cratestack_axum::rpc;
161
162// Everything below is sqlx (Postgres)-backed and only compiled in when the
163// default-on `postgres` feature is enabled (cratestack#329). A `db = None`
164// -only consumer builds with `default-features = false` (or explicitly
165// disables `postgres`) to drop `sqlx`/`cratestack-sqlx` from its dependency
166// graph entirely — nothing generated under `db = None` ever references these
167// symbols, since models (the only consumers of them) can never exist in a
168// `datasource { provider = "none" }` schema.
169#[cfg(feature = "postgres")]
170pub use cratestack_sqlx::AUDIT_TABLE_DDL;
171#[cfg(feature = "postgres")]
172pub use cratestack_sqlx::sqlx;
173#[cfg(feature = "postgres")]
174pub use cratestack_sqlx::{
175    Aggregate, AggregateColumn, AggregateCount, CreateRecord, DeleteMany, DeleteRecord, FindMany,
176    FindManyWith, FindUnique, FromPartialPgRow, ModelDelegate, ProjectedFindMany,
177    ProjectedFindUnique, RunInTxOutcome, ScopedAggregate, ScopedAggregateColumn,
178    ScopedAggregateCount, ScopedCreateRecord, ScopedDeleteMany, ScopedDeleteRecord, ScopedFindMany,
179    ScopedFindManyWith, ScopedFindUnique, ScopedModelDelegate, ScopedProjectedFindMany,
180    ScopedProjectedFindUnique, ScopedUpdateMany, ScopedUpdateManySet, ScopedUpdateRecord,
181    ScopedUpdateRecordSet, ScopedUpsertRecord, ScopedUpsertRecordDoNothing, SqlxIdempotencyStore,
182    UpdateMany, UpdateManySet, UpdateRecord, UpdateRecordSet, UpsertOutcome, UpsertRecord,
183    UpsertRecordDoNothing, ViewDelegate, ViewDelegateNoUnique, create_record_with_executor,
184    update_record_with_executor,
185};
186#[cfg(feature = "postgres")]
187pub use cratestack_sqlx::{
188    MIGRATIONS_TABLE_DDL, Migration, MigrationState, MigrationStatus, apply_pending,
189    ensure_migrations_table, status,
190};
191#[cfg(feature = "postgres")]
192pub use cratestack_sqlx::{
193    Tx, cratestack_error_from_sqlx, run_in_isolated_tx, run_in_isolated_tx_with_retries,
194};
195
196/// Crypto provider selection for FIPS-validated deployments.
197///
198/// **`crypto-aws-lc-rs` is not implemented yet.** Enabling it is a hard
199/// `compile_error!`, not a working FIPS mode — this used to return `Ok(())`
200/// without installing any provider, which is a false assurance in a
201/// compliance-facing API: a service that called this and checked for `Ok`
202/// got an affirmative return while still running on the non-FIPS `ring`
203/// backend. See <https://github.com/cratestack/cratestack/issues/334>.
204///
205/// Making this real requires the TLS backend becoming a genuine choice
206/// across `cratestack-sqlx` and `cratestack-client-rust` (both currently
207/// hard-select `ring`), not just adding `aws-lc-rs` as a dependency here —
208/// Cargo features are additive, so enabling `crypto-aws-lc-rs` today would
209/// only add a second provider alongside `ring`, not replace it. Until that
210/// backend-selection work lands, this function fails to compile under the
211/// feature rather than silently lying about what it installed.
212pub fn install_fips_crypto_provider() -> Result<(), cratestack_core::CratestackError> {
213    #[cfg(feature = "crypto-aws-lc-rs")]
214    {
215        compile_error!(
216            "cratestack-pg's `crypto-aws-lc-rs` feature does not install a FIPS-validated \
217             crypto provider yet — see install_fips_crypto_provider's doc comment and \
218             https://github.com/cratestack/cratestack/issues/334. Do not enable this feature."
219        )
220    }
221    #[cfg(not(feature = "crypto-aws-lc-rs"))]
222    {
223        Err(cratestack_core::CratestackError::Internal(
224            "cratestack was not compiled with `crypto-aws-lc-rs` feature; \
225             FIPS-validated crypto provider is unavailable"
226                .to_owned(),
227        ))
228    }
229}
230
231#[doc(hidden)]
232pub mod __private {
233    #[cfg(feature = "postgres")]
234    pub use cratestack_sqlx::SqlxRuntime;
235    // Not part of the public API surface — the generated
236    // `Cratestack::dispatch_audit_sink` (cratestack#534) is the
237    // consumer-facing wrapper around this; see its doc comment.
238    #[cfg(feature = "postgres")]
239    pub use cratestack_sqlx::dispatch_audit_sink;
240
241    /// Re-exports for the macro-emitted RPC dispatcher. Not part of the
242    /// public API surface — schema authors should never reference these
243    /// directly. Public helpers live at `cratestack::rpc::*`.
244    pub use cratestack_axum::rpc::{decode_rpc_body, encode_rpc_value, response_to_frame};
245
246    /// `@@subscribe` SSE dispatch (`GET /rpc/subscribe/{op_id}`, design
247    /// doc §3.4a, cratestack#390): the bounded-channel bridge from a
248    /// `CratestackEventBus` push callback to a `Stream`, and the encoder that
249    /// turns that `Stream` into a `text/event-stream` response. Not
250    /// part of the public API surface for the same reason as the rest
251    /// of this module.
252    pub use cratestack_axum::rpc::{
253        encode_model_event_sse_response, guarded_receiver_stream, subscription_channel,
254        validate_subscribe_accept_header,
255    };
256}