Skip to main content

a3s_orm/
function.rs

1use std::marker::PhantomData;
2
3use crate::expression::{BinaryOperator, Expression, SelectSubquery, Selection, SelectionExt};
4use crate::query::SelectQuery;
5use crate::schema::Table;
6use crate::value::IntoSqlValue;
7use crate::Column;
8
9#[derive(Clone, Debug)]
10pub struct TypedExpression<V> {
11    expression: Expression,
12    marker: PhantomData<fn() -> V>,
13}
14
15impl<V> TypedExpression<V> {
16    pub(crate) fn new(expression: Expression) -> Self {
17        Self {
18            expression,
19            marker: PhantomData,
20        }
21    }
22
23    pub fn eq(self, value: impl IntoSqlValue<V>) -> Expression {
24        self.compare(BinaryOperator::Eq, value)
25    }
26
27    pub fn ne(self, value: impl IntoSqlValue<V>) -> Expression {
28        self.compare(BinaryOperator::NotEq, value)
29    }
30
31    pub fn gt(self, value: impl IntoSqlValue<V>) -> Expression {
32        self.compare(BinaryOperator::GreaterThan, value)
33    }
34
35    pub fn gte(self, value: impl IntoSqlValue<V>) -> Expression {
36        self.compare(BinaryOperator::GreaterThanOrEq, value)
37    }
38
39    pub fn lt(self, value: impl IntoSqlValue<V>) -> Expression {
40        self.compare(BinaryOperator::LessThan, value)
41    }
42
43    pub fn lte(self, value: impl IntoSqlValue<V>) -> Expression {
44        self.compare(BinaryOperator::LessThanOrEq, value)
45    }
46
47    pub fn over(self) -> crate::WindowExpression<V> {
48        crate::WindowExpression::new(self.expression)
49    }
50
51    /// Consume this typed expression for composition in another expression.
52    pub fn expression(self) -> Expression {
53        self.expression
54    }
55
56    fn compare(self, operator: BinaryOperator, value: impl IntoSqlValue<V>) -> Expression {
57        Expression::Binary {
58            left: Box::new(self.expression),
59            operator,
60            right: Box::new(Expression::Value(value.into_sql_value())),
61        }
62    }
63}
64
65impl<V> Selection for TypedExpression<V> {
66    type Output = V;
67
68    fn expressions(self) -> Vec<Expression> {
69        vec![self.expression]
70    }
71}
72
73impl<V> SelectionExt for TypedExpression<V> {}
74
75pub fn count<T, V>(column: Column<T, V>) -> TypedExpression<i64> {
76    sql_function("count", vec![column.expression()])
77}
78
79pub fn count_all() -> TypedExpression<i64> {
80    sql_function("count", vec![Expression::Wildcard])
81}
82
83pub fn min<T, V>(column: Column<T, V>) -> TypedExpression<V> {
84    sql_function("min", vec![column.expression()])
85}
86
87pub fn max<T, V>(column: Column<T, V>) -> TypedExpression<V> {
88    sql_function("max", vec![column.expression()])
89}
90
91/// Build a typed bound-value expression.
92pub fn bound<V>(value: impl IntoSqlValue<V>) -> TypedExpression<V> {
93    TypedExpression::new(Expression::Value(value.into_sql_value()))
94}
95
96/// Call a scalar SQL function with a caller-declared result type.
97///
98/// The function name is validated as an identifier and every runtime value in
99/// its arguments remains a bound parameter.
100pub fn sql_function<V>(
101    name: &'static str,
102    arguments: impl IntoIterator<Item = Expression>,
103) -> TypedExpression<V> {
104    TypedExpression::new(Expression::Function {
105        name,
106        arguments: arguments.into_iter().collect(),
107    })
108}
109
110/// Build a typed SQL `COALESCE` expression.
111pub fn coalesce<V>(arguments: impl IntoIterator<Item = Expression>) -> TypedExpression<V> {
112    TypedExpression::new(Expression::Coalesce(arguments.into_iter().collect()))
113}
114
115/// Build a typed SQL `LEAST` expression.
116pub fn least<V>(arguments: impl IntoIterator<Item = Expression>) -> TypedExpression<V> {
117    TypedExpression::new(Expression::Least(arguments.into_iter().collect()))
118}
119
120/// Cast a typed expression to a validated SQL type name.
121///
122/// When a driver has no native codec for the target type, first cast a bound
123/// value to its source SQL type and then cast that expression to the target.
124pub fn cast<From, To>(
125    expression: TypedExpression<From>,
126    sql_type: &'static str,
127) -> TypedExpression<To> {
128    TypedExpression::new(Expression::Cast {
129        expression: Box::new(expression.expression()),
130        sql_type,
131    })
132}
133
134/// Embed a typed single-column SELECT as a scalar expression.
135///
136/// The compiler validates that custom selections still emit exactly one SQL
137/// expression.
138pub fn scalar_subquery<Source: Table, V>(query: SelectQuery<Source, V>) -> TypedExpression<V> {
139    TypedExpression::new(Expression::Subquery(SelectSubquery(Box::new(
140        query.into_node(),
141    ))))
142}