Skip to main content

vantage_sql/primitives/select/
window.rs

1use std::fmt::{Debug, Display};
2
3use vantage_expressions::{Expression, Expressive, ExpressiveEnum};
4
5/// SQL window specification for window functions.
6///
7/// # Examples
8///
9/// ```ignore
10/// let win = Window::new()
11///     .partition_by(ident("department_id").dot_of("u"))
12///     .order_by(ident("salary").dot_of("u"), Order::Desc);
13///
14/// // Inline: SUM(u.salary) OVER (PARTITION BY ... ORDER BY ... DESC)
15/// win.apply(Fx::new("sum", [ident("salary").dot_of("u").expr()]))
16///
17/// // Named reference: ROW_NUMBER() OVER win
18/// Window::named("win").apply(Fx::new("row_number", []))
19/// ```
20#[derive(Debug, Clone)]
21pub struct Window<T: Debug + Display + Clone> {
22    partition_by: Vec<Expression<T>>,
23    order_by: Vec<(Expression<T>, vantage_expressions::Order)>,
24    frame: Option<String>,
25    named_ref: Option<String>,
26}
27
28impl<T: Debug + Display + Clone> Default for Window<T> {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl<T: Debug + Display + Clone> Window<T> {
35    pub fn new() -> Self {
36        Self {
37            partition_by: Vec::new(),
38            order_by: Vec::new(),
39            frame: None,
40            named_ref: None,
41        }
42    }
43
44    /// Reference a named window defined in WINDOW clause.
45    pub fn named(name: impl Into<String>) -> Self {
46        Self {
47            partition_by: Vec::new(),
48            order_by: Vec::new(),
49            frame: None,
50            named_ref: Some(name.into()),
51        }
52    }
53
54    pub fn partition_by(mut self, expr: impl Expressive<T>) -> Self {
55        self.partition_by.push(expr.expr());
56        self
57    }
58
59    pub fn order_by(mut self, expr: impl Expressive<T>, order: vantage_expressions::Order) -> Self {
60        self.order_by.push((expr.expr(), order));
61        self
62    }
63
64    pub fn rows(mut self, from: &str, to: &str) -> Self {
65        self.frame = Some(format!("ROWS BETWEEN {} AND {}", from, to));
66        self
67    }
68
69    pub fn range(mut self, from: &str, to: &str) -> Self {
70        self.frame = Some(format!("RANGE BETWEEN {} AND {}", from, to));
71        self
72    }
73
74    /// Apply a function over this window: `expr OVER (spec)` or `expr OVER name`.
75    pub fn apply(&self, func: impl Expressive<T>) -> Expression<T> {
76        Expression::new(
77            "{} OVER {}",
78            vec![
79                ExpressiveEnum::Nested(func.expr()),
80                ExpressiveEnum::Nested(self.spec_expr()),
81            ],
82        )
83    }
84
85    /// Render the window spec — either a named reference or inline `(...)`.
86    fn spec_expr(&self) -> Expression<T> {
87        if let Some(name) = &self.named_ref {
88            return Expression::new(name.clone(), vec![]);
89        }
90
91        let mut parts: Vec<Expression<T>> = Vec::new();
92
93        if !self.partition_by.is_empty() {
94            parts.push(Expression::new(
95                "PARTITION BY {}",
96                vec![ExpressiveEnum::Nested(Expression::from_vec(
97                    self.partition_by.clone(),
98                    ", ",
99                ))],
100            ));
101        }
102
103        if !self.order_by.is_empty() {
104            let order_parts: Vec<Expression<T>> = self
105                .order_by
106                .iter()
107                .map(|(expr, order)| {
108                    Expression::new(
109                        format!("{{}}{}", order.suffix()),
110                        vec![ExpressiveEnum::Nested(expr.clone())],
111                    )
112                })
113                .collect();
114            parts.push(Expression::new(
115                "ORDER BY {}",
116                vec![ExpressiveEnum::Nested(Expression::from_vec(
117                    order_parts,
118                    ", ",
119                ))],
120            ));
121        }
122
123        if let Some(frame) = &self.frame {
124            parts.push(Expression::new(frame.clone(), vec![]));
125        }
126
127        let inner = Expression::from_vec(parts, " ");
128        Expression::new("({})", vec![ExpressiveEnum::Nested(inner)])
129    }
130
131    /// Render just the definition part (for WINDOW clause): `(PARTITION BY ... ORDER BY ...)`
132    pub fn definition(&self) -> Expression<T> {
133        self.spec_expr()
134    }
135}