use crate::{
Result,
ast::{ast::AstSort, parse::Parser},
error::{OperationKind, RqlError},
token::{
keyword::Keyword,
operator::Operator::{CloseCurly, Colon, OpenCurly},
separator::Separator::Comma,
},
};
impl<'bump> Parser<'bump> {
pub(crate) fn parse_sort(&mut self) -> Result<AstSort<'bump>> {
let start = self.current()?.fragment.offset();
let token = self.consume_keyword(Keyword::Sort)?;
if self.is_eof() || !self.current()?.is_operator(OpenCurly) {
return Err(RqlError::OperatorMissingBraces {
kind: OperationKind::Sort,
fragment: token.fragment.to_owned(),
}
.into());
}
self.advance()?;
let mut columns = Vec::new();
let mut directions = Vec::new();
if !self.is_eof() && self.current()?.is_operator(CloseCurly) {
self.advance()?; return Ok(AstSort {
token,
columns,
directions,
rql: self.source_since(start),
});
}
loop {
columns.push(self.parse_column_identifier()?);
if !self.is_eof()
&& !self.current()?.is_separator(Comma)
&& !self.current()?.is_operator(CloseCurly)
{
if self.current()?.is_operator(Colon) {
self.advance()?; if self.current()?.is_keyword(Keyword::Asc)
|| self.current()?.is_keyword(Keyword::Desc)
{
let token = self.current()?;
self.advance()?;
directions.push(Some(token.fragment));
} else {
directions.push(None);
}
} else {
directions.push(None);
}
} else {
directions.push(None);
}
if self.is_eof() {
break;
}
if self.current()?.is_operator(CloseCurly) {
self.advance()?; break;
}
if self.current()?.is_separator(Comma) {
self.advance()?;
} else {
break;
}
}
Ok(AstSort {
token,
columns,
directions,
rql: self.source_since(start),
})
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::{bump::Bump, token::tokenize};
#[test]
fn test_single_column() {
let bump = Bump::new();
let source = "SORT {name}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 1);
assert_eq!(sort.directions.len(), 1);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref(), None);
}
#[test]
fn test_keyword() {
let bump = Bump::new();
let source = "SORT {value: ASC}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 1);
assert_eq!(sort.directions.len(), 1);
assert_eq!(sort.columns[0].name.text(), "value");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "ASC");
}
#[test]
fn test_single_column_asc() {
let bump = Bump::new();
let source = "SORT {name: ASC}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 1);
assert_eq!(sort.directions.len(), 1);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "ASC");
}
#[test]
fn test_single_column_desc() {
let bump = Bump::new();
let source = "SORT {name: DESC}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 1);
assert_eq!(sort.directions.len(), 1);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "DESC");
}
#[test]
fn test_multiple_columns() {
let bump = Bump::new();
let source = "SORT {name, age}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 2);
assert_eq!(sort.directions.len(), 2);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref(), None);
assert_eq!(sort.columns[1].name.text(), "age");
assert_eq!(sort.directions[1].as_ref(), None);
}
#[test]
fn test_multiple_columns_asc_desc() {
let bump = Bump::new();
let source = "SORT {name: ASC, age: DESC}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 2);
assert_eq!(sort.directions.len(), 2);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "ASC");
assert_eq!(sort.columns[1].name.text(), "age");
assert_eq!(sort.directions[1].as_ref().unwrap().text(), "DESC");
}
#[test]
fn test_empty_braces() {
let bump = Bump::new();
let source = "SORT {}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 0);
assert_eq!(sort.directions.len(), 0);
}
#[test]
fn test_without_braces_fails() {
let bump = Bump::new();
let source = "SORT name";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let result = parser.parse();
assert!(result.is_err(), "Expected error for SORT without braces");
let err = result.unwrap_err();
assert_eq!(err.code, "SORT_001");
}
#[test]
fn test_space_syntax_rejected() {
let bump = Bump::new();
let source = "SORT {name DESC}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let result = parser.parse();
assert!(result.is_err(), "Expected error for space-separated sort direction");
}
#[test]
fn test_colon_syntax_asc() {
let bump = Bump::new();
let source = "SORT {name: asc}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 1);
assert_eq!(sort.directions.len(), 1);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "asc");
}
#[test]
fn test_colon_syntax_desc() {
let bump = Bump::new();
let source = "SORT {name: desc}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 1);
assert_eq!(sort.directions.len(), 1);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "desc");
}
#[test]
fn test_colon_syntax_multiple_columns() {
let bump = Bump::new();
let source = "SORT {name: asc, age: desc}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 2);
assert_eq!(sort.directions.len(), 2);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "asc");
assert_eq!(sort.columns[1].name.text(), "age");
assert_eq!(sort.directions[1].as_ref().unwrap().text(), "desc");
}
#[test]
fn test_colon_syntax_mixed() {
let bump = Bump::new();
let source = "SORT {name: asc, age}";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
let result = result.pop().unwrap();
let sort = result.first_unchecked().as_sort();
assert_eq!(sort.columns.len(), 2);
assert_eq!(sort.directions.len(), 2);
assert_eq!(sort.columns[0].name.text(), "name");
assert_eq!(sort.directions[0].as_ref().unwrap().text(), "asc");
assert_eq!(sort.columns[1].name.text(), "age");
assert_eq!(sort.directions[1].as_ref(), None);
}
}