Skip to main content

toolu_orm_core/
query_column.rs

1//! Column<T> and typed column operations (CommonOps, TextOps, NumericOps).
2
3use std::marker::PhantomData;
4
5use crate::column::{BigInt, Date, Integer, Real, SmallInt, Text, Time, Timestamp, Uuid, Varchar};
6use crate::expr::{Expr, JoinCondition, OrderBy};
7use crate::value::Value;
8
9// ── ColumnRef trait ───────────────────────────────────────────────────────────
10
11pub trait ColumnRef {
12  fn name(&self) -> &'static str;
13  fn table(&self) -> &'static str;
14}
15
16#[derive(Debug, Clone, Copy)]
17pub struct Column<T> {
18  pub table: &'static str,
19  pub name: &'static str,
20  _marker: PhantomData<T>,
21}
22
23impl<T> ColumnRef for Column<T> {
24  fn name(&self) -> &'static str {
25    self.name
26  }
27
28  fn table(&self) -> &'static str {
29    self.table
30  }
31}
32
33impl<T> Column<T> {
34  pub const fn new(table: &'static str, name: &'static str) -> Self {
35    Self {
36      table,
37      name,
38      _marker: PhantomData,
39    }
40  }
41
42  pub fn qualified(&self) -> String {
43    format!(r#""{}"."{}""#, self.table, self.name)
44  }
45
46  pub fn asc(&self) -> OrderBy {
47    OrderBy {
48      column: self.qualified(),
49      direction: "ASC",
50    }
51  }
52
53  pub fn desc(&self) -> OrderBy {
54    OrderBy {
55      column: self.qualified(),
56      direction: "DESC",
57    }
58  }
59
60  pub fn equals<U>(&self, other: &Column<U>) -> JoinCondition {
61    JoinCondition {
62      left: self.qualified(),
63      right: other.qualified(),
64    }
65  }
66}
67
68// ── Traits ───────────────────────────────────────────────────────────────────
69
70pub trait CommonOps {
71  fn eq<V: Into<Value>>(&self, val: V) -> Expr;
72  fn ne<V: Into<Value>>(&self, val: V) -> Expr;
73  fn in_list(&self, values: &[Value]) -> Expr;
74  fn not_in(&self, values: &[Value]) -> Expr;
75  fn is_null(&self) -> Expr;
76  fn is_not_null(&self) -> Expr;
77}
78
79pub trait TextOps {
80  fn like<V: Into<Value>>(&self, val: V) -> Expr;
81}
82
83pub trait NumericOps {
84  fn gt<V: Into<Value>>(&self, val: V) -> Expr;
85  fn lt<V: Into<Value>>(&self, val: V) -> Expr;
86  fn gte<V: Into<Value>>(&self, val: V) -> Expr;
87  fn lte<V: Into<Value>>(&self, val: V) -> Expr;
88  fn between<L: Into<Value>, H: Into<Value>>(&self, low: L, high: H) -> Expr;
89}
90
91// ── Blanket CommonOps impl for all Column<T> ─────────────────────────────────
92
93impl<T> CommonOps for Column<T> {
94  fn eq<V: Into<Value>>(&self, val: V) -> Expr {
95    Expr::comparison(self.qualified(), "=", val.into())
96  }
97
98  fn ne<V: Into<Value>>(&self, val: V) -> Expr {
99    Expr::comparison(self.qualified(), "!=", val.into())
100  }
101
102  fn in_list(&self, values: &[Value]) -> Expr {
103    Expr::in_list(self.qualified(), values.to_vec(), false)
104  }
105
106  fn not_in(&self, values: &[Value]) -> Expr {
107    Expr::in_list(self.qualified(), values.to_vec(), true)
108  }
109
110  fn is_null(&self) -> Expr {
111    Expr::is_null(self.qualified(), false)
112  }
113
114  fn is_not_null(&self) -> Expr {
115    Expr::is_null(self.qualified(), true)
116  }
117}
118
119// ── TextOps impls ─────────────────────────────────────────────────────────────
120
121macro_rules! impl_text_ops {
122  ($($ty:ty),+) => {
123    $(
124      impl TextOps for Column<$ty> {
125        fn like<V: Into<Value>>(&self, val: V) -> Expr {
126          Expr::comparison(self.qualified(), "LIKE", val.into())
127        }
128      }
129    )+
130  };
131}
132
133impl_text_ops!(Text, Uuid, Date, Time);
134
135impl<const N: u32> TextOps for Column<Varchar<N>> {
136  fn like<V: Into<Value>>(&self, val: V) -> Expr {
137    Expr::comparison(self.qualified(), "LIKE", val.into())
138  }
139}
140
141// ── NumericOps impls ──────────────────────────────────────────────────────────
142
143macro_rules! impl_numeric_ops {
144  ($($ty:ty),+) => {
145    $(
146      impl NumericOps for Column<$ty> {
147        fn gt<V: Into<Value>>(&self, val: V) -> Expr {
148          Expr::comparison(self.qualified(), ">", val.into())
149        }
150
151        fn lt<V: Into<Value>>(&self, val: V) -> Expr {
152          Expr::comparison(self.qualified(), "<", val.into())
153        }
154
155        fn gte<V: Into<Value>>(&self, val: V) -> Expr {
156          Expr::comparison(self.qualified(), ">=", val.into())
157        }
158
159        fn lte<V: Into<Value>>(&self, val: V) -> Expr {
160          Expr::comparison(self.qualified(), "<=", val.into())
161        }
162
163        fn between<L: Into<Value>, H: Into<Value>>(&self, low: L, high: H) -> Expr {
164          Expr::between(self.qualified(), low.into(), high.into())
165        }
166      }
167    )+
168  };
169}
170
171impl_numeric_ops!(Integer, Real, BigInt, SmallInt, Timestamp, Date, Time);