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}