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
//! Decimal scalar.
//!
//! Selected at compile time via mutually-exclusive Cargo features. Generated
//! code references `cratestack::Decimal` regardless of backend, so swapping
//! backends is a workspace-feature flip rather than a code change.
//!
//! The two backends are NOT drop-in equivalents at the trait level:
//! `rust_decimal::Decimal` is `Copy`; `bigdecimal::BigDecimal` is not (it
//! heap-allocates its digit buffer via `num-bigint`). Every call site across
//! the workspace that used to rely on an implicit `Decimal` copy was audited
//! and changed to an explicit `.clone()` as part of cratestack#495 — see
//! `cratestack-sqlx/src/query/support/values.rs`'s `push_bind_value` for the
//! one spot that actually needed it. Both backends do implement `Clone`,
//! `Debug`, `Display`, `FromStr`, `PartialEq`, `PartialOrd`, `Ord`, `Eq`,
//! `Hash`, and `Default`, so no other trait bound in the workspace needed to
//! change.
//!
//! # This mutual exclusivity is a graph-wide invariant, not a per-crate one (cratestack#505)
//!
//! `decimal-rust-decimal` and `decimal-bigdecimal` are mutually exclusive —
//! only ONE `Decimal` type alias can exist, so both selected simultaneously
//! is a hard `compile_error!` below, not a warning. Because Cargo features
//! are additive and unify globally across a dependency graph, this
//! exclusivity is not just a per-crate concern: it is possible for two
//! *independent* dependents, each individually well-formed and each
//! deliberately choosing a different backend, to force this error into a
//! combined build that neither one alone controls or can fix. There is
//! currently no way to avoid this other than the whole graph standardizing on
//! one backend feature; see cratestack#505 for the discussion of making the
//! backends genuinely additive instead (an unresolved, larger design change,
//! deliberately out of scope here — this crate only documents the invariant
//! and softens the *other* half of the same bug report, below).
//!
//! # Selecting NEITHER feature is not an error (cratestack#505)
//!
//! Older versions of this crate also hard-errored when NEITHER decimal
//! feature was selected, which bit a consumer that legitimately used
//! `default-features = false` to narrow its dependency graph and never
//! touched `Decimal` at all — that consumer was nonetheless forced to name a
//! decimal backend it never used, and the break was invisible in a
//! `cargo check --workspace` run (feature unification from other workspace
//! members hid it) until someone built that member alone.
//!
//! Because Cargo's feature system has no "else" — an optional dependency can
//! only be *activated* by a feature that names it, never *auto-activated*
//! by the absence of some other feature — `Decimal` cannot be made to
//! silently resolve to `rust_decimal::Decimal` in this situation without
//! either (a) making `rust_decimal` a mandatory, always-resolved dependency
//! of this crate (which would put `rust_decimal` back in the dependency tree
//! of every `decimal-bigdecimal` consumer too, defeating the whole point of
//! `decimal-bigdecimal` — verified empirically against `cratestack-pg`'s
//! `cargo tree`, cratestack#495's own acceptance bar), or (b) reverting the
//! `default-features = false` convention every internal dependency edge onto
//! this crate uses (which reopens the *original* default-leak bug,
//! cratestack#421). Neither is acceptable, so instead: the `Decimal` type
//! alias, and every unconditional use of it inside this crate (see
//! `validators::validate_range_decimal`), simply does not exist when neither
//! feature is selected — the crate compiles cleanly with a reduced surface
//! instead of hard-erroring. A consumer that never references `Decimal`
//! (directly, or via a schema with no `Decimal`-typed fields) now builds
//! successfully; a consumer that *does* try to use it without picking a
//! backend gets a plain "cannot find type `Decimal`" from rustc instead of
//! this crate's old, clearer `compile_error!` — a minor diagnostic
//! regression accepted in exchange for not hard-failing every backend-
//! agnostic consumer.
compile_error!;
pub type Decimal = Decimal;
pub type Decimal = BigDecimal;
// These tests intentionally reference only `Decimal` (the alias), never a
// backend-specific type, so the exact same suite runs unmodified under
// either `cargo test -p cratestack-core --features decimal-rust-decimal`
// (the default) or `--no-default-features --features decimal-bigdecimal` —
// see `.ci/feature-matrix.sh` for both invocations. `Decimal` doesn't exist
// at all when neither feature is selected (see the module doc above), so
// this module is gated the same way `Decimal` itself is.
// cratestack#505 regression coverage: `cargo test -p cratestack-core
// --no-default-features` (neither decimal backend selected) used to hard-
// fail with the "enable exactly one decimal backend" `compile_error!`
// before this feature even reached test collection. This module only
// exists to prove that configuration now builds and runs at all — the
// counterpart to `.ci/feature-matrix.sh`'s "[1/6]" step, which asserts the
// same thing from outside the crate via `cargo check`/`cargo test`. It
// cannot reference `Decimal` (nothing else in this crate can either, in
// this configuration — see the module doc above), so it is gated the
// opposite way `mod tests` above is: only when NEITHER decimal feature is
// active.