1use rust_decimal::Decimal;
7use rustledger_core::NaiveDate;
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum Query {
13 Select(Box<SelectQuery>),
15 Journal(JournalQuery),
17 Balances(BalancesQuery),
19 Print(PrintQuery),
21 CreateTable(CreateTableStmt),
23 Insert(InsertStmt),
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct ColumnDef {
30 pub name: String,
32 pub type_hint: Option<String>,
34}
35
36#[derive(Debug, Clone, PartialEq)]
38pub struct CreateTableStmt {
39 pub table_name: String,
41 pub columns: Vec<ColumnDef>,
43 pub as_select: Option<Box<SelectQuery>>,
45}
46
47#[derive(Debug, Clone, PartialEq)]
49pub struct InsertStmt {
50 pub table_name: String,
52 pub columns: Option<Vec<String>>,
54 pub source: InsertSource,
56}
57
58#[derive(Debug, Clone, PartialEq)]
60pub enum InsertSource {
61 Values(Vec<Vec<Expr>>),
63 Select(Box<SelectQuery>),
65}
66
67#[derive(Debug, Clone, PartialEq)]
69pub struct SelectQuery {
70 pub distinct: bool,
72 pub targets: Vec<Target>,
74 pub from: Option<FromClause>,
76 pub where_clause: Option<Expr>,
78 pub group_by: Option<Vec<Expr>>,
80 pub having: Option<Expr>,
82 pub pivot_by: Option<Vec<Expr>>,
84 pub order_by: Option<Vec<OrderSpec>>,
86 pub limit: Option<u64>,
88}
89
90#[derive(Debug, Clone, PartialEq)]
92pub struct Target {
93 pub expr: Expr,
95 pub alias: Option<String>,
97}
98
99#[derive(Debug, Clone, PartialEq)]
101pub struct FromClause {
102 pub open_on: Option<NaiveDate>,
104 pub close_on: Option<NaiveDate>,
106 pub clear: bool,
108 pub filter: Option<Expr>,
110 pub subquery: Option<Box<SelectQuery>>,
112 pub table_name: Option<String>,
114}
115
116#[derive(Debug, Clone, PartialEq)]
118pub struct OrderSpec {
119 pub expr: Expr,
121 pub direction: SortDirection,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
127pub enum SortDirection {
128 #[default]
130 Asc,
131 Desc,
133}
134
135#[derive(Debug, Clone, PartialEq)]
137pub struct JournalQuery {
138 pub account_pattern: String,
140 pub at_function: Option<String>,
142 pub from: Option<FromClause>,
144}
145
146#[derive(Debug, Clone, PartialEq)]
148pub struct BalancesQuery {
149 pub at_function: Option<String>,
151 pub from: Option<FromClause>,
153}
154
155#[derive(Debug, Clone, PartialEq)]
157pub struct PrintQuery {
158 pub from: Option<FromClause>,
160}
161
162#[derive(Debug, Clone, PartialEq)]
164pub enum Expr {
165 Wildcard,
167 Column(String),
169 Literal(Literal),
171 Function(FunctionCall),
173 Window(WindowFunction),
175 BinaryOp(Box<BinaryOp>),
177 UnaryOp(Box<UnaryOp>),
179 Paren(Box<Self>),
181 Between {
183 value: Box<Self>,
185 low: Box<Self>,
187 high: Box<Self>,
189 },
190 Set(Vec<Self>),
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum Literal {
197 String(String),
199 Number(Decimal),
201 Integer(i64),
203 Date(NaiveDate),
205 Boolean(bool),
207 Null,
209}
210
211#[derive(Debug, Clone, PartialEq)]
213pub struct FunctionCall {
214 pub name: String,
216 pub args: Vec<Expr>,
218}
219
220#[derive(Debug, Clone, PartialEq)]
222pub struct WindowFunction {
223 pub name: String,
225 pub args: Vec<Expr>,
227 pub over: WindowSpec,
229}
230
231#[derive(Debug, Clone, PartialEq, Default)]
233pub struct WindowSpec {
234 pub partition_by: Option<Vec<Expr>>,
236 pub order_by: Option<Vec<OrderSpec>>,
238}
239
240#[derive(Debug, Clone, PartialEq)]
242pub struct BinaryOp {
243 pub left: Expr,
245 pub op: BinaryOperator,
247 pub right: Expr,
249}
250
251#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub enum BinaryOperator {
254 Eq,
257 Ne,
259 Lt,
261 Le,
263 Gt,
265 Ge,
267 Regex,
269 NotRegex,
271 In,
273 NotIn,
275
276 And,
279 Or,
281
282 Add,
285 Sub,
287 Mul,
289 Div,
291 Mod,
293}
294
295#[derive(Debug, Clone, PartialEq)]
297pub struct UnaryOp {
298 pub op: UnaryOperator,
300 pub operand: Expr,
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
306pub enum UnaryOperator {
307 Not,
309 Neg,
311 IsNull,
313 IsNotNull,
315}
316
317impl SelectQuery {
318 pub const fn new(targets: Vec<Target>) -> Self {
320 Self {
321 distinct: false,
322 targets,
323 from: None,
324 where_clause: None,
325 group_by: None,
326 having: None,
327 pivot_by: None,
328 order_by: None,
329 limit: None,
330 }
331 }
332
333 #[must_use]
335 pub const fn distinct(mut self) -> Self {
336 self.distinct = true;
337 self
338 }
339
340 #[must_use]
342 pub fn from(mut self, from: FromClause) -> Self {
343 self.from = Some(from);
344 self
345 }
346
347 #[must_use]
349 pub fn where_clause(mut self, expr: Expr) -> Self {
350 self.where_clause = Some(expr);
351 self
352 }
353
354 #[must_use]
356 pub fn group_by(mut self, exprs: Vec<Expr>) -> Self {
357 self.group_by = Some(exprs);
358 self
359 }
360
361 #[must_use]
363 pub fn having(mut self, expr: Expr) -> Self {
364 self.having = Some(expr);
365 self
366 }
367
368 #[must_use]
370 pub fn pivot_by(mut self, exprs: Vec<Expr>) -> Self {
371 self.pivot_by = Some(exprs);
372 self
373 }
374
375 #[must_use]
377 pub fn order_by(mut self, specs: Vec<OrderSpec>) -> Self {
378 self.order_by = Some(specs);
379 self
380 }
381
382 #[must_use]
384 pub const fn limit(mut self, n: u64) -> Self {
385 self.limit = Some(n);
386 self
387 }
388}
389
390impl Target {
391 pub const fn new(expr: Expr) -> Self {
393 Self { expr, alias: None }
394 }
395
396 pub fn with_alias(expr: Expr, alias: impl Into<String>) -> Self {
398 Self {
399 expr,
400 alias: Some(alias.into()),
401 }
402 }
403}
404
405impl FromClause {
406 pub const fn new() -> Self {
408 Self {
409 open_on: None,
410 close_on: None,
411 clear: false,
412 filter: None,
413 subquery: None,
414 table_name: None,
415 }
416 }
417
418 pub fn from_subquery(query: SelectQuery) -> Self {
420 Self {
421 open_on: None,
422 close_on: None,
423 clear: false,
424 filter: None,
425 subquery: Some(Box::new(query)),
426 table_name: None,
427 }
428 }
429
430 pub fn from_table(name: impl Into<String>) -> Self {
432 Self {
433 open_on: None,
434 close_on: None,
435 clear: false,
436 filter: None,
437 subquery: None,
438 table_name: Some(name.into()),
439 }
440 }
441
442 pub const fn open_on(mut self, date: NaiveDate) -> Self {
444 self.open_on = Some(date);
445 self
446 }
447
448 pub const fn close_on(mut self, date: NaiveDate) -> Self {
450 self.close_on = Some(date);
451 self
452 }
453
454 pub const fn clear(mut self) -> Self {
456 self.clear = true;
457 self
458 }
459
460 pub fn filter(mut self, expr: Expr) -> Self {
462 self.filter = Some(expr);
463 self
464 }
465
466 pub fn subquery(mut self, query: SelectQuery) -> Self {
468 self.subquery = Some(Box::new(query));
469 self
470 }
471}
472
473impl Default for FromClause {
474 fn default() -> Self {
475 Self::new()
476 }
477}
478
479impl Expr {
480 pub fn column(name: impl Into<String>) -> Self {
482 Self::Column(name.into())
483 }
484
485 pub fn string(s: impl Into<String>) -> Self {
487 Self::Literal(Literal::String(s.into()))
488 }
489
490 pub const fn number(n: Decimal) -> Self {
492 Self::Literal(Literal::Number(n))
493 }
494
495 pub const fn integer(n: i64) -> Self {
497 Self::Literal(Literal::Integer(n))
498 }
499
500 pub const fn date(d: NaiveDate) -> Self {
502 Self::Literal(Literal::Date(d))
503 }
504
505 pub const fn boolean(b: bool) -> Self {
507 Self::Literal(Literal::Boolean(b))
508 }
509
510 pub const fn null() -> Self {
512 Self::Literal(Literal::Null)
513 }
514
515 pub fn function(name: impl Into<String>, args: Vec<Self>) -> Self {
517 Self::Function(FunctionCall {
518 name: name.into(),
519 args,
520 })
521 }
522
523 pub fn binary(left: Self, op: BinaryOperator, right: Self) -> Self {
525 Self::BinaryOp(Box::new(BinaryOp { left, op, right }))
526 }
527
528 pub fn unary(op: UnaryOperator, operand: Self) -> Self {
530 Self::UnaryOp(Box::new(UnaryOp { op, operand }))
531 }
532
533 pub fn between(value: Self, low: Self, high: Self) -> Self {
535 Self::Between {
536 value: Box::new(value),
537 low: Box::new(low),
538 high: Box::new(high),
539 }
540 }
541}
542
543impl OrderSpec {
544 pub const fn asc(expr: Expr) -> Self {
546 Self {
547 expr,
548 direction: SortDirection::Asc,
549 }
550 }
551
552 pub const fn desc(expr: Expr) -> Self {
554 Self {
555 expr,
556 direction: SortDirection::Desc,
557 }
558 }
559}
560
561impl fmt::Display for Expr {
562 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563 match self {
564 Self::Wildcard => write!(f, "*"),
565 Self::Column(name) => write!(f, "{name}"),
566 Self::Literal(lit) => write!(f, "{lit}"),
567 Self::Function(func) => {
568 write!(f, "{}(", func.name)?;
569 for (i, arg) in func.args.iter().enumerate() {
570 if i > 0 {
571 write!(f, ", ")?;
572 }
573 write!(f, "{arg}")?;
574 }
575 write!(f, ")")
576 }
577 Self::Window(wf) => {
578 write!(f, "{}(", wf.name)?;
579 for (i, arg) in wf.args.iter().enumerate() {
580 if i > 0 {
581 write!(f, ", ")?;
582 }
583 write!(f, "{arg}")?;
584 }
585 write!(f, ") OVER ()")
586 }
587 Self::BinaryOp(op) => write!(f, "({} {} {})", op.left, op.op, op.right),
588 Self::UnaryOp(op) => {
589 match op.op {
591 UnaryOperator::IsNull => write!(f, "{} IS NULL", op.operand),
592 UnaryOperator::IsNotNull => write!(f, "{} IS NOT NULL", op.operand),
593 _ => write!(f, "{}{}", op.op, op.operand),
594 }
595 }
596 Self::Paren(inner) => write!(f, "({inner})"),
597 Self::Between { value, low, high } => {
598 write!(f, "{value} BETWEEN {low} AND {high}")
599 }
600 Self::Set(elements) => {
601 write!(f, "(")?;
602 for (i, elem) in elements.iter().enumerate() {
603 if i > 0 {
604 write!(f, ", ")?;
605 }
606 write!(f, "{elem}")?;
607 }
608 write!(f, ")")
609 }
610 }
611 }
612}
613
614impl fmt::Display for Literal {
615 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616 match self {
617 Self::String(s) => write!(f, "\"{s}\""),
618 Self::Number(n) => write!(f, "{n}"),
619 Self::Integer(n) => write!(f, "{n}"),
620 Self::Date(d) => write!(f, "{d}"),
621 Self::Boolean(b) => write!(f, "{b}"),
622 Self::Null => write!(f, "NULL"),
623 }
624 }
625}
626
627impl fmt::Display for BinaryOperator {
628 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
629 let s = match self {
630 Self::Eq => "=",
631 Self::Ne => "!=",
632 Self::Lt => "<",
633 Self::Le => "<=",
634 Self::Gt => ">",
635 Self::Ge => ">=",
636 Self::Regex => "~",
637 Self::NotRegex => "!~",
638 Self::In => "IN",
639 Self::NotIn => "NOT IN",
640 Self::And => "AND",
641 Self::Or => "OR",
642 Self::Add => "+",
643 Self::Sub => "-",
644 Self::Mul => "*",
645 Self::Div => "/",
646 Self::Mod => "%",
647 };
648 write!(f, "{s}")
649 }
650}
651
652impl fmt::Display for UnaryOperator {
653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654 let s = match self {
655 Self::Not => "NOT ",
656 Self::Neg => "-",
657 Self::IsNull => " IS NULL",
658 Self::IsNotNull => " IS NOT NULL",
659 };
660 write!(f, "{s}")
661 }
662}
663
664#[cfg(test)]
665mod tests {
666 use super::*;
667 use rust_decimal_macros::dec;
668
669 #[test]
670 fn test_expr_display_wildcard() {
671 assert_eq!(Expr::Wildcard.to_string(), "*");
672 }
673
674 #[test]
675 fn test_expr_display_column() {
676 assert_eq!(Expr::Column("account".to_string()).to_string(), "account");
677 }
678
679 #[test]
680 fn test_expr_display_literals() {
681 assert_eq!(Expr::string("hello").to_string(), "\"hello\"");
682 assert_eq!(Expr::integer(42).to_string(), "42");
683 assert_eq!(Expr::number(dec!(3.14)).to_string(), "3.14");
684 assert_eq!(Expr::boolean(true).to_string(), "true");
685 assert_eq!(Expr::null().to_string(), "NULL");
686 }
687
688 #[test]
689 fn test_expr_display_date() {
690 let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
691 assert_eq!(Expr::date(date).to_string(), "2024-01-15");
692 }
693
694 #[test]
695 fn test_expr_display_function_no_args() {
696 let func = Expr::function("now", vec![]);
697 assert_eq!(func.to_string(), "now()");
698 }
699
700 #[test]
701 fn test_expr_display_function_one_arg() {
702 let func = Expr::function("account_sortkey", vec![Expr::column("account")]);
703 assert_eq!(func.to_string(), "account_sortkey(account)");
704 }
705
706 #[test]
707 fn test_expr_display_function_multiple_args() {
708 let func = Expr::function(
709 "coalesce",
710 vec![Expr::column("a"), Expr::column("b"), Expr::integer(0)],
711 );
712 assert_eq!(func.to_string(), "coalesce(a, b, 0)");
713 }
714
715 #[test]
716 fn test_expr_display_window() {
717 let wf = Expr::Window(WindowFunction {
718 name: "row_number".to_string(),
719 args: vec![],
720 over: WindowSpec::default(),
721 });
722 assert_eq!(wf.to_string(), "row_number() OVER ()");
723 }
724
725 #[test]
726 fn test_expr_display_window_with_args() {
727 let wf = Expr::Window(WindowFunction {
728 name: "sum".to_string(),
729 args: vec![Expr::column("amount")],
730 over: WindowSpec::default(),
731 });
732 assert_eq!(wf.to_string(), "sum(amount) OVER ()");
733 }
734
735 #[test]
736 fn test_expr_display_binary_op() {
737 let expr = Expr::binary(Expr::column("a"), BinaryOperator::Add, Expr::integer(1));
738 assert_eq!(expr.to_string(), "(a + 1)");
739 }
740
741 #[test]
742 fn test_expr_display_unary_not() {
743 let expr = Expr::unary(UnaryOperator::Not, Expr::column("flag"));
744 assert_eq!(expr.to_string(), "NOT flag");
745 }
746
747 #[test]
748 fn test_expr_display_unary_neg() {
749 let expr = Expr::unary(UnaryOperator::Neg, Expr::column("x"));
750 assert_eq!(expr.to_string(), "-x");
751 }
752
753 #[test]
754 fn test_expr_display_is_null() {
755 let expr = Expr::unary(UnaryOperator::IsNull, Expr::column("x"));
756 assert_eq!(expr.to_string(), "x IS NULL");
757 }
758
759 #[test]
760 fn test_expr_display_is_not_null() {
761 let expr = Expr::unary(UnaryOperator::IsNotNull, Expr::column("x"));
762 assert_eq!(expr.to_string(), "x IS NOT NULL");
763 }
764
765 #[test]
766 fn test_expr_display_paren() {
767 let inner = Expr::binary(Expr::column("a"), BinaryOperator::Add, Expr::column("b"));
768 let expr = Expr::Paren(Box::new(inner));
769 assert_eq!(expr.to_string(), "((a + b))");
770 }
771
772 #[test]
773 fn test_expr_display_between() {
774 let expr = Expr::between(Expr::column("x"), Expr::integer(1), Expr::integer(10));
775 assert_eq!(expr.to_string(), "x BETWEEN 1 AND 10");
776 }
777
778 #[test]
779 fn test_expr_display_set() {
780 let single = Expr::Set(vec![Expr::string("EUR")]);
782 assert_eq!(single.to_string(), r#"("EUR")"#);
783
784 let multi = Expr::Set(vec![
786 Expr::string("EUR"),
787 Expr::string("USD"),
788 Expr::string("GBP"),
789 ]);
790 assert_eq!(multi.to_string(), r#"("EUR", "USD", "GBP")"#);
791
792 let numeric = Expr::Set(vec![Expr::integer(2023), Expr::integer(2024)]);
794 assert_eq!(numeric.to_string(), "(2023, 2024)");
795 }
796
797 #[test]
798 fn test_binary_operator_display() {
799 assert_eq!(BinaryOperator::Eq.to_string(), "=");
800 assert_eq!(BinaryOperator::Ne.to_string(), "!=");
801 assert_eq!(BinaryOperator::Lt.to_string(), "<");
802 assert_eq!(BinaryOperator::Le.to_string(), "<=");
803 assert_eq!(BinaryOperator::Gt.to_string(), ">");
804 assert_eq!(BinaryOperator::Ge.to_string(), ">=");
805 assert_eq!(BinaryOperator::Regex.to_string(), "~");
806 assert_eq!(BinaryOperator::NotRegex.to_string(), "!~");
807 assert_eq!(BinaryOperator::In.to_string(), "IN");
808 assert_eq!(BinaryOperator::NotIn.to_string(), "NOT IN");
809 assert_eq!(BinaryOperator::And.to_string(), "AND");
810 assert_eq!(BinaryOperator::Or.to_string(), "OR");
811 assert_eq!(BinaryOperator::Add.to_string(), "+");
812 assert_eq!(BinaryOperator::Sub.to_string(), "-");
813 assert_eq!(BinaryOperator::Mul.to_string(), "*");
814 assert_eq!(BinaryOperator::Div.to_string(), "/");
815 assert_eq!(BinaryOperator::Mod.to_string(), "%");
816 }
817
818 #[test]
819 fn test_unary_operator_display() {
820 assert_eq!(UnaryOperator::Not.to_string(), "NOT ");
821 assert_eq!(UnaryOperator::Neg.to_string(), "-");
822 assert_eq!(UnaryOperator::IsNull.to_string(), " IS NULL");
823 assert_eq!(UnaryOperator::IsNotNull.to_string(), " IS NOT NULL");
824 }
825
826 #[test]
827 fn test_literal_display() {
828 assert_eq!(Literal::String("test".to_string()).to_string(), "\"test\"");
829 assert_eq!(Literal::Number(dec!(1.5)).to_string(), "1.5");
830 assert_eq!(Literal::Integer(42).to_string(), "42");
831 assert_eq!(Literal::Boolean(false).to_string(), "false");
832 assert_eq!(Literal::Null.to_string(), "NULL");
833 }
834}