rustledger_query/
ast.rs

1//! BQL Abstract Syntax Tree types.
2//!
3//! This module defines the AST for Beancount Query Language (BQL),
4//! a SQL-like query language for financial data analysis.
5
6use rust_decimal::Decimal;
7use rustledger_core::NaiveDate;
8
9/// A complete BQL query.
10#[derive(Debug, Clone, PartialEq)]
11pub enum Query {
12    /// SELECT query (boxed to reduce enum size).
13    Select(Box<SelectQuery>),
14    /// JOURNAL shorthand query.
15    Journal(JournalQuery),
16    /// BALANCES shorthand query.
17    Balances(BalancesQuery),
18    /// PRINT shorthand query.
19    Print(PrintQuery),
20}
21
22/// A SELECT query.
23#[derive(Debug, Clone, PartialEq)]
24pub struct SelectQuery {
25    /// Whether DISTINCT was specified.
26    pub distinct: bool,
27    /// Target columns/expressions.
28    pub targets: Vec<Target>,
29    /// FROM clause (transaction-level filtering).
30    pub from: Option<FromClause>,
31    /// WHERE clause (posting-level filtering).
32    pub where_clause: Option<Expr>,
33    /// GROUP BY clause.
34    pub group_by: Option<Vec<Expr>>,
35    /// HAVING clause (filter on aggregated results).
36    pub having: Option<Expr>,
37    /// PIVOT BY clause (pivot table transformation).
38    pub pivot_by: Option<Vec<Expr>>,
39    /// ORDER BY clause.
40    pub order_by: Option<Vec<OrderSpec>>,
41    /// LIMIT clause.
42    pub limit: Option<u64>,
43}
44
45/// A target in the SELECT clause.
46#[derive(Debug, Clone, PartialEq)]
47pub struct Target {
48    /// The expression to select.
49    pub expr: Expr,
50    /// Optional alias (AS name).
51    pub alias: Option<String>,
52}
53
54/// FROM clause with transaction-level modifiers.
55#[derive(Debug, Clone, PartialEq)]
56pub struct FromClause {
57    /// OPEN ON date - summarize entries before this date.
58    pub open_on: Option<NaiveDate>,
59    /// CLOSE ON date - truncate entries after this date.
60    pub close_on: Option<NaiveDate>,
61    /// CLEAR - transfer income/expense to equity.
62    pub clear: bool,
63    /// Filter expression.
64    pub filter: Option<Expr>,
65    /// Subquery (derived table).
66    pub subquery: Option<Box<SelectQuery>>,
67}
68
69/// ORDER BY specification.
70#[derive(Debug, Clone, PartialEq)]
71pub struct OrderSpec {
72    /// Expression to order by.
73    pub expr: Expr,
74    /// Sort direction.
75    pub direction: SortDirection,
76}
77
78/// Sort direction.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
80pub enum SortDirection {
81    /// Ascending (default).
82    #[default]
83    Asc,
84    /// Descending.
85    Desc,
86}
87
88/// JOURNAL shorthand query.
89#[derive(Debug, Clone, PartialEq)]
90pub struct JournalQuery {
91    /// Account pattern to filter by.
92    pub account_pattern: String,
93    /// Optional aggregation function (AT cost, AT units, etc.).
94    pub at_function: Option<String>,
95    /// Optional FROM clause.
96    pub from: Option<FromClause>,
97}
98
99/// BALANCES shorthand query.
100#[derive(Debug, Clone, PartialEq)]
101pub struct BalancesQuery {
102    /// Optional aggregation function.
103    pub at_function: Option<String>,
104    /// Optional FROM clause.
105    pub from: Option<FromClause>,
106}
107
108/// PRINT shorthand query.
109#[derive(Debug, Clone, PartialEq)]
110pub struct PrintQuery {
111    /// Optional FROM clause.
112    pub from: Option<FromClause>,
113}
114
115/// An expression in BQL.
116#[derive(Debug, Clone, PartialEq)]
117pub enum Expr {
118    /// Wildcard (*).
119    Wildcard,
120    /// Column reference.
121    Column(String),
122    /// Literal value.
123    Literal(Literal),
124    /// Function call.
125    Function(FunctionCall),
126    /// Window function call (with OVER clause).
127    Window(WindowFunction),
128    /// Binary operation.
129    BinaryOp(Box<BinaryOp>),
130    /// Unary operation.
131    UnaryOp(Box<UnaryOp>),
132    /// Parenthesized expression.
133    Paren(Box<Self>),
134}
135
136/// A literal value.
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub enum Literal {
139    /// String literal.
140    String(String),
141    /// Numeric literal.
142    Number(Decimal),
143    /// Integer literal.
144    Integer(i64),
145    /// Date literal.
146    Date(NaiveDate),
147    /// Boolean literal.
148    Boolean(bool),
149    /// NULL literal.
150    Null,
151}
152
153/// A function call.
154#[derive(Debug, Clone, PartialEq)]
155pub struct FunctionCall {
156    /// Function name.
157    pub name: String,
158    /// Arguments.
159    pub args: Vec<Expr>,
160}
161
162/// A window function call (function with OVER clause).
163#[derive(Debug, Clone, PartialEq)]
164pub struct WindowFunction {
165    /// Function name (`ROW_NUMBER`, RANK, SUM, etc.).
166    pub name: String,
167    /// Function arguments.
168    pub args: Vec<Expr>,
169    /// Window specification.
170    pub over: WindowSpec,
171}
172
173/// Window specification for OVER clause.
174#[derive(Debug, Clone, PartialEq, Default)]
175pub struct WindowSpec {
176    /// PARTITION BY expressions.
177    pub partition_by: Option<Vec<Expr>>,
178    /// ORDER BY specifications.
179    pub order_by: Option<Vec<OrderSpec>>,
180}
181
182/// A binary operation.
183#[derive(Debug, Clone, PartialEq)]
184pub struct BinaryOp {
185    /// Left operand.
186    pub left: Expr,
187    /// Operator.
188    pub op: BinaryOperator,
189    /// Right operand.
190    pub right: Expr,
191}
192
193/// Binary operators.
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum BinaryOperator {
196    // Comparison
197    /// Equal (=).
198    Eq,
199    /// Not equal (!=).
200    Ne,
201    /// Less than (<).
202    Lt,
203    /// Less than or equal (<=).
204    Le,
205    /// Greater than (>).
206    Gt,
207    /// Greater than or equal (>=).
208    Ge,
209    /// Regular expression match (~).
210    Regex,
211    /// IN operator.
212    In,
213
214    // Logical
215    /// Logical AND.
216    And,
217    /// Logical OR.
218    Or,
219
220    // Arithmetic
221    /// Addition (+).
222    Add,
223    /// Subtraction (-).
224    Sub,
225    /// Multiplication (*).
226    Mul,
227    /// Division (/).
228    Div,
229}
230
231/// A unary operation.
232#[derive(Debug, Clone, PartialEq)]
233pub struct UnaryOp {
234    /// Operator.
235    pub op: UnaryOperator,
236    /// Operand.
237    pub operand: Expr,
238}
239
240/// Unary operators.
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum UnaryOperator {
243    /// Logical NOT.
244    Not,
245    /// Negation (-).
246    Neg,
247}
248
249impl SelectQuery {
250    /// Create a new SELECT query with the given targets.
251    pub const fn new(targets: Vec<Target>) -> Self {
252        Self {
253            distinct: false,
254            targets,
255            from: None,
256            where_clause: None,
257            group_by: None,
258            having: None,
259            pivot_by: None,
260            order_by: None,
261            limit: None,
262        }
263    }
264
265    /// Set the DISTINCT flag.
266    pub const fn distinct(mut self) -> Self {
267        self.distinct = true;
268        self
269    }
270
271    /// Set the FROM clause.
272    pub fn from(mut self, from: FromClause) -> Self {
273        self.from = Some(from);
274        self
275    }
276
277    /// Set the WHERE clause.
278    pub fn where_clause(mut self, expr: Expr) -> Self {
279        self.where_clause = Some(expr);
280        self
281    }
282
283    /// Set the GROUP BY clause.
284    pub fn group_by(mut self, exprs: Vec<Expr>) -> Self {
285        self.group_by = Some(exprs);
286        self
287    }
288
289    /// Set the HAVING clause.
290    pub fn having(mut self, expr: Expr) -> Self {
291        self.having = Some(expr);
292        self
293    }
294
295    /// Set the PIVOT BY clause.
296    pub fn pivot_by(mut self, exprs: Vec<Expr>) -> Self {
297        self.pivot_by = Some(exprs);
298        self
299    }
300
301    /// Set the ORDER BY clause.
302    pub fn order_by(mut self, specs: Vec<OrderSpec>) -> Self {
303        self.order_by = Some(specs);
304        self
305    }
306
307    /// Set the LIMIT.
308    pub const fn limit(mut self, n: u64) -> Self {
309        self.limit = Some(n);
310        self
311    }
312}
313
314impl Target {
315    /// Create a new target from an expression.
316    pub const fn new(expr: Expr) -> Self {
317        Self { expr, alias: None }
318    }
319
320    /// Create a target with an alias.
321    pub fn with_alias(expr: Expr, alias: impl Into<String>) -> Self {
322        Self {
323            expr,
324            alias: Some(alias.into()),
325        }
326    }
327}
328
329impl FromClause {
330    /// Create a new empty FROM clause.
331    pub const fn new() -> Self {
332        Self {
333            open_on: None,
334            close_on: None,
335            clear: false,
336            filter: None,
337            subquery: None,
338        }
339    }
340
341    /// Create a FROM clause from a subquery.
342    pub fn from_subquery(query: SelectQuery) -> Self {
343        Self {
344            open_on: None,
345            close_on: None,
346            clear: false,
347            filter: None,
348            subquery: Some(Box::new(query)),
349        }
350    }
351
352    /// Set the OPEN ON date.
353    pub const fn open_on(mut self, date: NaiveDate) -> Self {
354        self.open_on = Some(date);
355        self
356    }
357
358    /// Set the CLOSE ON date.
359    pub const fn close_on(mut self, date: NaiveDate) -> Self {
360        self.close_on = Some(date);
361        self
362    }
363
364    /// Set the CLEAR flag.
365    pub const fn clear(mut self) -> Self {
366        self.clear = true;
367        self
368    }
369
370    /// Set the filter expression.
371    pub fn filter(mut self, expr: Expr) -> Self {
372        self.filter = Some(expr);
373        self
374    }
375
376    /// Set the subquery.
377    pub fn subquery(mut self, query: SelectQuery) -> Self {
378        self.subquery = Some(Box::new(query));
379        self
380    }
381}
382
383impl Default for FromClause {
384    fn default() -> Self {
385        Self::new()
386    }
387}
388
389impl Expr {
390    /// Create a column reference.
391    pub fn column(name: impl Into<String>) -> Self {
392        Self::Column(name.into())
393    }
394
395    /// Create a string literal.
396    pub fn string(s: impl Into<String>) -> Self {
397        Self::Literal(Literal::String(s.into()))
398    }
399
400    /// Create a number literal.
401    pub const fn number(n: Decimal) -> Self {
402        Self::Literal(Literal::Number(n))
403    }
404
405    /// Create an integer literal.
406    pub const fn integer(n: i64) -> Self {
407        Self::Literal(Literal::Integer(n))
408    }
409
410    /// Create a date literal.
411    pub const fn date(d: NaiveDate) -> Self {
412        Self::Literal(Literal::Date(d))
413    }
414
415    /// Create a boolean literal.
416    pub const fn boolean(b: bool) -> Self {
417        Self::Literal(Literal::Boolean(b))
418    }
419
420    /// Create a NULL literal.
421    pub const fn null() -> Self {
422        Self::Literal(Literal::Null)
423    }
424
425    /// Create a function call.
426    pub fn function(name: impl Into<String>, args: Vec<Self>) -> Self {
427        Self::Function(FunctionCall {
428            name: name.into(),
429            args,
430        })
431    }
432
433    /// Create a binary operation.
434    pub fn binary(left: Self, op: BinaryOperator, right: Self) -> Self {
435        Self::BinaryOp(Box::new(BinaryOp { left, op, right }))
436    }
437
438    /// Create a unary operation.
439    pub fn unary(op: UnaryOperator, operand: Self) -> Self {
440        Self::UnaryOp(Box::new(UnaryOp { op, operand }))
441    }
442}
443
444impl OrderSpec {
445    /// Create an ascending order spec.
446    pub const fn asc(expr: Expr) -> Self {
447        Self {
448            expr,
449            direction: SortDirection::Asc,
450        }
451    }
452
453    /// Create a descending order spec.
454    pub const fn desc(expr: Expr) -> Self {
455        Self {
456            expr,
457            direction: SortDirection::Desc,
458        }
459    }
460}