surrealpatch/lib/src/sql/order.rs

283 lines
5.8 KiB
Rust

use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::error::IResult;
use crate::sql::fmt::Fmt;
use crate::sql::idiom::{basic, Idiom};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::{map, opt};
use nom::multi::separated_list1;
use nom::sequence::tuple;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::Deref;
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Orders(pub Vec<Order>);
impl Deref for Orders {
type Target = Vec<Order>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl IntoIterator for Orders {
type Item = Order;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl fmt::Display for Orders {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ORDER BY {}", Fmt::comma_separated(&self.0))
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Order {
pub order: Idiom,
pub random: bool,
pub collate: bool,
pub numeric: bool,
pub direction: bool,
}
impl Deref for Order {
type Target = Idiom;
fn deref(&self) -> &Self::Target {
&self.order
}
}
impl fmt::Display for Order {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.order)?;
if self.random {
write!(f, "RAND()")?;
}
if self.collate {
write!(f, " COLLATE")?;
}
if self.numeric {
write!(f, " NUMERIC")?;
}
match self.direction {
false => write!(f, " DESC")?,
true => (),
};
Ok(())
}
}
pub fn order(i: &str) -> IResult<&str, Orders> {
let (i, _) = tag_no_case("ORDER")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("BY"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = alt((order_rand, separated_list1(commas, order_raw)))(i)?;
Ok((i, Orders(v)))
}
fn order_rand(i: &str) -> IResult<&str, Vec<Order>> {
let (i, _) = tag_no_case("RAND()")(i)?;
Ok((
i,
vec![Order {
order: Default::default(),
random: true,
collate: false,
numeric: false,
direction: true,
}],
))
}
fn order_raw(i: &str) -> IResult<&str, Order> {
let (i, v) = basic(i)?;
let (i, c) = opt(tuple((shouldbespace, tag_no_case("COLLATE"))))(i)?;
let (i, n) = opt(tuple((shouldbespace, tag_no_case("NUMERIC"))))(i)?;
let (i, d) = opt(alt((
map(tuple((shouldbespace, tag_no_case("ASC"))), |_| true),
map(tuple((shouldbespace, tag_no_case("DESC"))), |_| false),
)))(i)?;
Ok((
i,
Order {
order: v,
random: false,
collate: c.is_some(),
numeric: n.is_some(),
direction: d.unwrap_or(true),
},
))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::test::Parse;
#[test]
fn order_statement() {
let sql = "ORDER field";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![Order {
order: Idiom::parse("field"),
random: false,
collate: false,
numeric: false,
direction: true,
}])
);
assert_eq!("ORDER BY field", format!("{}", out));
}
#[test]
fn order_statement_by() {
let sql = "ORDER BY field";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![Order {
order: Idiom::parse("field"),
random: false,
collate: false,
numeric: false,
direction: true,
}])
);
assert_eq!("ORDER BY field", format!("{}", out));
}
#[test]
fn order_statement_random() {
let sql = "ORDER RAND()";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![Order {
order: Default::default(),
random: true,
collate: false,
numeric: false,
direction: true,
}])
);
assert_eq!("ORDER BY RAND()", format!("{}", out));
}
#[test]
fn order_statement_multiple() {
let sql = "ORDER field, other.field";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![
Order {
order: Idiom::parse("field"),
random: false,
collate: false,
numeric: false,
direction: true,
},
Order {
order: Idiom::parse("other.field"),
random: false,
collate: false,
numeric: false,
direction: true,
},
])
);
assert_eq!("ORDER BY field, other.field", format!("{}", out));
}
#[test]
fn order_statement_collate() {
let sql = "ORDER field COLLATE";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![Order {
order: Idiom::parse("field"),
random: false,
collate: true,
numeric: false,
direction: true,
}])
);
assert_eq!("ORDER BY field COLLATE", format!("{}", out));
}
#[test]
fn order_statement_numeric() {
let sql = "ORDER field NUMERIC";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![Order {
order: Idiom::parse("field"),
random: false,
collate: false,
numeric: true,
direction: true,
}])
);
assert_eq!("ORDER BY field NUMERIC", format!("{}", out));
}
#[test]
fn order_statement_direction() {
let sql = "ORDER field DESC";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![Order {
order: Idiom::parse("field"),
random: false,
collate: false,
numeric: false,
direction: false,
}])
);
assert_eq!("ORDER BY field DESC", format!("{}", out));
}
#[test]
fn order_statement_all() {
let sql = "ORDER field COLLATE NUMERIC DESC";
let res = order(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
out,
Orders(vec![Order {
order: Idiom::parse("field"),
random: false,
collate: true,
numeric: true,
direction: false,
}])
);
assert_eq!("ORDER BY field COLLATE NUMERIC DESC", format!("{}", out));
}
}