use crate::model::Model;
use crate::pg::accumulator::SqlAccumulator;
use crate::query::field::FieldRef;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Direction {
Asc,
Desc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum NullsOrder {
Default,
First,
Last,
}
#[derive(Debug, Clone)]
pub enum OrderExpr {
#[non_exhaustive]
Column {
column: &'static str,
direction: Direction,
nulls: NullsOrder,
},
#[cfg(feature = "spatial")]
#[non_exhaustive]
SpatialDistance {
field_column: &'static str,
center: crate::geo::GeoPoint,
pk_column: &'static str,
},
}
impl OrderExpr {
#[must_use = "order expressions are inert until passed to `order_by`"]
pub fn nulls_first(mut self) -> Self {
match self {
OrderExpr::Column { ref mut nulls, .. } => {
*nulls = NullsOrder::First;
}
#[cfg(feature = "spatial")]
OrderExpr::SpatialDistance { .. } => {}
}
self
}
#[must_use = "order expressions are inert until passed to `order_by`"]
pub fn nulls_last(mut self) -> Self {
match self {
OrderExpr::Column { ref mut nulls, .. } => {
*nulls = NullsOrder::Last;
}
#[cfg(feature = "spatial")]
OrderExpr::SpatialDistance { .. } => {}
}
self
}
pub(crate) fn emit(&self, acc: &mut SqlAccumulator, table_qualifier: Option<&'static str>) {
match self {
OrderExpr::Column {
column,
direction,
nulls,
} => {
if let Some(table) = table_qualifier {
acc.push_sql(table);
acc.push_sql(".");
}
acc.push_sql(column);
match direction {
Direction::Asc => acc.push_sql(" ASC"),
Direction::Desc => acc.push_sql(" DESC"),
}
match nulls {
NullsOrder::First => acc.push_sql(" NULLS FIRST"),
NullsOrder::Last => acc.push_sql(" NULLS LAST"),
NullsOrder::Default => {}
}
}
#[cfg(feature = "spatial")]
OrderExpr::SpatialDistance {
field_column,
center,
pk_column,
} => {
acc.push_sql("ST_Distance(");
if let Some(table) = table_qualifier {
acc.push_sql(table);
acc.push_sql(".");
}
acc.push_sql(field_column);
acc.push_sql(", ST_Point(");
acc.push_bind(center.lon);
acc.push_sql(", ");
acc.push_bind(center.lat);
acc.push_sql(")::geography) ASC");
acc.push_sql(", ");
if let Some(table) = table_qualifier {
acc.push_sql(table);
acc.push_sql(".");
}
acc.push_sql(pk_column);
acc.push_sql(" ASC");
}
}
}
}
#[cfg(feature = "spatial")]
impl OrderExpr {
pub(crate) fn spatial_distance_with_pk_tiebreak(
field_column: &'static str,
center: crate::geo::GeoPoint,
pk_column: &'static str,
) -> Self {
OrderExpr::SpatialDistance {
field_column,
center,
pk_column,
}
}
}
impl<M: Model, V> FieldRef<M, V> {
#[must_use = "order expressions are inert until passed to `order_by`"]
pub fn asc(self) -> OrderExpr {
OrderExpr::Column {
column: self.column(),
direction: Direction::Asc,
nulls: NullsOrder::Default,
}
}
#[must_use = "order expressions are inert until passed to `order_by`"]
pub fn desc(self) -> OrderExpr {
OrderExpr::Column {
column: self.column(),
direction: Direction::Desc,
nulls: NullsOrder::Default,
}
}
}
impl From<OrderExpr> for Vec<OrderExpr> {
fn from(o: OrderExpr) -> Self {
vec![o]
}
}
impl OrderExpr {
#[doc(hidden)]
#[must_use]
pub fn __from_macro_column(
column: &'static str,
direction: Direction,
nulls: NullsOrder,
) -> Self {
OrderExpr::Column {
column,
direction,
nulls,
}
}
}