Skip to main content

cratestack_core/
lib.rs

1//! `cratestack-core` — backend-agnostic primitives shared by every
2//! crate in the framework: schema IR, audit + envelope primitives,
3//! the `CoolError` / `CoolContext` / `Value` types, batch envelopes,
4//! RPC wire shapes, and field-level validators.
5//!
6//! The public surface is intentionally flat at the crate root: every
7//! type re-exports from a focused submodule below, so callers can
8//! keep writing `cratestack_core::CoolError` while the implementation
9//! lives in `cratestack_core::error`. New code can opt into the
10//! submodule paths directly.
11
12pub mod audit;
13pub mod batch;
14pub mod codec;
15pub mod context;
16pub mod envelope;
17pub mod error;
18pub mod events;
19pub mod find_many;
20pub mod idempotency_record;
21pub mod json;
22pub mod page;
23pub mod projection;
24pub mod route_naming;
25pub mod rpc;
26pub mod rust_keywords;
27pub mod schema;
28pub mod store;
29pub mod transport;
30pub mod validators;
31pub mod value;
32
33// -----------------------------------------------------------------------------
34// Decimal scalar
35//
36// Selected at compile time via mutually-exclusive Cargo features. Generated
37// code references `cratestack::Decimal` regardless of backend, so swapping
38// backends is a workspace-feature flip rather than a code change.
39//
40// The two backends are NOT drop-in equivalents at the trait level:
41// `rust_decimal::Decimal` is `Copy`; `bigdecimal::BigDecimal` is not (it
42// heap-allocates its digit buffer via `num-bigint`). Every call site across
43// the workspace that used to rely on an implicit `Decimal` copy was audited
44// and changed to an explicit `.clone()` as part of cratestack#495 — see
45// `cratestack-sqlx/src/query/support/values.rs`'s `push_bind_value` for the
46// one spot that actually needed it. Both backends do implement `Clone`,
47// `Debug`, `Display`, `FromStr`, `PartialEq`, `PartialOrd`, `Ord`, `Eq`,
48// `Hash`, and `Default`, so no other trait bound in the workspace needed to
49// change.
50// -----------------------------------------------------------------------------
51
52#[cfg(not(any(feature = "decimal-rust-decimal", feature = "decimal-bigdecimal")))]
53compile_error!(
54    "cratestack: enable exactly one decimal backend feature — `decimal-rust-decimal` or `decimal-bigdecimal`"
55);
56
57#[cfg(all(feature = "decimal-rust-decimal", feature = "decimal-bigdecimal"))]
58compile_error!(
59    "cratestack: `decimal-rust-decimal` and `decimal-bigdecimal` are mutually exclusive — enable exactly one"
60);
61
62#[cfg(all(feature = "decimal-rust-decimal", not(feature = "decimal-bigdecimal")))]
63pub type Decimal = rust_decimal::Decimal;
64
65#[cfg(all(feature = "decimal-bigdecimal", not(feature = "decimal-rust-decimal")))]
66pub type Decimal = bigdecimal::BigDecimal;
67
68/// Body bytes carried through the transport layer.
69pub type CoolBody = bytes::Bytes;
70
71// Backwards-compatible re-exports so external crates keep using
72// `cratestack_core::Type` rather than `cratestack_core::module::Type`.
73
74pub use audit::{
75    AuditActor, AuditEvent, AuditOperation, AuditSink, MulticastAuditSink, NoopAuditSink,
76    TransactionIsolation,
77};
78pub use batch::{
79    BATCH_MAX_ITEMS, BatchItemError, BatchItemResult, BatchItemStatus, BatchRequest, BatchResponse,
80    BatchSummary, find_duplicate_position,
81};
82pub use codec::{CoolCodec, CoolEnvelope, NoEnvelope};
83pub use context::{
84    AuthProvider, CoolAuthIdentity, CoolContext, PrincipalContext, PrincipalFacet, RequestContext,
85    SystemContext,
86};
87pub use envelope::{
88    HmacEnvelope, InMemoryNonceStore, KeyProvider, NonceStore, SealedEnvelope, StaticKeyProvider,
89};
90pub use error::{CoolError, CoolErrorResponse, DbErrorInfo, parse_cuid};
91pub use events::{
92    CoolEventBus, CoolEventEnvelope, CoolEventFuture, ModelEvent, ModelEventKind,
93    SubscriptionGuard, SubscriptionHandle, event_topic, parse_emit_attribute,
94};
95pub use find_many::FieldFilterInput;
96pub use idempotency_record::{IdempotencyRecord, ReservationOutcome};
97pub use json::Json;
98pub use page::{MAX_LIST_LIMIT, Page, PageInfo, PageInput};
99pub use projection::ProjectionDecoder;
100pub use schema::{
101    Attribute, AuthBlock, ConfigBlock, ConfigEntry, Datasource, EnumDecl, EnumVariant,
102    ExtensionKind, Field, MixinDecl, Model, OwnedSchemaSummary, ParsedIndexAttribute, Procedure,
103    ProcedureArg, ProcedureKind, Schema, SchemaSummary, SelectionQuery, SourceSpan, TransportStyle,
104    TypeArity, TypeDecl, TypeRef, View, ViewSource, parse_composite_id_attribute,
105    parse_composite_unique_attribute, parse_index_attribute,
106};
107pub use store::{
108    ClientStateStore, IdempotencyStore, InMemoryStateStore, JsonFileStateStore,
109    PersistedClientState, RateLimitConfig, RateLimitDecision, RateLimitStore, RequestJournalEntry,
110};
111pub use transport::{
112    OpDescriptor, OpKind, RouteTransportCapabilities, RouteTransportDescriptor,
113    canonical_request_string,
114};
115pub use validators::{
116    validate_email, validate_iso4217, validate_length, validate_range_decimal, validate_range_i64,
117    validate_uri,
118};
119pub use value::Value;
120
121// These tests intentionally reference only `Decimal` (the alias), never a
122// backend-specific type, so the exact same suite runs unmodified under
123// either `cargo test -p cratestack-core --features decimal-rust-decimal`
124// (the default) or `--no-default-features --features decimal-bigdecimal` —
125// see `.ci/feature-matrix.sh` for both invocations.
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn decimal_backend_is_available() {
132        // Verify that whichever backend is active compiles and the Decimal
133        // type works.
134        let d = Decimal::from(42);
135        assert_eq!(d.to_string(), "42");
136    }
137
138    #[test]
139    fn decimal_type_arithmetic() {
140        // Verify basic decimal operations work
141        let d1 = Decimal::from(10);
142        let d2 = Decimal::from(20);
143        // Just verify the types compile and basic operations work
144        let _ = d1 + d2;
145    }
146}