ddx_core/error.rs
1// SPDX-FileCopyrightText: 2026 Alexander Merose <al@merose.com> & ddx Authors
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! The error type for differentiation and rewriting.
6
7use std::fmt;
8
9/// An error produced while differentiating or rewriting SQL.
10///
11/// Every failure mode of `ddx-core` is one of these. In keeping with design
12/// principle 5 — *fail loud, never silently wrong* (design.md §2) — an
13/// unsupported construct is always one of these typed errors, never an
14/// approximate or silently-zero derivative.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum DiffError {
17 /// A node or function has no differentiation rule. This is the "permanent
18 /// or roadmap" bucket: `atan2` (no rule yet), general `u^v`, `CASE`,
19 /// comparisons, string/temporal expressions (design.md §3.6).
20 NotImplemented(String),
21
22 /// The `wrt` (or a marker call) is malformed: `wrt` is not a bare column,
23 /// wrong argument count, etc.
24 InvalidMarker(String),
25
26 /// An occurrence of the `wrt` base name could not be pinned syntactically —
27 /// a bare occurrence when `wrt` is qualified, or a qualified occurrence
28 /// when `wrt` is bare. Hard error demanding full qualification
29 /// (design.md §3.2, F2).
30 AmbiguousColumn(String),
31
32 /// A marker argument references an identifier that is a *computed*
33 /// select-list alias of a CTE/derived table in the same statement, used as
34 /// a non-`wrt` term — differentiation would silently drop terms across the
35 /// projection boundary (design.md §3.5, F3/G4).
36 ProjectionBoundary(String),
37
38 /// The input SQL did not parse under the given dialect. Only ever reported
39 /// for a statement that *contains* a marker (the parse-free pre-gate means
40 /// marker-free statements are never parsed, design.md §3.2, F5).
41 Parse(String),
42
43 /// An internal invariant was violated (e.g. an empty source span the API
44 /// documents as possible, with no safe fallback). Should not occur in
45 /// normal use.
46 Internal(String),
47}
48
49impl fmt::Display for DiffError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 DiffError::NotImplemented(m) => write!(f, "not implemented: {m}"),
53 DiffError::InvalidMarker(m) => write!(f, "invalid marker call: {m}"),
54 DiffError::AmbiguousColumn(m) => write!(f, "ambiguous column: {m}"),
55 DiffError::ProjectionBoundary(m) => write!(f, "projection boundary: {m}"),
56 DiffError::Parse(m) => write!(f, "parse error: {m}"),
57 DiffError::Internal(m) => write!(f, "internal error: {m}"),
58 }
59 }
60}
61
62impl std::error::Error for DiffError {}
63
64/// The result type used throughout `ddx-core`.
65pub type Result<T> = std::result::Result<T, DiffError>;