Skip to main content

reifydb_routine/function/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_type::{
5	error::{Diagnostic, Error, IntoDiagnostic, TypeError},
6	fragment::Fragment,
7	value::r#type::Type,
8};
9
10#[derive(Debug, thiserror::Error)]
11pub enum FunctionError {
12	#[error("function {} expects {expected} arguments, got {actual}", function.text())]
13	ArityMismatch {
14		function: Fragment,
15		expected: usize,
16		actual: usize,
17	},
18
19	#[error("function {} argument {} has invalid type: got {actual:?}", function.text(), argument_index + 1)]
20	InvalidArgumentType {
21		function: Fragment,
22		argument_index: usize,
23		expected: Vec<Type>,
24		actual: Type,
25	},
26
27	#[error("function {} execution failed: {reason}", function.text())]
28	ExecutionFailed {
29		function: Fragment,
30		reason: String,
31	},
32
33	#[error("generator function '{}' not found", function.text())]
34	NotFound {
35		function: Fragment,
36	},
37
38	#[error(transparent)]
39	Wrapped(Box<Error>),
40}
41
42impl From<Error> for FunctionError {
43	fn from(err: Error) -> Self {
44		FunctionError::Wrapped(Box::new(err))
45	}
46}
47
48impl From<TypeError> for FunctionError {
49	fn from(err: TypeError) -> Self {
50		FunctionError::Wrapped(Box::new(Error::from(err)))
51	}
52}
53
54impl From<Box<TypeError>> for FunctionError {
55	fn from(err: Box<TypeError>) -> Self {
56		FunctionError::Wrapped(Box::new(Error::from(err)))
57	}
58}
59
60impl FunctionError {
61	/// Attach function name context to Wrapped errors.
62	/// Named variants already carry the function name and convert normally.
63	/// Wrapped variants become FUNCTION_007 with the inner error as `cause`.
64	pub fn with_context(self, function: Fragment) -> Error {
65		match self {
66			FunctionError::Wrapped(inner) => {
67				let name = function.text().to_string();
68				let mut cause = *inner.0;
69				cause.with_fragment(function.clone());
70				Error(Box::new(Diagnostic {
71					code: "FUNCTION_007".to_string(),
72					rql: None,
73					message: format!("Function {} execution failed", name),
74					column: None,
75					fragment: function,
76					label: Some("execution failed".to_string()),
77					help: Some("Check function arguments and data".to_string()),
78					notes: vec![],
79					cause: Some(Box::new(cause)),
80					operator_chain: None,
81				}))
82			}
83			other => Error(Box::new(other.into_diagnostic())),
84		}
85	}
86}
87
88impl From<FunctionError> for Error {
89	fn from(err: FunctionError) -> Self {
90		Error(Box::new(err.into_diagnostic()))
91	}
92}
93
94impl IntoDiagnostic for FunctionError {
95	fn into_diagnostic(self) -> Diagnostic {
96		match self {
97			FunctionError::ArityMismatch {
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			FunctionError::InvalidArgumentType {
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			FunctionError::ExecutionFailed {
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			FunctionError::NotFound {
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			FunctionError::Wrapped(err) => *err.0,
187		}
188	}
189}