Skip to main content

linkrs_query/executor/expression/functions/builtin/
macros.rs

1//! Module for defining built-in function macros
2//!
3//! Macros for reducing boilerplate in function enumeration and execution.
4
5/// Macro for defining an enumeration of built-in functions
6///
7/// Auto-generate name(), arity(), is_variadic(), description(), execute()
8#[macro_export]
9macro_rules! define_function_enum {
10    (
11        $(#[$meta:meta])*
12        $vis:vis enum $name:ident {
13            $(
14                $(#[$variant_meta:meta])*
15                $variant:ident => {
16                    name: $func_name:literal,
17                    arity: $arity:expr,
18                    variadic: $variadic:expr,
19                    description: $desc:literal,
20                    handler: $handler:expr
21                }
22            ),* $(,)?
23        }
24    ) => {
25        $(#[$meta])*
26        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
27        $vis enum $name {
28            $(
29                $(#[$variant_meta])*
30                $variant,
31            )*
32        }
33
34        impl $name {
35            /// Obtain the function name
36            $vis fn name(&self) -> &str {
37                match self {
38                    $(Self::$variant => $func_name,)*
39                }
40            }
41
42            /// Determine the number of parameters
43            $vis fn arity(&self) -> usize {
44                match self {
45                    $(Self::$variant => $arity,)*
46                }
47            }
48
49            /// Is it a function with variable parameters?
50            $vis fn is_variadic(&self) -> bool {
51                match self {
52                    $(Self::$variant => $variadic,)*
53                }
54            }
55
56            /// Obtain the function description
57            $vis fn description(&self) -> &str {
58                match self {
59                    $(Self::$variant => $desc,)*
60                }
61            }
62
63            /// Execute the function
64            $vis fn execute(&self, args: &[linkrs_core::Value]) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
65                if !self.is_variadic() && args.len() != self.arity() {
66                    return Err($crate::executor::expression::ExpressionError::invalid_arity(
67                        self.name(),
68                        self.arity(),
69                        args.len(),
70                    ));
71                }
72                let handler: fn(&[linkrs_core::Value]) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> = match self {
73                    $(Self::$variant => $handler,)*
74                };
75                handler(args)
76            }
77        }
78    };
79}
80
81/// Define a single-parameter numerical function that returns a Float value.
82#[macro_export]
83macro_rules! define_unary_float_fn {
84    ($name:ident, $op:expr, $desc:literal) => {
85        fn $name(
86            args: &[linkrs_core::Value],
87        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
88            let op = $op;
89            match &args[0] {
90                Value::SmallInt(i) => Ok(Value::Float(op(*i as f32))),
91                Value::Int(i) => Ok(Value::Float(op(*i as f32))),
92                Value::BigInt(i) => Ok(Value::Double(op(*i as f32) as f64)),
93                Value::Float(f) => Ok(Value::Float(op(*f))),
94                Value::Double(f) => Ok(Value::Double(op(*f as f32) as f64)),
95                Value::Null(_) => Ok(Value::Null(NullType::Null)),
96                _ => Err($crate::executor::expression::ExpressionError::type_error(
97                    concat!($desc, "Functions require numeric types"),
98                )),
99            }
100        }
101    };
102}
103
104/// Define a single-parameter integer/float function (while preserving the data type)
105#[macro_export]
106macro_rules! define_unary_numeric_fn {
107    ($name:ident, int: $int_op:expr, float: $float_op:expr, $desc:literal) => {
108        fn $name(
109            args: &[linkrs_core::Value],
110        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
111            match &args[0] {
112                Value::SmallInt(i) => $int_op(*i as i32),
113                Value::Int(i) => $int_op(*i),
114                Value::BigInt(i) => $int_op(*i as i32),
115                Value::Float(f) => $float_op(*f),
116                Value::Double(f) => $float_op(*f as f32),
117                Value::Null(_) => Ok(Value::Null(NullType::Null)),
118                _ => Err($crate::executor::expression::ExpressionError::type_error(
119                    concat!($desc, "Functions require numeric types"),
120                )),
121            }
122        }
123    };
124}
125
126/// Define a single-parameter string function
127#[macro_export]
128macro_rules! define_unary_string_fn {
129    ($name:ident, $op:expr, $desc:literal) => {
130        fn $name(
131            args: &[linkrs_core::Value],
132        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
133            let op = $op;
134            match &args[0] {
135                Value::String(s) => Ok(Value::string(op(s))),
136                Value::Null(_) => Ok(Value::Null(NullType::Null)),
137                _ => Err($crate::executor::expression::ExpressionError::type_error(
138                    concat!($desc, "Functions require a string type"),
139                )),
140            }
141        }
142    };
143}
144
145/// Define a function for extracting date and time fields
146#[macro_export]
147macro_rules! define_datetime_extractor {
148    ($name:ident, Date => $date_field:ident, DateTime => $datetime_field:ident) => {
149        fn $name(
150            args: &[linkrs_core::Value],
151        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
152            match &args[0] {
153                Value::Date(d) => Ok(Value::BigInt(d.$date_field as i64)),
154                Value::DateTime(dt) => Ok(Value::BigInt(dt.$datetime_field as i64)),
155                Value::Null(_) => Ok(Value::Null(NullType::Null)),
156                _ => Err($crate::executor::expression::ExpressionError::type_error(
157                    concat!(
158                        stringify!($name),
159                        "Functions require a date or datetime type"
160                    ),
161                )),
162            }
163        }
164    };
165    ($name:ident, Time => $time_field:ident, DateTime => $datetime_field:ident) => {
166        fn $name(
167            args: &[linkrs_core::Value],
168        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
169            match &args[0] {
170                Value::Time(t) => Ok(Value::BigInt(t.$time_field as i64)),
171                Value::DateTime(dt) => Ok(Value::BigInt(dt.$datetime_field as i64)),
172                Value::Null(_) => Ok(Value::Null(NullType::Null)),
173                _ => Err($crate::executor::expression::ExpressionError::type_error(
174                    concat!(
175                        stringify!($name),
176                        "Functions require a time or datetime type"
177                    ),
178                )),
179            }
180        }
181    };
182}
183
184/// Define a binary numeric operation function
185#[macro_export]
186macro_rules! define_binary_numeric_fn {
187    ($name:ident, $op:expr, $desc:literal) => {
188        fn $name(
189            args: &[linkrs_core::Value],
190        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
191            let op = $op;
192            match (&args[0], &args[1]) {
193                (Value::SmallInt(a), Value::SmallInt(b)) => op(*a as f32, *b as f32),
194                (Value::Int(a), Value::Int(b)) => op(*a as f32, *b as f32),
195                (Value::BigInt(a), Value::BigInt(b)) => op(*a as f32, *b as f32),
196                (Value::SmallInt(a), Value::Int(b)) => op(*a as f32, *b as f32),
197                (Value::Int(a), Value::SmallInt(b)) => op(*a as f32, *b as f32),
198                (Value::Float(a), Value::Float(b)) => op(*a, *b),
199                (Value::Double(a), Value::Double(b)) => op(*a as f32, *b as f32),
200                (Value::Float(a), Value::Double(b)) => op(*a, *b as f32),
201                (Value::Double(a), Value::Float(b)) => op(*a as f32, *b),
202                (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
203                _ => Err($crate::executor::expression::ExpressionError::type_error(
204                    concat!($desc, "Functions require numeric types"),
205                )),
206            }
207        }
208    };
209}
210
211/// Define a binary string comparison function
212#[macro_export]
213macro_rules! define_binary_string_bool_fn {
214    ($name:ident, $op:expr, $desc:literal) => {
215        fn $name(
216            args: &[linkrs_core::Value],
217        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
218            let op = $op;
219            match (&args[0], &args[1]) {
220                (Value::String(a), Value::String(b)) => Ok(Value::Bool(op(a, b))),
221                (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
222                _ => Err($crate::executor::expression::ExpressionError::type_error(
223                    concat!($desc, "Functions require a string type"),
224                )),
225            }
226        }
227    };
228}
229
230/// Define a geospatial binary function
231#[macro_export]
232macro_rules! define_binary_geography_fn {
233    ($name:ident, $op:expr, $desc:literal) => {
234        fn $name(
235            args: &[linkrs_core::Value],
236        ) -> Result<linkrs_core::Value, $crate::executor::expression::ExpressionError> {
237            let op = $op;
238            match (&args[0], &args[1]) {
239                (Value::Geography(geo1), Value::Geography(geo2)) => op(geo1, geo2),
240                (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
241                _ => Err($crate::executor::expression::ExpressionError::type_error(
242                    concat!($desc, "Functions require geo-typed parameters"),
243                )),
244            }
245        }
246    };
247}