8000 minigraf/src/error.rs at v3 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
3049 lines (3002 loc) · 84.3 KB

error.rs

File metadata and controls

3049 lines (3002 loc) · 84.3 KB
//! Structured runtime error codes surfaced through `MinigrafError`.
//!
//! See `docs/superpowers/specs/2026-08-25-structured-error-codes-design.md`
//! and `docs/ERROR_REFERENCE.md` for the design and the full code registry.
use std::fmt;
/// Coarse-grained error category — the type library consumers match on.
///
/// `#[non_exhaustive]`: a future category can be added without that being a
/// breaking change. Downstream `match` expressions must include a wildcard
/// `_` arm. Has no effect within this crate.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorCategory {
/// Datalog/EDN syntax errors (`PRS-` codes).
Parser,
/// Query planning/execution errors (`QRY-` codes).
Query,
/// On-disk storage/file-format errors (`STG-` codes).
Storage,
/// Write-ahead log errors (`WAL-` codes).
Wal,
/// Public `Minigraf` API misuse or internal sequencing errors (`API-` codes).
Api,
/// Unclassified internal invariant violations (`INT-` codes).
Internal,
}
/// Crate-internal handle for one documented `docs/ERROR_REFERENCE.md` code.
///
/// Never exposed to library consumers — only the `&'static str` code string
/// it resolves to via [`REGISTRY`] is public (through [`MinigrafError::code`]).
/// Kept internal so this enum is free to be reorganized without breaking any
/// downstream `match`.
///
/// Deliberately does NOT derive `Debug`: this enum's variant count tracks
/// every documented error code (186, as of #277's final API+INT category PR
/// — 130 originally documented `PRS`/`QRY`/`STG`/`WAL`/`API` codes plus 55
/// `INT-0xx` codes assigned to the crate's remaining previously-uncoded
/// call sites, plus the original `INT-000` catch-all), and a derived
/// `Debug` impl generates a per-variant string-literal match arm that's
/// pure binary-size cost with no runtime use anywhere in this crate
/// (nothing formats an `ErrorCode` with `{:?}` — [`CodedError`]'s own
/// hand-written `Debug` below only needs the already-formatted `message`
/// and the code string, not this enum). Keeps the project's binary
/// size goal (see PHILOSOPHY.md) affordable as the registry grows.
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum ErrorCode {
Prs001,
Prs002,
Prs003,
Prs004,
Prs005,
Prs006,
Prs007,
Prs008,
Prs009,
Prs010,
Prs011,
Prs012,
Prs013,
Prs014,
Prs015,
Prs016,
Prs017,
Prs018,
Prs019,
Prs020,
Prs021,
Prs022,
Prs023,
Prs024,
Prs025,
Prs026,
Prs027,
Prs028,
Prs029,
// PRS-030 is deprecated since v3.0.0: lag/lead windows are supported.
// Kept so the code is never reused.
#[allow(dead_code)]
Prs030,
Prs031,
Prs032,
Prs033,
Prs034,
Prs035,
Prs036,
Prs037,
Prs038,
Prs039,
Prs040,
Prs041,
Prs042,
Prs043,
Prs044,
Prs045,
Prs046,
Prs047,
Prs048,
Prs049,
Prs050,
Prs051,
Prs052,
Prs053,
Prs054,
Prs055,
Prs056,
Prs057,
Prs058,
Prs059,
Prs060,
Prs061,
Prs062,
Prs063,
Prs064,
Prs065,
Prs066,
Prs067,
Prs068,
Prs069,
Prs070,
Prs071,
Prs072,
Prs073,
Prs074,
Prs075,
Prs076,
Prs077,
Prs078,
Prs079,
Prs080,
Prs081,
Prs082,
Prs083,
Prs084,
Prs085,
Prs086,
Prs087,
Prs088,
Prs089,
Prs090,
Prs091,
Prs092,
Prs093,
Prs094,
Prs095,
Prs096,
Prs097,
Prs098,
Prs099,
Prs100,
Prs101,
Prs102,
Prs103,
Prs104,
Prs105,
Prs106,
Prs107,
Prs108,
Prs109,
Prs110,
Prs111,
Prs112,
Prs113,
Prs114,
Prs115,
Prs116,
Prs117,
Prs118,
Prs119,
Prs120,
Prs121,
Prs122,
Prs123,
Prs124,
Prs125,
Qry001,
Qry002,
Qry003,
Qry004,
Qry005,
Qry006,
Qry007,
Qry008,
Qry009,
Qry010,
Qry011,
Qry012,
Qry013,
Qry014,
Qry015,
Int000,
Stg001,
Stg002,
// STG-003/STG-004 are deprecated since v3.0.0: they reported truncated
// v4–v6 headers, and formats older than v7 are now rejected with STG-028
// before any size check. Kept so the codes are never reused.
#[allow(dead_code)]
Stg003,
#[allow(dead_code)]
Stg004,
Stg005,
Stg006,
Stg007,
Stg008,
Stg009,
Stg010,
Stg011,
Stg012,
Stg013,
Stg014,
Stg015,
Stg016,
Stg017,
Stg018,
Stg019,
Stg020,
Stg021,
Stg022,
Stg023,
Stg024,
Stg025,
Stg026,
Stg027,
Stg028,
Stg029,
Stg030,
Stg031,
Stg032,
Stg033,
Stg034,
Stg035,
Stg036,
Stg037,
Stg038,
Stg039,
Stg040,
Stg041,
Stg042,
Stg043,
Stg044,
Stg045,
Stg046,
Stg047,
Wal001,
Wal002,
Wal003,
Wal004,
Wal005,
Wal006,
Wal007,
Api001,
Api002,
Api003,
Api004,
Api005,
Api006,
Api007,
Api008,
Api009,
Api010,
Api011,
Api012,
// API-013 is deprecated since v3.0.0: fact logs no longer defer
// checkpoints; they pin their generation instead (#543). Kept so the code
// is never reused.
#[allow(dead_code)]
Api013,
Api014,
Api015,
Api016,
Api017,
Api018,
Api019,
Api020,
Api021,
Api022,
Api023,
Api024,
Api025,
Int001,
Int002,
Int003,
Int004,
Int 10000 005,
Int006,
Int007,
Int008,
Int009,
Int010,
Int011,
Int012,
Int013,
Int014,
Int015,
Int016,
Int017,
Int018,
Int019,
Int020,
Int021,
Int022,
Int023,
Int024,
Int025,
Int026,
Int027,
Int028,
Int029,
Int030,
Int031,
Int032,
// INT-033/034/035 are deprecated since v3.0.0: a bind mistake is the
// caller's, so it is API-022/023/024 (#181). Kept so the codes are never
// reused.
#[allow(dead_code)]
Int033,
#[allow(dead_code)]
Int034,
#[allow(dead_code)]
Int035,
Int036,
Int037,
Int038,
Int039,
Int040,
Int041,
Int042,
Int043,
Int044,
Int045,
Int046,
Int047,
Int048,
Int049,
Int050,
Int051,
Int052,
Int053,
Int054,
Int055,
Int056,
}
/// Single source of truth: (code, code string, message template, category).
///
/// The message template is verified against `docs/ERROR_REFERENCE.md`'s
/// "Error text" lines by the sync test in this module.
pub(crate) const REGISTRY: &[(ErrorCode, &str, &str, ErrorCategory)] = &[
(
ErrorCode::Prs001,
"PRS-001",
"Unexpected end of input",
ErrorCategory::Parser,
),
(
ErrorCode::Prs002,
"PRS-002",
"Unexpected character: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs003,
"PRS-003",
"Unexpected token: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs004,
"PRS-004",
"Unclosed vector",
ErrorCategory::Parser,
),
(
ErrorCode::Prs005,
"PRS-005",
"Unclosed list",
ErrorCategory::Parser,
),
(
ErrorCode::Prs006,
"PRS-006",
"Unterminated map: missing '}'",
ErrorCategory::Parser,
),
(
ErrorCode::Prs007,
"PRS-007",
"String exceeds maximum length of {} bytes",
ErrorCategory::Parser,
),
(
ErrorCode::Prs008,
"PRS-008",
"Keyword exceeds maximum length of {} bytes",
ErrorCategory::Parser,
),
(
ErrorCode::Prs009,
"PRS-009",
"Tagged literal exceeds maximum length of {} bytes",
ErrorCategory::Parser,
),
(
ErrorCode::Prs010,
"PRS-010",
"Expected command symbol",
ErrorCategory::Parser,
),
(
ErrorCode::Prs011,
"PRS-011",
"Unknown command: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs012,
"PRS-012",
"Expected a list starting with a command symbol",
ErrorCategory::Parser,
),
(
ErrorCode::Prs013,
"PRS-013",
"Query requires a map argument",
ErrorCategory::Parser,
),
(
ErrorCode::Prs014,
"PRS-014",
":as-of requires a value",
ErrorCategory::Parser,
),
(
ErrorCode::Prs015,
"PRS-015",
":as-of counter must be non-negative, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs016,
"PRS-016",
":as-of must be an integer (counter) or ISO 8601 string, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs017,
"PRS-017",
":valid-at requires a value",
ErrorCategory::Parser,
),
(
ErrorCode::Prs018,
"PRS-018",
":valid-at must be an ISO 8601 string or :any-valid-time, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs019,
"PRS-019",
":valid-from must be an ISO 8601 string, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs020,
"PRS-020",
":valid-to must be an ISO 8601 string, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs021,
"PRS-021",
"':with' clause requires at least one aggregate in :find",
ErrorCategory::Parser,
),
(
ErrorCode::Prs022,
"PRS-022",
"':with' variable {} not bound in :where",
ErrorCategory::Parser,
),
(
ErrorCode::Prs023,
"PRS-023",
"Aggregate variable {} not bound in :where",
ErrorCategory::Parser,
),
(
ErrorCode::Prs024,
"PRS-024",
"Aggregate expression must have exactly 2 elements (func ?var), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs025,
"PRS-025",
"Aggregate function name must be a symbol, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs026,
"PRS-026",
"Aggregate argument must be a variable (starting with ?)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs027,
"PRS-027",
"'{}' is a window function and requires an ':over (...)' clause",
ErrorCategory::Parser,
),
(
ErrorCode::Prs028,
"PRS-028",
"window expression cannot be empty",
ErrorCategory::Parser,
),
(
ErrorCode::Prs029,
"PRS-029",
"window function name must be a symbol",
ErrorCategory::Parser,
),
(
ErrorCode::Prs030,
"PRS-030",
"'{}' is not supported in this version; lag/lead are planned for a future release",
ErrorCategory::Parser,
),
(
ErrorCode::Prs031,
"PRS-031",
"'{}' is not window-compatible and cannot be used with ':over'",
ErrorCategory::Parser,
),
(
ErrorCode::Prs032,
"PRS-032",
"'{}' requires a variable argument (starting with ?) before ':over'",
ErrorCategory::Parser,
),
(
ErrorCode::Prs033,
"PRS-033",
"'{}' requires ':over' after the variable argument",
ErrorCategory::Parser,
),
(
ErrorCode::Prs034,
"PRS-034",
"'{}' requires ':over' immediately after the function name (no variable argument)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs035,
"PRS-035",
"':over' must be followed by a list, e.g., (:order-by ?var)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs036,
"PRS-036",
"unexpected tokens after ':over' clause in window expression",
ErrorCategory::Parser,
),
(
ErrorCode::Prs037,
"PRS-037",
"':partition-by' requires a variable (starting with ?)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs038,
"PRS-038",
"':order-by' requires a variable (starting with ?)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs039,
"PRS-039",
"unknown option in ':over' clause: '{}'",
ErrorCategory::Parser,
),
(
ErrorCode::Prs040,
"PRS-040",
"unexpected element in ':over' clause: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs041,
"PRS-041",
"Transact requires a vector of facts",
ErrorCategory::Parser,
),
(
ErrorCode::Prs042,
"PRS-042",
"Transact argument must be a vector of facts",
ErrorCategory::Parser,
),
(
ErrorCode::Prs043,
"PRS-043",
"Retract requires a vector of facts",
ErrorCategory::Parser,
),
(
ErrorCode::Prs044,
"PRS-044",
"Retract argument must be a vector of facts",
ErrorCategory::Parser,
),
(
ErrorCode::Prs045,
"PRS-045",
"Each fact must be a vector [e a v] or [e a v {opts}]",
ErrorCategory::Parser,
),
(
ErrorCode::Prs046,
"PRS-046",
"Fact must have at least 3 elements (E A V), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs047,
"PRS-047",
"Optional 4th element of a fact must be a map {:valid-from ... :valid-to ...}, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs048,
"PRS-048",
"Transact with options requires a facts vector after the map",
ErrorCategory::Parser,
),
(
ErrorCode::Prs049,
"PRS-049",
"unexpected end of fact vector",
ErrorCategory::Parser,
),
(
ErrorCode::Prs050,
"PRS-050",
"Empty list in :where clause",
ErrorCategory::Parser,
),
(
ErrorCode::Prs051,
"PRS-051",
"(not ...) cannot appear inside another (not ...)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs052,
"PRS-052",
"(not) requires at least one clause",
ErrorCategory::Parser,
),
(
ErrorCode::Prs053,
"PRS-053",
"(or) requires at least one branch",
ErrorCategory::Parser,
),
(
ErrorCode::Prs054,
"PRS-054",
"(or-join) requires a join-vars vector and at least one branch",
ErrorCategory::Parser,
),
(
ErrorCode::Prs055,
"PRS-055",
"(or-join) first argument must be a vector of join variables",
ErrorCategory::Parser,
),
(
ErrorCode::Prs056,
"PRS-056",
"(or-join) join variables must be logic variables, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs057,
"PRS-057",
"all branches of (or ...) must introduce the same set of new variables",
ErrorCategory::Parser,
),
(
ErrorCode::Prs058,
"PRS-058",
"(and) inside or/or-join requires at least one clause",
ErrorCategory::Parser,
),
(
ErrorCode::Prs059,
"PRS-059",
"(not-join) requires a join-vars vector and at least one clause",
ErrorCategory::Parser,
),
(
ErrorCode::Prs060,
"PRS-060",
"Expected pattern vector or rule invocation in :where clause, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs061,
"PRS-061",
"Unexpected element in query: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs062,
"PRS-062",
"expression list cannot be empty",
ErrorCategory::Parser,
),
(
ErrorCode::Prs063,
"PRS-063",
"expression head must be a symbol, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs064,
"PRS-064",
"{} takes exactly 1 argument",
ErrorCategory::Parser,
),
(
ErrorCode::Prs065,
"PRS-065",
"{} takes exactly 2 arguments",
ErrorCategory::Parser,
),
(
ErrorCode::Prs066,
"PRS-066",
"matches? second argument must be a string literal",
ErrorCategory::Parser,
),
(
ErrorCode::Prs067,
"PRS-067",
"unknown expression operator: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs068,
"PRS-068",
"expression clause must be [(expr)] or [(expr) ?out], got {} elements",
ErrorCategory::Parser,
),
(
ErrorCode::Prs069,
"PRS-069",
"expression output must be a ?variable, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs070,
"PRS-070",
"unsupported expression argument: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs071,
"PRS-071",
"Expected UUID string after #uuid tag",
ErrorCategory::Parser,
),
(
ErrorCode::Prs072,
"PRS-072",
"Invalid UUID",
ErrorCategory::Parser,
),
(
ErrorCode::Prs073,
"PRS-073",
"Unknown tagged literal: #{}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs074,
"PRS-074",
"Bind slot name exceeds maximum length of {} bytes",
ErrorCategory::Parser,
),
(
ErrorCode::Prs075,
"PRS-075",
"transact takes (transact [facts]) or (transact {opts} [facts]); found {} unexpected trailing argument(s) — the valid-time options map must come BEFORE the facts vector, not after",
ErrorCategory::Parser,
),
(
ErrorCode::Prs076,
"PRS-076",
"retract takes (retract [facts]); found {} unexpected trailing argument(s)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs077,
"PRS-077",
"unexpected trailing input after a complete form: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs078,
"PRS-078",
"query takes (query [...]); found {} unexpected trailing argument(s)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs079,
"PRS-079",
"rule takes (rule [...]); found {} unexpected trailing argument(s)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs080,
"PRS-080",
":find variable {} not bound in :where",
ErrorCategory::Parser,
),
(
ErrorCode::Prs081,
"PRS-081",
"{} on {} has no pattern before it, with the same entity, that reads a stored attribute; add one (e.g. [{} ?a _]) before it",
ErrorCategory::Parser,
),
(
ErrorCode::Prs082,
"PRS-082",
"{} in {} must be a plain :find variable when the query has aggregates",
ErrorCategory::Parser,
),
(
ErrorCode::Prs083,
"PRS-083",
"Bind slot '$' must be followed by an identifier (e.g. $entity)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs084,
"PRS-084",
"':order-by' is required in the ':over' clause",
ErrorCategory::Parser,
),
(
ErrorCode::Prs085,
"PRS-085",
"Query argument must be a vector",
ErrorCategory::Parser,
),
(
ErrorCode::Prs086,
"PRS-086",
"':with' clause accepts only variables, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs087,
"PRS-087",
"Expected variable or aggregate expression in :find clause, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs088,
"PRS-088",
"Unknown key in valid-time map: {}; expected :valid-from or :valid-to",
ErrorCategory::Parser,
),
(
ErrorCode::Prs089,
"PRS-089",
"Each fact must be a vector [e a v]",
ErrorCategory::Parser,
),
(
ErrorCode::Prs090,
"PRS-090",
"Retract fact must have exactly 3 elements (E A V), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs091,
"PRS-091",
"invalid regex pattern {}: {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs092,
"PRS-092",
"expected pattern or rule invocation inside (not), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs093,
"PRS-093",
"(not-join ...) cannot appear inside another (not ...) or (not-join ...)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs094,
"PRS-094",
"(not-join) first argument must be a vector of join variables",
ErrorCategory::Parser,
),
(
ErrorCode::Prs095,
"PRS-095",
"(not-join) join variables must be logic variables, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs096,
"PRS-096",
"expected pattern or rule invocation inside (not-join), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs097,
"PRS-097",
"Rule invocation must start with predicate name (symbol), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs098,
"PRS-098",
"Pattern must have exactly 3 elements (E A V), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs099,
"PRS-099",
"expected clause inside or branch, got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs100,
"PRS-100",
"Rule must have a body",
ErrorCategory::Parser,
),
(
ErrorCode::Prs101,
"PRS-101",
"Rule body must be a vector",
ErrorCategory::Parser,
),
(
ErrorCode::Prs102,
"PRS-102",
"Rule body cannot be empty",
ErrorCategory::Parser,
),
(
ErrorCode::Prs103,
"PRS-103",
"Rule head must be a list: (predicate ?args)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs104,
"PRS-104",
"Rule head cannot be empty",
ErrorCategory::Parser,
),
(
ErrorCode::Prs105,
"PRS-105",
"Rule head must start with a symbol (predicate name)",
ErrorCategory::Parser,
),
(
ErrorCode::Prs106,
"PRS-106",
"Rule body clause must be a vector (pattern) or list (rule invocation / not), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs107,
"PRS-107",
"Rule must have at least one pattern or rule invocation in body",
ErrorCategory::Parser,
),
(
ErrorCode::Prs108,
"PRS-108",
"Fact must have at most 4 elements (E A V and an optional map), got {}",
ErrorCategory::Parser,
),
(
ErrorCode::Prs109,
0