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