Skip to main content

vantage_expressions/expression/
macros.rs

1/// Macro to create expressions with template and parameters for any type T
2/// Syntax:
3/// - expr_as!(T, "template", arg) -> arg becomes Scalar
4/// - expr_as!(T, "template", (expr)) -> expr becomes Nested
5/// - expr_as!(T, "template", {deferred}) -> deferred becomes Deferred
6///
7#[macro_export]
8macro_rules! expr_as {
9    // Simple template without parameters: expr_as!(T, "age")
10    ($t:ty, $template:expr) => {
11        $crate::expression::core::Expression::<$t>::new($template, vec![])
12    };
13
14    // Template with parameters
15    ($t:ty, $template:expr, $($param:tt),*) => {
16        $crate::expression::core::Expression::<$t>::new(
17            $template,
18            vec![
19                $(
20                    $crate::expr_param!($param)
21                ),*
22            ]
23        )
24    };
25}
26
27/// Macro to create expressions where type is inferred from context
28/// Syntax:
29/// - expr_any!("template", arg) -> arg becomes Scalar
30/// - expr_any!("template", (expr)) -> expr becomes Nested
31/// - expr_any!("template", {deferred}) -> deferred becomes Deferred
32///
33/// Function calls like `f(x)` work as scalar parameters without extra wrapping.
34#[macro_export]
35macro_rules! expr_any {
36    // Simple template without parameters: expr_any!("age")
37    ($template:expr) => {
38        $crate::expression::core::Expression::new($template, vec![])
39    };
40
41    // Template with parameters
42    ($template:expr, $($param:tt),*) => {
43        $crate::expression::core::Expression::new(
44            $template,
45            vec![
46                $(
47                    $crate::expr_param!($param)
48                ),*
49            ]
50        )
51    };
52}
53
54/// Macro to create expressions with serde_json::Value as default type
55/// Syntax:
56/// - expr!("template", arg) -> arg becomes Scalar
57/// - expr!("template", (expr)) -> expr becomes Nested
58/// - expr!("template", {deferred}) -> deferred becomes Deferred
59#[macro_export]
60macro_rules! expr {
61    // Simple template without parameters: expr!("age")
62    ($template:expr) => {
63        $crate::expr_as!(serde_json::Value, $template)
64    };
65
66    // Template with parameters
67    ($template:expr, $($param:tt),*) => {
68        $crate::expr_as!(serde_json::Value, $template, $($param),*)
69    };
70}
71
72/// Helper macro to handle different parameter syntaxes.
73///
74/// - `(expr)` — nested expression (calls `.expr()` via `Expressive`)
75/// - `{expr}` — deferred function
76/// - `expr`   — scalar (calls `.into()`)
77///
78/// Function calls like `id_value(id)` work as scalars because `$($rest:tt)*`
79/// captures the entire token sequence before forwarding to the scalar arm.
80#[macro_export]
81macro_rules! expr_param {
82    // Nested expression: (expr) -> ExpressiveEnum::Nested(expr)
83    (($expr:expr)) => {
84        $crate::traits::expressive::ExpressiveEnum::Nested({
85            #[allow(unused_imports)]
86            use $crate::traits::expressive::Expressive;
87            $expr.expr()
88        })
89    };
90
91    // Deferred function: {fn} -> ExpressiveEnum::Deferred(fn)
92    ({$deferred:expr}) => {
93        $deferred.into()
94    };
95
96    // Regular scalar: expr -> ExpressiveEnum::Scalar(expr.into())
97    ($param:expr) => {
98        $crate::traits::expressive::ExpressiveEnum::Scalar($param.into())
99    };
100}
101
102#[cfg(test)]
103mod tests {
104    use crate::Expression;
105
106    #[test]
107    fn test_expr_macro() {
108        let expr = expr!("age > {}", 18);
109        assert_eq!(expr.template, "age > {}");
110        assert_eq!(expr.parameters.len(), 1);
111    }
112
113    #[test]
114    fn test_expr_as_macro() {
115        let expr = expr_as!(i32, "age > {}", 18);
116        assert_eq!(expr.template, "age > {}");
117        assert_eq!(expr.parameters.len(), 1);
118    }
119
120    #[test]
121    fn test_expr_any_macro() {
122        let expr: Expression<i16> = expr_any!("age > {}", 18i16);
123        assert_eq!(expr.template, "age > {}");
124        assert_eq!(expr.parameters.len(), 1);
125    }
126
127    #[test]
128    fn test_nested_expr() {
129        let inner = expr!("status = {}", "active");
130        let outer = expr!("WHERE {} AND age > {}", (inner), "21");
131
132        assert_eq!(outer.template, "WHERE {} AND age > {}");
133        assert_eq!(outer.parameters.len(), 2);
134    }
135
136    #[test]
137    fn test_preview() {
138        let expr = expr_as!(String, "Hello {}", "world");
139        assert_eq!(expr.preview(), "Hello world");
140    }
141}