surrealpatch/lib/src/sql/order.rs

287 lines
5.8 KiB
Rust
Raw Normal View History

2020-06-29 15:36:01 +00:00
use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
2022-01-16 20:31:50 +00:00
use crate::sql::error::IResult;
use crate::sql::idiom::{basic, Idiom};
2020-06-29 15:36:01 +00:00
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::{map, opt};
2021-03-29 15:43:37 +00:00
use nom::multi::separated_list1;
2020-06-29 15:36:01 +00:00
use nom::sequence::tuple;
use serde::{Deserialize, Serialize};
use std::fmt;
2022-03-25 18:43:22 +00:00
use std::ops::Deref;
2020-06-29 15:36:01 +00:00
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Orders(pub Vec<Order>);
2020-06-29 15:36:01 +00:00
2022-03-25 18:43:22 +00:00
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()
}
}
2020-06-29 15:36:01 +00:00
impl fmt::Display for Orders {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ORDER BY {}",
self.0.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", ")
)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
2020-06-29 15:36:01 +00:00
pub struct Order {
pub order: Idiom,
pub random: bool,
pub collate: bool,
pub numeric: bool,
pub direction: bool,
}
2022-05-05 11:27:08 +00:00
impl Deref for Order {
type Target = Idiom;
fn deref(&self) -> &Self::Target {
&self.order
}
}
2020-06-29 15:36:01 +00:00
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)?;
2021-03-29 15:43:37 +00:00
let (i, v) = alt((order_rand, separated_list1(commas, order_raw)))(i)?;
2020-06-29 15:36:01 +00:00
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)?;
2020-06-29 15:36:01 +00:00
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;
2020-06-29 15:36:01 +00:00
#[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"),
2020-06-29 15:36:01 +00:00
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"),
2020-06-29 15:36:01 +00:00
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"),
2020-06-29 15:36:01 +00:00
random: false,
collate: false,
numeric: false,
direction: true,
},
Order {
order: Idiom::parse("other.field"),
2020-06-29 15:36:01 +00:00
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"),
2020-06-29 15:36:01 +00:00
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"),
2020-06-29 15:36:01 +00:00
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"),
2020-06-29 15:36:01 +00:00
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"),
2020-06-29 15:36:01 +00:00
random: false,
collate: true,
numeric: true,
direction: false,
}])
);
assert_eq!("ORDER BY field COLLATE NUMERIC DESC", format!("{}", out));
}
}