sql-orm-query 0.2.0-rc.1

Query AST and builder primitives for sql-orm.
Documentation
use crate::expr::{Expr, TableRef};
use crate::join::Join;
use crate::order::SortDirection;
use crate::pagination::Pagination;
use crate::predicate::Predicate;
use sql_orm_core::{Entity, EntityColumn};

#[derive(Debug, Clone, PartialEq)]
pub enum AggregateExpr {
    GroupKey(Expr),
    CountAll,
    Count(Expr),
    Sum(Expr),
    Avg(Expr),
    Min(Expr),
    Max(Expr),
}

impl AggregateExpr {
    pub fn group_key(expr: impl Into<Expr>) -> Self {
        Self::GroupKey(expr.into())
    }

    pub const fn count_all() -> Self {
        Self::CountAll
    }

    pub fn count(expr: impl Into<Expr>) -> Self {
        Self::Count(expr.into())
    }

    pub fn sum(expr: impl Into<Expr>) -> Self {
        Self::Sum(expr.into())
    }

    pub fn avg(expr: impl Into<Expr>) -> Self {
        Self::Avg(expr.into())
    }

    pub fn min(expr: impl Into<Expr>) -> Self {
        Self::Min(expr.into())
    }

    pub fn max(expr: impl Into<Expr>) -> Self {
        Self::Max(expr.into())
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct AggregateProjection {
    pub expr: AggregateExpr,
    pub alias: &'static str,
}

impl AggregateProjection {
    pub fn group_key<E: Entity>(column: EntityColumn<E>) -> Self {
        let alias = column.column_name();
        Self {
            expr: AggregateExpr::GroupKey(Expr::from(column)),
            alias,
        }
    }

    pub fn group_key_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
        Self {
            expr: AggregateExpr::GroupKey(expr.into()),
            alias,
        }
    }

    pub const fn expr_as(expr: AggregateExpr, alias: &'static str) -> Self {
        Self { expr, alias }
    }

    pub fn count_as(alias: &'static str) -> Self {
        Self::expr_as(AggregateExpr::CountAll, alias)
    }

    pub fn sum_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
        Self::expr_as(AggregateExpr::sum(expr), alias)
    }

    pub fn avg_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
        Self::expr_as(AggregateExpr::avg(expr), alias)
    }

    pub fn min_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
        Self::expr_as(AggregateExpr::min(expr), alias)
    }

    pub fn max_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
        Self::expr_as(AggregateExpr::max(expr), alias)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum AggregatePredicate {
    Eq(AggregateExpr, Expr),
    Ne(AggregateExpr, Expr),
    Gt(AggregateExpr, Expr),
    Gte(AggregateExpr, Expr),
    Lt(AggregateExpr, Expr),
    Lte(AggregateExpr, Expr),
    And(Vec<AggregatePredicate>),
    Or(Vec<AggregatePredicate>),
    Not(Box<AggregatePredicate>),
}

impl AggregatePredicate {
    pub fn eq(left: AggregateExpr, right: impl Into<Expr>) -> Self {
        Self::Eq(left, right.into())
    }

    pub fn ne(left: AggregateExpr, right: impl Into<Expr>) -> Self {
        Self::Ne(left, right.into())
    }

    pub fn gt(left: AggregateExpr, right: impl Into<Expr>) -> Self {
        Self::Gt(left, right.into())
    }

    pub fn gte(left: AggregateExpr, right: impl Into<Expr>) -> Self {
        Self::Gte(left, right.into())
    }

    pub fn lt(left: AggregateExpr, right: impl Into<Expr>) -> Self {
        Self::Lt(left, right.into())
    }

    pub fn lte(left: AggregateExpr, right: impl Into<Expr>) -> Self {
        Self::Lte(left, right.into())
    }

    pub fn and(predicates: Vec<AggregatePredicate>) -> Self {
        Self::And(predicates)
    }

    pub fn or(predicates: Vec<AggregatePredicate>) -> Self {
        Self::Or(predicates)
    }

    pub fn negate(predicate: AggregatePredicate) -> Self {
        Self::Not(Box::new(predicate))
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct AggregateOrderBy {
    pub expr: AggregateExpr,
    pub direction: SortDirection,
}

impl AggregateOrderBy {
    pub fn new(expr: AggregateExpr, direction: SortDirection) -> Self {
        Self { expr, direction }
    }

    pub fn asc(expr: AggregateExpr) -> Self {
        Self::new(expr, SortDirection::Asc)
    }

    pub fn desc(expr: AggregateExpr) -> Self {
        Self::new(expr, SortDirection::Desc)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct AggregateQuery {
    pub from: TableRef,
    pub joins: Vec<Join>,
    pub projection: Vec<AggregateProjection>,
    pub predicate: Option<Predicate>,
    pub group_by: Vec<Expr>,
    pub having: Option<AggregatePredicate>,
    pub order_by: Vec<AggregateOrderBy>,
    pub pagination: Option<Pagination>,
}

impl AggregateQuery {
    pub fn from_entity<E: Entity>() -> Self {
        Self {
            from: TableRef::for_entity::<E>(),
            joins: Vec::new(),
            projection: Vec::new(),
            predicate: None,
            group_by: Vec::new(),
            having: None,
            order_by: Vec::new(),
            pagination: None,
        }
    }

    pub fn from_entity_as<E: Entity>(alias: &'static str) -> Self {
        Self {
            from: TableRef::for_entity_as::<E>(alias),
            joins: Vec::new(),
            projection: Vec::new(),
            predicate: None,
            group_by: Vec::new(),
            having: None,
            order_by: Vec::new(),
            pagination: None,
        }
    }

    pub fn project<P, I>(mut self, projection: I) -> Self
    where
        P: Into<AggregateProjection>,
        I: IntoIterator<Item = P>,
    {
        self.projection = projection.into_iter().map(Into::into).collect();
        self
    }

    pub fn group_by<G, I>(mut self, group_by: I) -> Self
    where
        G: Into<Expr>,
        I: IntoIterator<Item = G>,
    {
        self.group_by = group_by.into_iter().map(Into::into).collect();
        self
    }

    pub fn filter(mut self, predicate: Predicate) -> Self {
        self.predicate = Some(match self.predicate.take() {
            Some(existing) => Predicate::and(vec![existing, predicate]),
            None => predicate,
        });
        self
    }

    pub fn join(mut self, join: Join) -> Self {
        self.joins.push(join);
        self
    }

    pub fn inner_join<E: Entity>(self, on: Predicate) -> Self {
        self.join(Join::inner_entity::<E>(on))
    }

    pub fn left_join<E: Entity>(self, on: Predicate) -> Self {
        self.join(Join::left_entity::<E>(on))
    }

    pub fn inner_join_as<E: Entity>(self, alias: &'static str, on: Predicate) -> Self {
        self.join(Join::inner_entity_as::<E>(alias, on))
    }

    pub fn left_join_as<E: Entity>(self, alias: &'static str, on: Predicate) -> Self {
        self.join(Join::left_entity_as::<E>(alias, on))
    }

    pub fn having(mut self, predicate: AggregatePredicate) -> Self {
        self.having = Some(match self.having.take() {
            Some(existing) => AggregatePredicate::and(vec![existing, predicate]),
            None => predicate,
        });
        self
    }

    pub fn order_by(mut self, order: AggregateOrderBy) -> Self {
        self.order_by.push(order);
        self
    }

    pub fn paginate(mut self, pagination: Pagination) -> Self {
        self.pagination = Some(pagination);
        self
    }
}