Skip to main content

vantage_sql/primitives/
fx.rs

1use std::fmt::{Debug, Display};
2
3use vantage_core::util::IntoVec;
4use vantage_expressions::{Expression, Expressive, ExpressiveEnum};
5
6/// SQL function call: `NAME(arg1, arg2, ...)`.
7///
8/// The function name is automatically uppercased. Arguments are expressions
9/// that get rendered comma-separated inside the parentheses.
10///
11/// Prefer the [`fx!`] macro for ergonomic construction — it calls `.expr()`
12/// on each argument automatically.
13///
14/// # Examples
15///
16/// ```ignore
17/// // With macro (preferred):
18/// fx!("sum", ident("total").dot_of("o"))
19/// fx!("coalesce", fx!("sum", ident("total")), 0.0f64)
20///
21/// // With struct (when you need IntoVec or programmatic args):
22/// Fx::new("sum", [ident("total").dot_of("o").expr()])
23/// ```
24#[derive(Debug, Clone)]
25pub struct Fx<T: Debug + Display + Clone> {
26    name: String,
27    args: Vec<Expression<T>>,
28}
29
30impl<T: Debug + Display + Clone> Fx<T> {
31    pub fn new(name: impl Into<String>, args: impl IntoVec<Expression<T>>) -> Self {
32        Self {
33            name: name.into().to_uppercase(),
34            args: args.into_vec(),
35        }
36    }
37}
38
39impl<T: Debug + Display + Clone> Expressive<T> for Fx<T> {
40    fn expr(&self) -> Expression<T> {
41        let args_expr = Expression::from_vec(self.args.clone(), ", ");
42        Expression::new(
43            format!("{}({{}})", self.name),
44            vec![ExpressiveEnum::Nested(args_expr)],
45        )
46    }
47}
48
49impl<T: Debug + Display + Clone> From<Fx<T>> for Expression<T> {
50    fn from(fx: Fx<T>) -> Self {
51        fx.expr()
52    }
53}
54
55/// Macro for building SQL function calls with automatic `.expr()` on arguments.
56///
57/// Each argument has `.expr()` called via the `Expressive` trait, so you can
58/// pass `Identifier`, `Column`, `Fx`, scalars, or any `Expressive<T>` directly.
59///
60/// # Examples
61///
62/// ```ignore
63/// use vantage_sql::primitives::*;
64///
65/// // Single argument
66/// fx!("count", sqlite_expr!("*"))
67///
68/// // Multiple arguments
69/// fx!("coalesce", ident("name"), "unnamed")
70///
71/// // Nested function calls
72/// fx!("round", fx!("avg", ident("price")), 2i64)
73/// ```
74#[macro_export]
75macro_rules! fx {
76    ($name:expr, $($arg:expr),+ $(,)?) => {
77        $crate::primitives::fx::Fx::new($name, vec![
78            $({
79                #[allow(unused_imports)]
80                use vantage_expressions::Expressive;
81                ($arg).expr()
82            }),+
83        ])
84    };
85}