Skip to main content

linkrs_query/executor/
build_error.rs

1use std::fmt;
2
3use linkrs_core::error::QueryError;
4
5/// Structured error type for physical plan construction failures.
6///
7/// Every variant carries a stable discriminant so callers can match on the
8/// specific kind of error without parsing a free-text message.
9#[derive(Debug, Clone)]
10pub enum PlanBuildError {
11    /// The plan node type has no physical executor implementation.
12    UnsupportedNode {
13        node_type: String,
14        node_id: i64,
15        detail: String,
16    },
17    /// A required configuration value is missing from the plan or context.
18    MissingRequiredValue {
19        node_type: String,
20        node_id: i64,
21        field: String,
22        detail: String,
23    },
24    /// The runtime does not provide a capability that the plan requires.
25    CapabilityUnavailable { capability: String, detail: String },
26    /// The plan requires a transaction mode that does not match the current scope.
27    InvalidTransactionMode {
28        required: String,
29        actual: String,
30        detail: String,
31    },
32    /// An expression references a slot, parameter, or variable that cannot be bound.
33    ExpressionBinding {
34        node_type: String,
35        node_id: i64,
36        expression: String,
37        detail: String,
38    },
39}
40
41impl PlanBuildError {
42    pub fn unsupported(
43        node_type: impl Into<String>,
44        node_id: i64,
45        detail: impl Into<String>,
46    ) -> Self {
47        Self::UnsupportedNode {
48            node_type: node_type.into(),
49            node_id,
50            detail: detail.into(),
51        }
52    }
53
54    pub fn missing_value(
55        node_type: impl Into<String>,
56        node_id: i64,
57        field: impl Into<String>,
58        detail: impl Into<String>,
59    ) -> Self {
60        Self::MissingRequiredValue {
61            node_type: node_type.into(),
62            node_id,
63            field: field.into(),
64            detail: detail.into(),
65        }
66    }
67
68    pub fn capability(capability: impl Into<String>, detail: impl Into<String>) -> Self {
69        Self::CapabilityUnavailable {
70            capability: capability.into(),
71            detail: detail.into(),
72        }
73    }
74
75    pub fn expression(
76        node_type: impl Into<String>,
77        node_id: i64,
78        expression: impl Into<String>,
79        detail: impl Into<String>,
80    ) -> Self {
81        Self::ExpressionBinding {
82            node_type: node_type.into(),
83            node_id,
84            expression: expression.into(),
85            detail: detail.into(),
86        }
87    }
88}
89
90impl fmt::Display for PlanBuildError {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            Self::UnsupportedNode {
94                node_type,
95                node_id,
96                detail,
97            } => {
98                write!(
99                    f,
100                    "Unsupported node '{}' (id={}): {}",
101                    node_type, node_id, detail
102                )
103            }
104            Self::MissingRequiredValue {
105                node_type,
106                node_id,
107                field,
108                detail,
109            } => {
110                write!(
111                    f,
112                    "Missing required value '{}' for node '{}' (id={}): {}",
113                    field, node_type, node_id, detail
114                )
115            }
116            Self::CapabilityUnavailable { capability, detail } => {
117                write!(
118                    f,
119                    "Capability '{}' is not available: {}",
120                    capability, detail
121                )
122            }
123            Self::InvalidTransactionMode {
124                required,
125                actual,
126                detail,
127            } => {
128                write!(
129                    f,
130                    "Transaction mode mismatch: required '{}', actual '{}': {}",
131                    required, actual, detail
132                )
133            }
134            Self::ExpressionBinding {
135                node_type,
136                node_id,
137                expression,
138                detail,
139            } => {
140                write!(
141                    f,
142                    "Expression binding error in node '{}' (id={}): '{}' - {}",
143                    node_type, node_id, expression, detail
144                )
145            }
146        }
147    }
148}
149
150impl std::error::Error for PlanBuildError {}
151
152impl From<PlanBuildError> for QueryError {
153    fn from(e: PlanBuildError) -> Self {
154        QueryError::execution(e.to_string())
155    }
156}