use super::simple_expr::{CaseStatement, Keyword, SimpleExpr};
use crate::types::{ColumnRef, DynIden, IntoColumnRef, IntoIden, WindowStatement};
use crate::value::{IntoValue, Value};
#[derive(Debug, Clone)]
pub struct Expr(SimpleExpr);
impl Expr {
pub fn col<C>(col: C) -> Self
where
C: IntoColumnRef,
{
Self(SimpleExpr::Column(col.into_column_ref()))
}
pub fn tbl<T, C>(table: T, col: C) -> Self
where
T: IntoIden,
C: IntoIden,
{
Self(SimpleExpr::TableColumn(table.into_iden(), col.into_iden()))
}
pub fn val<V>(val: V) -> Self
where
V: IntoValue,
{
Self(SimpleExpr::Value(val.into_value()))
}
pub fn cust<S>(sql: S) -> Self
where
S: Into<String>,
{
Self(SimpleExpr::Custom(sql.into()))
}
pub fn cust_with_values<S, I, V>(sql: S, values: I) -> Self
where
S: Into<String>,
I: IntoIterator<Item = V>,
V: Into<SimpleExpr>,
{
Self(SimpleExpr::CustomWithExpr(
sql.into(),
values.into_iter().map(|v| v.into()).collect(),
))
}
pub fn tuple<I>(exprs: I) -> Self
where
I: IntoIterator<Item = Self>,
{
Self(SimpleExpr::Tuple(
exprs.into_iter().map(|e| e.into_simple_expr()).collect(),
))
}
pub fn asterisk() -> Self {
Self(SimpleExpr::Asterisk)
}
pub fn subquery(select: crate::query::SelectStatement) -> Self {
Self(SimpleExpr::SubQuery(None, Box::new(select)))
}
pub fn exists(select: crate::query::SelectStatement) -> Self {
Self(SimpleExpr::SubQuery(
Some(super::simple_expr::SubQueryOper::Exists),
Box::new(select),
))
}
pub fn not_exists(select: crate::query::SelectStatement) -> Self {
Self(SimpleExpr::SubQuery(
Some(super::simple_expr::SubQueryOper::NotExists),
Box::new(select),
))
}
pub fn in_subquery(self, select: crate::query::SelectStatement) -> Self {
Self(SimpleExpr::Binary(
Box::new(self.0),
crate::types::BinOper::In,
Box::new(SimpleExpr::SubQuery(None, Box::new(select))),
))
}
pub fn not_in_subquery(self, select: crate::query::SelectStatement) -> Self {
Self(SimpleExpr::Binary(
Box::new(self.0),
crate::types::BinOper::NotIn,
Box::new(SimpleExpr::SubQuery(None, Box::new(select))),
))
}
pub fn case() -> CaseExprBuilder {
CaseExprBuilder {
case: CaseStatement::new(),
}
}
pub fn value<V>(val: V) -> Self
where
V: IntoValue,
{
Self::val(val)
}
pub fn expr_col(col: DynIden) -> Self {
Self(SimpleExpr::Column(ColumnRef::Column(col)))
}
pub fn null() -> Self {
Self(SimpleExpr::Constant(Keyword::Null))
}
pub fn constant_true() -> Self {
Self(SimpleExpr::Constant(Keyword::True))
}
pub fn constant_false() -> Self {
Self(SimpleExpr::Constant(Keyword::False))
}
#[allow(clippy::should_implement_trait)]
pub fn default() -> Self {
Self(SimpleExpr::Constant(Keyword::Default))
}
pub fn current_timestamp() -> Self {
Self(SimpleExpr::Constant(Keyword::CurrentTimestamp))
}
pub fn current_date() -> Self {
Self(SimpleExpr::Constant(Keyword::CurrentDate))
}
pub fn current_time() -> Self {
Self(SimpleExpr::Constant(Keyword::CurrentTime))
}
#[must_use]
pub fn over(self, window: WindowStatement) -> SimpleExpr {
SimpleExpr::Window {
func: Box::new(self.0),
window,
}
}
#[must_use]
pub fn over_named<T: IntoIden>(self, name: T) -> SimpleExpr {
SimpleExpr::WindowNamed {
func: Box::new(self.0),
name: name.into_iden(),
}
}
pub fn row_number() -> Self {
Self(SimpleExpr::FunctionCall(
"ROW_NUMBER".into_iden(),
Vec::new(),
))
}
pub fn rank() -> Self {
Self(SimpleExpr::FunctionCall("RANK".into_iden(), Vec::new()))
}
pub fn dense_rank() -> Self {
Self(SimpleExpr::FunctionCall(
"DENSE_RANK".into_iden(),
Vec::new(),
))
}
pub fn ntile(buckets: i64) -> Self {
Self(SimpleExpr::FunctionCall(
"NTILE".into_iden(),
vec![SimpleExpr::Value(Value::BigInt(Some(buckets)))],
))
}
pub fn lead(expr: SimpleExpr, offset: Option<i64>, default: Option<Value>) -> Self {
let mut args = vec![expr];
if let Some(off) = offset {
args.push(SimpleExpr::Value(Value::BigInt(Some(off))));
if let Some(def) = default {
args.push(SimpleExpr::Value(def));
}
}
Self(SimpleExpr::FunctionCall("LEAD".into_iden(), args))
}
pub fn lag(expr: SimpleExpr, offset: Option<i64>, default: Option<Value>) -> Self {
let mut args = vec![expr];
if let Some(off) = offset {
args.push(SimpleExpr::Value(Value::BigInt(Some(off))));
if let Some(def) = default {
args.push(SimpleExpr::Value(def));
}
}
Self(SimpleExpr::FunctionCall("LAG".into_iden(), args))
}
pub fn first_value(expr: SimpleExpr) -> Self {
Self(SimpleExpr::FunctionCall(
"FIRST_VALUE".into_iden(),
vec![expr],
))
}
pub fn last_value(expr: SimpleExpr) -> Self {
Self(SimpleExpr::FunctionCall(
"LAST_VALUE".into_iden(),
vec![expr],
))
}
pub fn nth_value(expr: SimpleExpr, n: i64) -> Self {
Self(SimpleExpr::FunctionCall(
"NTH_VALUE".into_iden(),
vec![expr, SimpleExpr::Value(Value::BigInt(Some(n)))],
))
}
#[must_use]
pub fn into_simple_expr(self) -> SimpleExpr {
self.0
}
#[must_use]
pub fn as_simple_expr(&self) -> &SimpleExpr {
&self.0
}
pub fn binary<R>(self, op: crate::types::BinOper, right: R) -> SimpleExpr
where
R: Into<SimpleExpr>,
{
SimpleExpr::Binary(Box::new(self.0), op, Box::new(right.into()))
}
pub fn equals<C>(self, col: C) -> SimpleExpr
where
C: IntoColumnRef,
{
SimpleExpr::Binary(
Box::new(self.0),
crate::types::BinOper::Equal,
Box::new(SimpleExpr::Column(col.into_column_ref())),
)
}
#[must_use]
pub fn expr_as<T: IntoIden>(self, alias: T) -> SimpleExpr {
SimpleExpr::ExprAlias(Box::new(self.0), alias.into_iden())
}
}
impl From<Expr> for SimpleExpr {
fn from(e: Expr) -> Self {
e.0
}
}
impl From<SimpleExpr> for Expr {
fn from(e: SimpleExpr) -> Self {
Self(e)
}
}
impl From<&str> for Expr {
fn from(s: &str) -> Self {
Expr::val(s)
}
}
impl crate::value::IntoValue for Expr {
fn into_value(self) -> crate::value::Value {
match self.0 {
SimpleExpr::Value(v) => v,
_ => panic!("Cannot convert non-value Expr to Value"),
}
}
}
#[derive(Debug, Clone)]
pub struct CaseExprBuilder {
case: CaseStatement,
}
impl CaseExprBuilder {
#[must_use]
pub fn when<C, R>(mut self, condition: C, result: R) -> Self
where
C: Into<SimpleExpr>,
R: Into<SimpleExpr>,
{
self.case = self.case.when(condition, result);
self
}
#[must_use]
pub fn else_result<E>(mut self, result: E) -> Expr
where
E: Into<SimpleExpr>,
{
self.case = self.case.else_result(result);
Expr(SimpleExpr::Case(Box::new(self.case)))
}
#[must_use]
pub fn build(self) -> Expr {
Expr(SimpleExpr::Case(Box::new(self.case)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::Value;
use rstest::rstest;
#[rstest]
fn test_expr_col() {
let expr = Expr::col("name");
assert!(matches!(expr.0, SimpleExpr::Column(_)));
}
#[rstest]
fn test_expr_tbl() {
let expr = Expr::tbl("users", "name");
assert!(matches!(expr.0, SimpleExpr::TableColumn(_, _)));
}
#[rstest]
fn test_expr_val() {
let expr = Expr::val(42);
assert!(matches!(expr.0, SimpleExpr::Value(Value::Int(Some(42)))));
}
#[rstest]
fn test_expr_cust() {
let expr = Expr::cust("NOW()");
if let SimpleExpr::Custom(s) = expr.0 {
assert_eq!(s, "NOW()");
} else {
panic!("Expected Custom variant");
}
}
#[rstest]
fn test_expr_cust_with_values() {
let expr = Expr::cust_with_values("? + ?", [1i32, 2i32]);
assert!(matches!(expr.0, SimpleExpr::CustomWithExpr(_, _)));
}
#[rstest]
fn test_expr_tuple() {
let expr = Expr::tuple([Expr::val(1), Expr::val(2), Expr::val(3)]);
if let SimpleExpr::Tuple(v) = expr.0 {
assert_eq!(v.len(), 3);
} else {
panic!("Expected Tuple variant");
}
}
#[rstest]
fn test_expr_asterisk() {
let expr = Expr::asterisk();
assert!(matches!(expr.0, SimpleExpr::Asterisk));
}
#[rstest]
fn test_expr_null() {
let expr = Expr::null();
assert!(matches!(expr.0, SimpleExpr::Constant(Keyword::Null)));
}
#[rstest]
fn test_expr_current_timestamp() {
let expr = Expr::current_timestamp();
assert!(matches!(
expr.0,
SimpleExpr::Constant(Keyword::CurrentTimestamp)
));
}
#[rstest]
fn test_case_expr_builder() {
let expr = Expr::case()
.when(true, 1i32)
.when(false, 0i32)
.else_result(-1i32);
assert!(matches!(expr.0, SimpleExpr::Case(_)));
}
#[rstest]
fn test_case_expr_without_else() {
let expr = Expr::case().when(true, 1i32).build();
assert!(matches!(expr.0, SimpleExpr::Case(_)));
}
#[rstest]
fn test_expr_into_simple_expr() {
let expr = Expr::val(42);
let simple = expr.into_simple_expr();
assert!(matches!(simple, SimpleExpr::Value(Value::Int(Some(42)))));
}
}