1use reifydb_catalog::error::CatalogError;
5use reifydb_type::{
6 error::{Diagnostic, Error, IntoDiagnostic, TypeError},
7 fragment::Fragment,
8 value::r#type::Type,
9};
10
11#[derive(Debug, thiserror::Error)]
20pub enum RoutineError {
21 #[error("function {} expects {expected} arguments, got {actual}", function.text())]
22 FunctionArityMismatch {
23 function: Fragment,
24 expected: usize,
25 actual: usize,
26 },
27
28 #[error("function {} argument {} has invalid type: got {actual:?}", function.text(), argument_index + 1)]
29 FunctionInvalidArgumentType {
30 function: Fragment,
31 argument_index: usize,
32 expected: Vec<Type>,
33 actual: Type,
34 },
35
36 #[error("function {} execution failed: {reason}", function.text())]
37 FunctionExecutionFailed {
38 function: Fragment,
39 reason: String,
40 },
41
42 #[error("generator function '{}' not found", function.text())]
43 FunctionNotFound {
44 function: Fragment,
45 },
46
47 #[error("procedure {} expects {expected} arguments, got {actual}", procedure.text())]
48 ProcedureArityMismatch {
49 procedure: Fragment,
50 expected: usize,
51 actual: usize,
52 },
53
54 #[error("procedure {} argument {} has invalid type: got {actual:?}", procedure.text(), argument_index + 1)]
55 ProcedureInvalidArgumentType {
56 procedure: Fragment,
57 argument_index: usize,
58 expected: Vec<Type>,
59 actual: Type,
60 },
61
62 #[error("procedure {} execution failed: {reason}", procedure.text())]
63 ProcedureExecutionFailed {
64 procedure: Fragment,
65 reason: String,
66 },
67
68 #[error(transparent)]
69 Wrapped(Box<Error>),
70}
71
72impl From<Error> for RoutineError {
73 fn from(err: Error) -> Self {
74 RoutineError::Wrapped(Box::new(err))
75 }
76}
77
78impl From<CatalogError> for RoutineError {
79 fn from(err: CatalogError) -> Self {
80 RoutineError::Wrapped(Box::new(Error::from(err)))
81 }
82}
83
84impl From<TypeError> for RoutineError {
85 fn from(err: TypeError) -> Self {
86 RoutineError::Wrapped(Box::new(Error::from(err)))
87 }
88}
89
90impl From<Box<TypeError>> for RoutineError {
91 fn from(err: Box<TypeError>) -> Self {
92 RoutineError::Wrapped(Box::new(Error::from(err)))
93 }
94}
95
96impl From<RoutineError> for Error {
97 fn from(err: RoutineError) -> Self {
98 Error(Box::new(err.into_diagnostic()))
99 }
100}
101
102impl IntoDiagnostic for RoutineError {
103 fn into_diagnostic(self) -> Diagnostic {
104 match self {
105 RoutineError::FunctionArityMismatch {
106 function,
107 expected,
108 actual,
109 } => {
110 let name = function.text().to_string();
111 Diagnostic {
112 code: "FUNCTION_002".to_string(),
113 rql: None,
114 message: format!(
115 "Function {} expects {} arguments, got {}",
116 name, expected, actual
117 ),
118 column: None,
119 fragment: function,
120 label: Some("wrong number of arguments".to_string()),
121 help: Some(format!(
122 "Provide exactly {} arguments to function {}",
123 expected, name
124 )),
125 notes: vec![],
126 cause: None,
127 operator_chain: None,
128 }
129 }
130 RoutineError::FunctionInvalidArgumentType {
131 function,
132 argument_index,
133 expected,
134 actual,
135 } => {
136 let name = function.text().to_string();
137 let expected_types =
138 expected.iter().map(|t| format!("{:?}", t)).collect::<Vec<_>>().join(", ");
139 Diagnostic {
140 code: "FUNCTION_004".to_string(),
141 rql: None,
142 message: format!(
143 "Function {} argument {} has invalid type: expected one of [{}], got {:?}",
144 name,
145 argument_index + 1,
146 expected_types,
147 actual
148 ),
149 column: None,
150 fragment: function,
151 label: Some("invalid argument type".to_string()),
152 help: Some(format!("Provide an argument of type: {}", expected_types)),
153 notes: vec![],
154 cause: None,
155 operator_chain: None,
156 }
157 }
158 RoutineError::FunctionExecutionFailed {
159 function,
160 reason,
161 } => {
162 let name = function.text().to_string();
163 Diagnostic {
164 code: "FUNCTION_007".to_string(),
165 rql: None,
166 message: format!("Function {} execution failed: {}", name, reason),
167 column: None,
168 fragment: function,
169 label: Some("execution failed".to_string()),
170 help: Some("Check function arguments and data".to_string()),
171 notes: vec![],
172 cause: None,
173 operator_chain: None,
174 }
175 }
176 RoutineError::FunctionNotFound {
177 function,
178 } => {
179 let name = function.text().to_string();
180 Diagnostic {
181 code: "FUNCTION_009".to_string(),
182 rql: None,
183 message: format!("Generator function '{}' not found", name),
184 column: None,
185 fragment: function,
186 label: Some("unknown generator function".to_string()),
187 help: Some("Check the generator function name and ensure it is registered"
188 .to_string()),
189 notes: vec![],
190 cause: None,
191 operator_chain: None,
192 }
193 }
194 RoutineError::ProcedureArityMismatch {
195 procedure,
196 expected,
197 actual,
198 } => {
199 let name = procedure.text().to_string();
200 Diagnostic {
201 code: "PROCEDURE_001".to_string(),
202 rql: None,
203 message: format!(
204 "Procedure {} expects {} arguments, got {}",
205 name, expected, actual
206 ),
207 column: None,
208 fragment: procedure,
209 label: Some("wrong number of arguments".to_string()),
210 help: Some(format!(
211 "Provide exactly {} arguments to procedure {}",
212 expected, name
213 )),
214 notes: vec![],
215 cause: None,
216 operator_chain: None,
217 }
218 }
219 RoutineError::ProcedureInvalidArgumentType {
220 procedure,
221 argument_index,
222 expected,
223 actual,
224 } => {
225 let name = procedure.text().to_string();
226 let expected_types =
227 expected.iter().map(|t| format!("{:?}", t)).collect::<Vec<_>>().join(", ");
228 Diagnostic {
229 code: "PROCEDURE_002".to_string(),
230 rql: None,
231 message: format!(
232 "Procedure {} argument {} has invalid type: expected one of [{}], got {:?}",
233 name,
234 argument_index + 1,
235 expected_types,
236 actual
237 ),
238 column: None,
239 fragment: procedure,
240 label: Some("invalid argument type".to_string()),
241 help: Some(format!("Provide an argument of type: {}", expected_types)),
242 notes: vec![],
243 cause: None,
244 operator_chain: None,
245 }
246 }
247 RoutineError::ProcedureExecutionFailed {
248 procedure,
249 reason,
250 } => {
251 let name = procedure.text().to_string();
252 Diagnostic {
253 code: "PROCEDURE_003".to_string(),
254 rql: None,
255 message: format!("Procedure {} execution failed: {}", name, reason),
256 column: None,
257 fragment: procedure,
258 label: Some("execution failed".to_string()),
259 help: Some("Check procedure arguments and context".to_string()),
260 notes: vec![],
261 cause: None,
262 operator_chain: None,
263 }
264 }
265 RoutineError::Wrapped(err) => *err.0,
266 }
267 }
268}
269
270impl RoutineError {
271 pub fn with_context(self, fragment: Fragment, is_procedure: bool) -> Error {
276 match self {
277 RoutineError::Wrapped(inner) => {
278 let name = fragment.text().to_string();
279 let mut cause = *inner.0;
280 cause.with_fragment(fragment.clone());
281 let (code, message, help) = if is_procedure {
282 (
283 "PROCEDURE_003",
284 format!("Procedure {} execution failed", name),
285 "Check procedure arguments and context",
286 )
287 } else {
288 (
289 "FUNCTION_007",
290 format!("Function {} execution failed", name),
291 "Check function arguments and data",
292 )
293 };
294 Error(Box::new(Diagnostic {
295 code: code.to_string(),
296 rql: None,
297 message,
298 column: None,
299 fragment,
300 label: Some("execution failed".to_string()),
301 help: Some(help.to_string()),
302 notes: vec![],
303 cause: Some(Box::new(cause)),
304 operator_chain: None,
305 }))
306 }
307 other => Error(Box::new(other.into_diagnostic())),
308 }
309 }
310}