Skip to main content

shaperail_codegen/diagnostics/
registry.rs

1/// Severity level for a diagnostic entry.
2#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
3#[serde(rename_all = "lowercase")]
4pub enum Severity {
5    Error,
6    Warning,
7    Info,
8}
9
10/// A static registry entry describing a single SR* diagnostic code.
11#[derive(Debug, Clone, Copy, serde::Serialize)]
12pub struct RegistryEntry {
13    /// Stable error code, e.g. "SR001".
14    pub code: &'static str,
15    /// One-line human-readable summary of what this code means.
16    pub summary: &'static str,
17    /// Severity level of this diagnostic.
18    pub severity: Severity,
19}
20
21/// The canonical table of all SR* diagnostic codes emitted by `shaperail-codegen`
22/// and `shaperail-cli`.
23///
24/// SR000 (YAML parse error) is emitted by `shaperail-cli` check.rs and is listed here
25/// so that `Diagnostic::error("SR000", ...)` does not trigger the debug-assert for
26/// unregistered codes. SR100 (legacy INT/BIGINT drift warning) is also emitted by
27/// `shaperail-cli` and is NOT listed here — it is constructed directly as a JSON value,
28/// not via `Diagnostic::error`.
29///
30/// Keep this table sorted by code. Codes are also clustered by SR-decade
31/// (SR0x = schema/field, SR2x = endpoint/inputs, SR3x = filters/cache, SR4x = controllers,
32/// SR5x = upload, SR6x = relations, SR7x = indexes/auth/tenant/soft-delete/events/jobs,
33/// SR8x = misc); preserve clustering when adding new codes.
34///
35/// When adding a new emission site in `inner.rs`, add a corresponding entry here in the
36/// same commit (enforced by the `diagnostic_registry` integration test).
37pub const REGISTRY: &[RegistryEntry] = &[
38    RegistryEntry {
39        code: "SR000",
40        summary: "YAML parse error",
41        severity: Severity::Error,
42    },
43    RegistryEntry {
44        code: "SR001",
45        summary: "resource name must not be empty",
46        severity: Severity::Error,
47    },
48    RegistryEntry {
49        code: "SR002",
50        summary: "version must be >= 1",
51        severity: Severity::Error,
52    },
53    RegistryEntry {
54        code: "SR003",
55        summary: "schema is empty — must have at least one field",
56        severity: Severity::Error,
57    },
58    RegistryEntry {
59        code: "SR004",
60        summary: "schema has no primary key field",
61        severity: Severity::Error,
62    },
63    RegistryEntry {
64        code: "SR005",
65        summary: "schema has more than one primary key field",
66        severity: Severity::Error,
67    },
68    RegistryEntry {
69        code: "SR010",
70        summary: "field is type enum but declares no values",
71        severity: Severity::Error,
72    },
73    RegistryEntry {
74        code: "SR011",
75        summary: "non-enum field declares values list",
76        severity: Severity::Error,
77    },
78    RegistryEntry {
79        code: "SR012",
80        summary: "ref on non-uuid field",
81        severity: Severity::Error,
82    },
83    RegistryEntry {
84        code: "SR013",
85        summary: "ref value missing dot notation (expected resource.field)",
86        severity: Severity::Error,
87    },
88    RegistryEntry {
89        code: "SR014",
90        summary: "array field has no items type declared",
91        severity: Severity::Error,
92    },
93    RegistryEntry {
94        code: "SR015",
95        summary: "format attribute used on non-string field",
96        severity: Severity::Error,
97    },
98    RegistryEntry {
99        code: "SR016",
100        summary: "primary key field is neither generated nor required",
101        severity: Severity::Error,
102    },
103    RegistryEntry {
104        code: "SR020",
105        summary: "tenant_key references a field that is not type uuid",
106        severity: Severity::Error,
107    },
108    RegistryEntry {
109        code: "SR021",
110        summary: "tenant_key references a field not found in schema",
111        severity: Severity::Error,
112    },
113    RegistryEntry {
114        code: "SR030",
115        summary: "controller.before has an empty hook name",
116        severity: Severity::Error,
117    },
118    RegistryEntry {
119        code: "SR031",
120        summary: "controller.after has an empty hook name",
121        severity: Severity::Error,
122    },
123    RegistryEntry {
124        code: "SR032",
125        summary: "events list contains an empty event name",
126        severity: Severity::Error,
127    },
128    RegistryEntry {
129        code: "SR033",
130        summary: "jobs list contains an empty job name",
131        severity: Severity::Error,
132    },
133    RegistryEntry {
134        code: "SR035",
135        summary: "controller hook uses 'wasm:' prefix but provides no path",
136        severity: Severity::Error,
137    },
138    RegistryEntry {
139        code: "SR036",
140        summary: "controller hook WASM path does not end with '.wasm'",
141        severity: Severity::Error,
142    },
143    RegistryEntry {
144        code: "SR040",
145        summary: "endpoint input/filter/search/sort references a field not in schema",
146        severity: Severity::Error,
147    },
148    RegistryEntry {
149        code: "SR041",
150        summary: "soft_delete declared but schema has no deleted_at field",
151        severity: Severity::Error,
152    },
153    RegistryEntry {
154        code: "SR050",
155        summary: "upload declared on an endpoint whose method is not POST, PATCH, or PUT",
156        severity: Severity::Error,
157    },
158    RegistryEntry {
159        code: "SR051",
160        summary: "upload field exists in schema but is not type file",
161        severity: Severity::Error,
162    },
163    RegistryEntry {
164        code: "SR052",
165        summary: "upload field not found in schema",
166        severity: Severity::Error,
167    },
168    RegistryEntry {
169        code: "SR053",
170        summary: "upload storage backend is not one of: local, s3, gcs, azure",
171        severity: Severity::Error,
172    },
173    RegistryEntry {
174        code: "SR054",
175        summary: "upload field is not listed in the endpoint input array",
176        severity: Severity::Error,
177    },
178    RegistryEntry {
179        code: "SR060",
180        summary: "belongs_to relation is missing required key field",
181        severity: Severity::Error,
182    },
183    RegistryEntry {
184        code: "SR061",
185        summary: "has_many or has_one relation is missing required foreign_key field",
186        severity: Severity::Error,
187    },
188    RegistryEntry {
189        code: "SR062",
190        summary: "relation key field not found in schema",
191        severity: Severity::Error,
192    },
193    RegistryEntry {
194        code: "SR063",
195        summary: "controller before/after list is empty",
196        severity: Severity::Error,
197    },
198    RegistryEntry {
199        code: "SR070",
200        summary: "index definition has no fields listed",
201        severity: Severity::Error,
202    },
203    RegistryEntry {
204        code: "SR071",
205        summary: "index references a field not found in schema",
206        severity: Severity::Error,
207    },
208    RegistryEntry {
209        code: "SR072",
210        summary: "index order must be 'asc' or 'desc'",
211        severity: Severity::Error,
212    },
213    RegistryEntry {
214        code: "SR073",
215        summary: "subscriber entry has an empty event pattern",
216        severity: Severity::Error,
217    },
218    RegistryEntry {
219        code: "SR074",
220        summary: "subscriber entry has an empty handler name",
221        severity: Severity::Error,
222    },
223    RegistryEntry {
224        code: "SR075",
225        summary: "non-convention endpoint has no handler declared",
226        severity: Severity::Error,
227    },
228    RegistryEntry {
229        code: "SR076",
230        summary: "array field has nested array items (not supported)",
231        severity: Severity::Error,
232    },
233    RegistryEntry {
234        code: "SR077",
235        summary: "array items type is enum but declares no values",
236        severity: Severity::Error,
237    },
238    RegistryEntry {
239        code: "SR078",
240        summary: "array items.format is only valid when items.type is string",
241        severity: Severity::Error,
242    },
243    RegistryEntry {
244        code: "SR079",
245        summary: "array items.ref requires items.type to be uuid",
246        severity: Severity::Error,
247    },
248    RegistryEntry {
249        code: "SR080",
250        summary: "array items.ref must use 'resource.field' dot notation",
251        severity: Severity::Error,
252    },
253];
254
255/// Look up a registry entry by its SR code.
256///
257/// Returns `None` if the code is not registered.
258pub fn lookup(code: &str) -> Option<&'static RegistryEntry> {
259    REGISTRY.iter().find(|e| e.code == code)
260}