Skip to main content

surrealdb_core/exec/function/
macros.rs

1//! Macros for defining scalar functions with minimal boilerplate.
2
3/// Define a pure scalar function that wraps an existing `fnc::*` implementation.
4///
5/// # Usage
6///
7/// ```ignore
8/// // Simple function with one argument
9/// define_pure_function!(
10///     MathAbs,                        // Struct name
11///     "math::abs",                    // Function name
12///     (value: Number) -> Number,      // Signature: (args) -> return
13///     crate::fnc::math::abs           // Implementation path
14/// );
15///
16/// // Function with multiple arguments
17/// define_pure_function!(
18///     MathClamp,
19///     "math::clamp",
20///     (value: Number, min: Number, max: Number) -> Number,
21///     crate::fnc::math::clamp
22/// );
23///
24/// // Function with no arguments
25/// define_pure_function!(
26///     Rand,
27///     "rand",
28///     () -> Float,
29///     crate::fnc::rand::rand
30/// );
31///
32/// // Function with optional arguments
33/// define_pure_function!(
34///     MathRound,
35///     "math::round",
36///     (value: Number, ?precision: Number) -> Number,
37///     crate::fnc::math::round
38/// );
39///
40/// // Function with variadic arguments
41/// define_pure_function!(
42///     StringConcat,
43///     "string::concat",
44///     (...values: Any) -> String,
45///     crate::fnc::string::concat
46/// );
47/// ```
48#[macro_export]
49macro_rules! define_pure_function {
50	// No arguments: () -> ReturnType
51	(
52		$struct_name:ident,
53		$func_name:literal,
54		() -> $ret:ident,
55		$impl_path:path
56	) => {
57		#[derive(Debug, Clone, Copy, Default)]
58		pub struct $struct_name;
59
60		impl $crate::exec::function::ScalarFunction for $struct_name {
61			fn name(&self) -> &'static str {
62				$func_name
63			}
64
65			fn signature(&self) -> $crate::exec::function::Signature {
66				$crate::exec::function::Signature::new().returns($crate::expr::Kind::$ret)
67			}
68
69			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
70				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
71				$impl_path(args)
72			}
73		}
74	};
75
76	// Single required argument: (name: Type) -> ReturnType
77	(
78		$struct_name:ident,
79		$func_name:literal,
80		($arg_name:ident : $arg_type:ident) -> $ret:ident,
81		$impl_path:path
82	) => {
83		#[derive(Debug, Clone, Copy, Default)]
84		pub struct $struct_name;
85
86		impl $crate::exec::function::ScalarFunction for $struct_name {
87			fn name(&self) -> &'static str {
88				$func_name
89			}
90
91			fn signature(&self) -> $crate::exec::function::Signature {
92				$crate::exec::function::Signature::new()
93					.arg(stringify!($arg_name), $crate::expr::Kind::$arg_type)
94					.returns($crate::expr::Kind::$ret)
95			}
96
97			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
98				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
99				$impl_path(args)
100			}
101		}
102	};
103
104	// Two required arguments: (a: Type1, b: Type2) -> ReturnType
105	(
106		$struct_name:ident,
107		$func_name:literal,
108		($arg1_name:ident : $arg1_type:ident, $arg2_name:ident : $arg2_type:ident) -> $ret:ident,
109		$impl_path:path
110	) => {
111		#[derive(Debug, Clone, Copy, Default)]
112		pub struct $struct_name;
113
114		impl $crate::exec::function::ScalarFunction for $struct_name {
115			fn name(&self) -> &'static str {
116				$func_name
117			}
118
119			fn signature(&self) -> $crate::exec::function::Signature {
120				$crate::exec::function::Signature::new()
121					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
122					.arg(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
123					.returns($crate::expr::Kind::$ret)
124			}
125
126			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
127				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
128				$impl_path(args)
129			}
130		}
131	};
132
133	// Three required arguments
134	(
135		$struct_name:ident,
136		$func_name:literal,
137		($arg1_name:ident : $arg1_type:ident, $arg2_name:ident : $arg2_type:ident, $arg3_name:ident : $arg3_type:ident) -> $ret:ident,
138		$impl_path:path
139	) => {
140		#[derive(Debug, Clone, Copy, Default)]
141		pub struct $struct_name;
142
143		impl $crate::exec::function::ScalarFunction for $struct_name {
144			fn name(&self) -> &'static str {
145				$func_name
146			}
147
148			fn signature(&self) -> $crate::exec::function::Signature {
149				$crate::exec::function::Signature::new()
150					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
151					.arg(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
152					.arg(stringify!($arg3_name), $crate::expr::Kind::$arg3_type)
153					.returns($crate::expr::Kind::$ret)
154			}
155
156			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
157				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
158				$impl_path(args)
159			}
160		}
161	};
162
163	// Variadic: (...name: Type) -> ReturnType
164	(
165		$struct_name:ident,
166		$func_name:literal,
167		(... $arg_name:ident : $arg_type:ident) -> $ret:ident,
168		$impl_path:path
169	) => {
170		#[derive(Debug, Clone, Copy, Default)]
171		pub struct $struct_name;
172
173		impl $crate::exec::function::ScalarFunction for $struct_name {
174			fn name(&self) -> &'static str {
175				$func_name
176			}
177
178			fn signature(&self) -> $crate::exec::function::Signature {
179				$crate::exec::function::Signature::new()
180					.variadic($crate::expr::Kind::$arg_type)
181					.returns($crate::expr::Kind::$ret)
182			}
183
184			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
185				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
186				$impl_path(args)
187			}
188		}
189	};
190
191	// One required + variadic: (first: Type1, ...rest: Type2) -> ReturnType
192	(
193		$struct_name:ident,
194		$func_name:literal,
195		($arg1_name:ident : $arg1_type:ident, ... $rest_name:ident : $rest_type:ident) -> $ret:ident,
196		$impl_path:path
197	) => {
198		#[derive(Debug, Clone, Copy, Default)]
199		pub struct $struct_name;
200
201		impl $crate::exec::function::ScalarFunction for $struct_name {
202			fn name(&self) -> &'static str {
203				$func_name
204			}
205
206			fn signature(&self) -> $crate::exec::function::Signature {
207				$crate::exec::function::Signature::new()
208					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
209					.variadic($crate::expr::Kind::$rest_type)
210					.returns($crate::expr::Kind::$ret)
211			}
212
213			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
214				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
215				$impl_path(args)
216			}
217		}
218	};
219
220	// One required + one optional: (req: Type1, ?opt: Type2) -> ReturnType
221	(
222		$struct_name:ident,
223		$func_name:literal,
224		($arg1_name:ident : $arg1_type:ident, ? $arg2_name:ident : $arg2_type:ident) -> $ret:ident,
225		$impl_path:path
226	) => {
227		#[derive(Debug, Clone, Copy, Default)]
228		pub struct $struct_name;
229
230		impl $crate::exec::function::ScalarFunction for $struct_name {
231			fn name(&self) -> &'static str {
232				$func_name
233			}
234
235			fn signature(&self) -> $crate::exec::function::Signature {
236				$crate::exec::function::Signature::new()
237					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
238					.optional(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
239					.returns($crate::expr::Kind::$ret)
240			}
241
242			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
243				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
244				$impl_path(args)
245			}
246		}
247	};
248
249	// Two required + one optional: (a: T1, b: T2, ?c: T3) -> ReturnType
250	(
251		$struct_name:ident,
252		$func_name:literal,
253		($arg1_name:ident : $arg1_type:ident, $arg2_name:ident : $arg2_type:ident, ? $arg3_name:ident : $arg3_type:ident) -> $ret:ident,
254		$impl_path:path
255	) => {
256		#[derive(Debug, Clone, Copy, Default)]
257		pub struct $struct_name;
258
259		impl $crate::exec::function::ScalarFunction for $struct_name {
260			fn name(&self) -> &'static str {
261				$func_name
262			}
263
264			fn signature(&self) -> $crate::exec::function::Signature {
265				$crate::exec::function::Signature::new()
266					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
267					.arg(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
268					.optional(stringify!($arg3_name), $crate::expr::Kind::$arg3_type)
269					.returns($crate::expr::Kind::$ret)
270			}
271
272			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
273				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
274				$impl_path(args)
275			}
276		}
277	};
278
279	// Two optional arguments: (?a: T1, ?b: T2) -> ReturnType
280	(
281		$struct_name:ident,
282		$func_name:literal,
283		(? $arg1_name:ident : $arg1_type:ident, ? $arg2_name:ident : $arg2_type:ident) -> $ret:ident,
284		$impl_path:path
285	) => {
286		#[derive(Debug, Clone, Copy, Default)]
287		pub struct $struct_name;
288
289		impl $crate::exec::function::ScalarFunction for $struct_name {
290			fn name(&self) -> &'static str {
291				$func_name
292			}
293
294			fn signature(&self) -> $crate::exec::function::Signature {
295				$crate::exec::function::Signature::new()
296					.optional(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
297					.optional(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
298					.returns($crate::expr::Kind::$ret)
299			}
300
301			fn invoke(&self, args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
302				let args = $crate::fnc::args::FromArgs::from_args($func_name, args)?;
303				$impl_path(args)
304			}
305		}
306	};
307}
308
309/// Helper macro to register multiple functions at once.
310///
311/// # Usage
312///
313/// ```ignore
314/// register_functions!(registry,
315///     MathAbs,
316///     MathCeil,
317///     MathFloor,
318///     // ...
319/// );
320/// ```
321#[macro_export]
322macro_rules! register_functions {
323	($registry:expr, $($func:ty),* $(,)?) => {
324		$(
325			$registry.register(<$func>::default());
326		)*
327	};
328}
329
330/// Define a context-aware scalar function that needs access to EvalContext.
331///
332/// Context-aware functions are not pure (they depend on session state) but are
333/// not async. They synchronously access context information like session::ns(),
334/// session::db(), etc.
335///
336/// # Usage
337///
338/// ```ignore
339/// // Function with no arguments that reads from context
340/// define_context_function!(
341///     SessionNs,                      // Struct name
342///     "session::ns",                  // Function name
343///     () -> Any,                      // Signature: () -> return type
344///     session_ns_impl                 // Implementation function
345/// );
346/// ```
347#[macro_export]
348macro_rules! define_context_function {
349	// No arguments: () -> ReturnType
350	(
351		$struct_name:ident,
352		$func_name:literal,
353		() -> $ret:ident,
354		$impl_fn:expr
355	) => {
356		#[derive(Debug, Clone, Copy, Default)]
357		pub struct $struct_name;
358
359		impl $crate::exec::function::ScalarFunction for $struct_name {
360			fn name(&self) -> &'static str {
361				$func_name
362			}
363
364			fn signature(&self) -> $crate::exec::function::Signature {
365				$crate::exec::function::Signature::new().returns($crate::expr::Kind::$ret)
366			}
367
368			fn is_pure(&self) -> bool {
369				false
370			}
371
372			fn invoke(&self, _args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
373				Err(anyhow::anyhow!("Function '{}' requires context", self.name()))
374			}
375
376			fn invoke_async<'a>(
377				&'a self,
378				ctx: &'a $crate::exec::physical_expr::EvalContext<'_>,
379				_args: Vec<$crate::val::Value>,
380			) -> $crate::exec::BoxFut<'a, anyhow::Result<$crate::val::Value>> {
381				Box::pin(async move { $impl_fn(ctx) })
382			}
383		}
384	};
385}
386
387/// Define an async scalar function that needs access to EvalContext.
388///
389/// Async functions are used for:
390/// - I/O-bound operations (HTTP requests)
391/// - CPU-intensive operations (crypto hashing)
392/// - Timer-based operations (sleep)
393///
394/// # Usage
395///
396/// ```ignore
397/// // Async function with one argument
398/// define_async_function!(
399///     Sleep,                          // Struct name
400///     "sleep",                        // Function name
401///     (duration: Duration) -> None,   // Signature
402///     sleep_impl                      // Async implementation function
403/// );
404///
405/// // Async function with two arguments
406/// define_async_function!(
407///     HttpGet,
408///     "http::get",
409///     (url: String, ?opts: Object) -> Any,
410///     http_get_impl
411/// );
412/// ```
413#[macro_export]
414macro_rules! define_async_function {
415	// No arguments: () -> ReturnType
416	(
417		$struct_name:ident,
418		$func_name:literal,
419		() -> $ret:ident,
420		$impl_fn:expr
421	) => {
422		#[derive(Debug, Clone, Copy, Default)]
423		pub struct $struct_name;
424
425		impl $crate::exec::function::ScalarFunction for $struct_name {
426			fn name(&self) -> &'static str {
427				$func_name
428			}
429
430			fn signature(&self) -> $crate::exec::function::Signature {
431				$crate::exec::function::Signature::new().returns($crate::expr::Kind::$ret)
432			}
433
434			fn is_pure(&self) -> bool {
435				false
436			}
437
438			fn is_async(&self) -> bool {
439				true
440			}
441
442			fn invoke(&self, _args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
443				Err(anyhow::anyhow!("Function '{}' requires async execution", self.name()))
444			}
445
446			fn invoke_async<'a>(
447				&'a self,
448				ctx: &'a $crate::exec::physical_expr::EvalContext<'_>,
449				_args: Vec<$crate::val::Value>,
450			) -> $crate::exec::BoxFut<'a, anyhow::Result<$crate::val::Value>> {
451				Box::pin(async move { $impl_fn(ctx).await })
452			}
453		}
454	};
455
456	// Single required argument: (name: Type) -> ReturnType
457	(
458		$struct_name:ident,
459		$func_name:literal,
460		($arg_name:ident : $arg_type:ident) -> $ret:ident,
461		$impl_fn:expr
462	) => {
463		#[derive(Debug, Clone, Copy, Default)]
464		pub struct $struct_name;
465
466		impl $crate::exec::function::ScalarFunction for $struct_name {
467			fn name(&self) -> &'static str {
468				$func_name
469			}
470
471			fn signature(&self) -> $crate::exec::function::Signature {
472				$crate::exec::function::Signature::new()
473					.arg(stringify!($arg_name), $crate::expr::Kind::$arg_type)
474					.returns($crate::expr::Kind::$ret)
475			}
476
477			fn is_pure(&self) -> bool {
478				false
479			}
480
481			fn is_async(&self) -> bool {
482				true
483			}
484
485			fn invoke(&self, _args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
486				Err(anyhow::anyhow!("Function '{}' requires async execution", self.name()))
487			}
488
489			fn invoke_async<'a>(
490				&'a self,
491				ctx: &'a $crate::exec::physical_expr::EvalContext<'_>,
492				args: Vec<$crate::val::Value>,
493			) -> $crate::exec::BoxFut<'a, anyhow::Result<$crate::val::Value>> {
494				Box::pin(async move { $impl_fn(ctx, args).await })
495			}
496		}
497	};
498
499	// Two required arguments: (a: Type1, b: Type2) -> ReturnType
500	(
501		$struct_name:ident,
502		$func_name:literal,
503		($arg1_name:ident : $arg1_type:ident, $arg2_name:ident : $arg2_type:ident) -> $ret:ident,
504		$impl_fn:expr
505	) => {
506		#[derive(Debug, Clone, Copy, Default)]
507		pub struct $struct_name;
508
509		impl $crate::exec::function::ScalarFunction for $struct_name {
510			fn name(&self) -> &'static str {
511				$func_name
512			}
513
514			fn signature(&self) -> $crate::exec::function::Signature {
515				$crate::exec::function::Signature::new()
516					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
517					.arg(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
518					.returns($crate::expr::Kind::$ret)
519			}
520
521			fn is_pure(&self) -> bool {
522				false
523			}
524
525			fn is_async(&self) -> bool {
526				true
527			}
528
529			fn invoke(&self, _args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
530				Err(anyhow::anyhow!("Function '{}' requires async execution", self.name()))
531			}
532
533			fn invoke_async<'a>(
534				&'a self,
535				ctx: &'a $crate::exec::physical_expr::EvalContext<'_>,
536				args: Vec<$crate::val::Value>,
537			) -> $crate::exec::BoxFut<'a, anyhow::Result<$crate::val::Value>> {
538				Box::pin(async move { $impl_fn(ctx, args).await })
539			}
540		}
541	};
542
543	// One required + one optional: (req: Type1, ?opt: Type2) -> ReturnType
544	(
545		$struct_name:ident,
546		$func_name:literal,
547		($arg1_name:ident : $arg1_type:ident, ? $arg2_name:ident : $arg2_type:ident) -> $ret:ident,
548		$impl_fn:expr
549	) => {
550		#[derive(Debug, Clone, Copy, Default)]
551		pub struct $struct_name;
552
553		impl $crate::exec::function::ScalarFunction for $struct_name {
554			fn name(&self) -> &'static str {
555				$func_name
556			}
557
558			fn signature(&self) -> $crate::exec::function::Signature {
559				$crate::exec::function::Signature::new()
560					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
561					.optional(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
562					.returns($crate::expr::Kind::$ret)
563			}
564
565			fn is_pure(&self) -> bool {
566				false
567			}
568
569			fn is_async(&self) -> bool {
570				true
571			}
572
573			fn invoke(&self, _args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
574				Err(anyhow::anyhow!("Function '{}' requires async execution", self.name()))
575			}
576
577			fn invoke_async<'a>(
578				&'a self,
579				ctx: &'a $crate::exec::physical_expr::EvalContext<'_>,
580				args: Vec<$crate::val::Value>,
581			) -> $crate::exec::BoxFut<'a, anyhow::Result<$crate::val::Value>> {
582				Box::pin(async move { $impl_fn(ctx, args).await })
583			}
584		}
585	};
586
587	// One required + two optional: (req: Type1, ?opt1: Type2, ?opt2: Type3) -> ReturnType
588	(
589		$struct_name:ident,
590		$func_name:literal,
591		($arg1_name:ident : $arg1_type:ident, ? $arg2_name:ident : $arg2_type:ident, ? $arg3_name:ident : $arg3_type:ident) -> $ret:ident,
592		$impl_fn:expr
593	) => {
594		#[derive(Debug, Clone, Copy, Default)]
595		pub struct $struct_name;
596
597		impl $crate::exec::function::ScalarFunction for $struct_name {
598			fn name(&self) -> &'static str {
599				$func_name
600			}
601
602			fn signature(&self) -> $crate::exec::function::Signature {
603				$crate::exec::function::Signature::new()
604					.arg(stringify!($arg1_name), $crate::expr::Kind::$arg1_type)
605					.optional(stringify!($arg2_name), $crate::expr::Kind::$arg2_type)
606					.optional(stringify!($arg3_name), $crate::expr::Kind::$arg3_type)
607					.returns($crate::expr::Kind::$ret)
608			}
609
610			fn is_pure(&self) -> bool {
611				false
612			}
613
614			fn is_async(&self) -> bool {
615				true
616			}
617
618			fn invoke(&self, _args: Vec<$crate::val::Value>) -> anyhow::Result<$crate::val::Value> {
619				Err(anyhow::anyhow!("Function '{}' requires async execution", self.name()))
620			}
621
622			fn invoke_async<'a>(
623				&'a self,
624				ctx: &'a $crate::exec::physical_expr::EvalContext<'_>,
625				args: Vec<$crate::val::Value>,
626			) -> $crate::exec::BoxFut<'a, anyhow::Result<$crate::val::Value>> {
627				Box::pin(async move { $impl_fn(ctx, args).await })
628			}
629		}
630	};
631}
632
633// Note: The macros are exported from the crate root via #[macro_export]
634// so they can be used as crate::define_pure_function and crate::register_functions