Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 58 additions & 37 deletions src/grammar/clause/order_by.rs
Original file line number Diff line number Diff line change
@@ -1,54 +1,68 @@
//! The `ORDER BY` clause.

use Result;
use grammar::definition::Column;
use grammar::{Buffer, Clause, Expression};

/// An `ORDER BY` clause.
#[derive(Debug, Default)]
pub struct OrderBy(Vec<Box<Expression>>);

/// An order.
use crate::grammar::definition::Column;
use crate::grammar::{Buffer, Clause, Expression};
use crate::Result;

/// Represents an `ORDER BY` clause in an SQL statement.
///
/// This struct holds a list of expressions, each of which will be compiled
/// to SQL and joined by commas in the final clause.
#[derive(Debug, Default, Clone)]
pub struct OrderBy(Vec<Box<dyn Expression>>);

/// Specifies the direction of ordering in an `ORDER BY` clause.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Order {
/// The ascending order.
/// Ascending order (ASC).
Ascending,
/// The descending order.
/// Descending order (DESC).
Descending,
}

/// An object that can be ordered by.
pub trait Orderable where Self: Sized {
/// The type produced after setting an order.
/// A trait for types that can be ordered in an `ORDER BY` clause.
///
/// Allows specifying the order direction (ascending or descending)
/// for columns, strings, or other custom expressions.
pub trait Orderable: Sized {
/// The type produced when setting an order.
type Output;

/// Set the order.
fn order(self, Option<Order>) -> Self::Output;
/// Set the order explicitly.
fn order(self, order: Option<Order>) -> Self::Output;

/// Set the ascending order.
/// Set ascending order.
fn ascend(self) -> Self::Output {
self.order(Some(Order::Ascending))
}

/// Set the descending order.
/// Set descending order.
fn descend(self) -> Self::Output {
self.order(Some(Order::Descending))
}
}

impl OrderBy {
#[doc(hidden)]
pub fn append<T>(mut self, expression: T) -> Self where T: Expression + 'static {
/// Create a new, empty `OrderBy` clause.
pub fn new() -> Self {
Self(Vec::new())
}

/// Append an expression to the `OrderBy` clause.
/// This uses a builder pattern for convenient chaining.
pub fn append<T>(&mut self, expression: T) -> &mut Self
where
T: Expression + 'static,
{
self.0.push(Box::new(expression));
self
}
}

impl Clause for OrderBy {
/// Compile the `OrderBy` clause into an SQL string.
fn compile(&self) -> Result<String> {
let mut buffer = Buffer::new();
for expression in &self.0 {
buffer.push(try!(expression.compile()));
buffer.push(expression.compile()?);
}
Ok(format!("ORDER BY {}", buffer.join(", ")))
}
Expand All @@ -57,40 +71,45 @@ impl Clause for OrderBy {
impl Orderable for Column {
type Output = (Column, Option<Order>);

#[inline]
fn order(self, order: Option<Order>) -> Self::Output {
(self, order)
}
}

impl<'l> Orderable for &'l str {
// Implement Orderable for any type that can be converted into a String.
impl<T: Into<String>> Orderable for T {
type Output = (String, Option<Order>);

#[inline]
fn order(self, order: Option<Order>) -> Self::Output {
(self.to_string(), order)
(self.into(), order)
}
}

impl<T: Expression> Expression for (T, Option<Order>) {
fn compile(&self) -> Result<String> {
let main = try!(self.0.compile());
let main = self.0.compile()?;
Ok(match self.1 {
Some(Order::Ascending) => format!("{} ASC", main),
Some(Order::Descending) => format!("{} DESC", main),
_ => main,
None => main,
})
}
}

#[cfg(test)]
mod tests {
use grammar::Clause;
use prelude::*;

macro_rules! new(
($first:expr) => (super::OrderBy::default().append($first));
);
use super::*;
use crate::grammar::Clause;
use crate::prelude::*;

// Macro for initializing an OrderBy with a first element, using the new builder pattern.
macro_rules! new {
($first:expr) => {{
let mut ob = OrderBy::default();
ob.append($first);
ob
}};
}

#[test]
fn from_column() {
Expand Down Expand Up @@ -118,8 +137,10 @@ mod tests {

#[test]
fn append() {
let clause = new!("foo").append(column("bar").ascend())
.append("baz".to_string().descend());
let mut clause = new!("foo");
clause
.append(column("bar").ascend())
.append("baz".descend());

assert_eq!(clause.compile().unwrap(), "ORDER BY foo, `bar` ASC, baz DESC");
}
Expand Down