use super::AstLowerer;
use crate::ast::{CountStmt, FacetStmt, OrderDirection, ScrollOrderBy, ScrollStmt, Stmt};
use crate::error::QqlError;
use crate::token::TokenKind;
use alloc::boxed::Box;
impl<'a> AstLowerer<'a> {
pub fn parse_scroll(&mut self) -> Result<Stmt, QqlError> {
self.expect(TokenKind::Scroll)?;
self.expect(TokenKind::From)?;
let collection = self.parse_identifier()?;
let filter = if self.peek()?.kind == TokenKind::Where {
self.advance()?;
Some(Box::new(self.parse_filter_expr()?))
} else {
None
};
let after = if self.peek()?.kind == TokenKind::After {
self.advance()?;
Some(self.parse_point_id("SCROLL AFTER")?)
} else {
None
};
let order_by = if self.peek()?.kind == TokenKind::Order {
self.advance()?;
self.expect(TokenKind::By)?;
let field = self.parse_field_path()?;
let direction = match self.peek()?.kind {
TokenKind::Desc => {
self.advance()?;
OrderDirection::Desc
}
TokenKind::Asc => {
self.advance()?;
OrderDirection::Asc
}
_ => OrderDirection::Asc,
};
let start_from = self.parse_optional_order_start()?;
Some(ScrollOrderBy {
field,
direction,
start_from,
})
} else {
None
};
let shard_key = if self.peek()?.kind == TokenKind::Shard {
self.advance()?;
Some(self.parse_shard_key_atom()?)
} else {
None
};
let with_payload = if self.peek()?.kind == TokenKind::With
&& self.peek_nth(1).kind == TokenKind::Payload
{
self.advance()?;
self.advance()?;
Some(self.parse_payload_selector()?)
} else {
None
};
let with_vector =
if self.peek()?.kind == TokenKind::With && self.peek_nth(1).kind == TokenKind::Vector {
self.advance()?;
self.advance()?;
Some(self.parse_vector_selector()?)
} else {
None
};
self.expect(TokenKind::Limit)?;
let (limit, limit_param, limit_span) =
if let Some((param, span)) = self.parse_placeholder_param()? {
(10, Some(param), Some(span))
} else {
(self.parse_positive_u64("SCROLL LIMIT")?, None, None)
};
Ok(Stmt::Scroll(Box::new(ScrollStmt {
collection,
limit,
filter,
after,
order_by,
shard_key,
with_payload,
with_vector,
limit_param,
limit_span,
})))
}
pub fn parse_count(&mut self) -> Result<Stmt, QqlError> {
self.expect(TokenKind::Count)?;
self.expect(TokenKind::From)?;
let collection = crate::ast::QueryCollection::Explicit(self.parse_identifier()?);
let filter = if self.peek()?.kind == TokenKind::Where {
self.advance()?;
Some(Box::new(self.parse_filter_expr()?))
} else {
None
};
let shard_key = if self.peek()?.kind == TokenKind::Shard {
self.advance()?;
Some(self.parse_shard_key_atom()?)
} else {
None
};
let exact = if self.peek()?.kind == TokenKind::With {
let with_span = self.peek()?.span;
self.advance()?;
let opts = self.parse_config_block()?;
let mut exact = None;
for (key, value) in &opts {
if !key.eq_ignore_ascii_case("exact") {
return Err(QqlError::parse(
"QQL-PARSE-COUNT-CONFIG",
alloc::format!("unknown COUNT parameter '{key}'. Expected: exact"),
with_span,
));
}
match value {
crate::ast::Value::Bool(b) => exact = Some(*b),
_ => {
return Err(QqlError::parse(
"QQL-PARSE-COUNT-CONFIG",
"COUNT 'exact' must be true or false",
with_span,
));
}
}
}
exact
} else {
None
};
if matches!(
self.peek()?.kind,
TokenKind::Where | TokenKind::Shard | TokenKind::With
) {
return Err(QqlError::parse(
"QQL-PARSE-CLAUSE-ORDER",
"duplicate or out-of-order COUNT clause (grammar order: WHERE, SHARD, WITH)",
self.peek()?.span,
));
}
Ok(Stmt::Count(Box::new(CountStmt {
collection,
filter,
shard_key,
exact,
})))
}
pub fn parse_facet(&mut self) -> Result<Stmt, QqlError> {
self.expect(TokenKind::Facet)?;
let (key, collection) = if self.peek()?.kind == TokenKind::From {
self.advance()?;
let coll = crate::ast::QueryCollection::Explicit(self.parse_identifier()?);
if self.peek()?.kind == TokenKind::Key || self.peek_word("KEY")? {
self.advance()?;
}
let k = self.parse_identifier()?;
(k, coll)
} else {
let k = self.parse_identifier()?;
self.expect(TokenKind::From)?;
let coll = crate::ast::QueryCollection::Explicit(self.parse_identifier()?);
(k, coll)
};
let mut filter = None;
let mut limit = None;
let mut limit_param = None;
let mut limit_span = None;
let mut exact = None;
let mut shard_key = None;
while self.peek()?.kind != TokenKind::Eof && self.peek()?.kind != TokenKind::Semicolon {
match self.peek()?.kind {
TokenKind::Where if filter.is_none() => {
self.advance()?;
filter = Some(Box::new(self.parse_filter_expr()?));
}
TokenKind::Limit if limit.is_none() && limit_param.is_none() => {
self.advance()?;
if let Some((param, span)) = self.parse_placeholder_param()? {
limit_param = Some(param);
limit_span = Some(span);
} else {
limit = Some(self.parse_positive_u64("FACET LIMIT")?);
}
}
TokenKind::Limit => {
return Err(QqlError::parse(
"QQL-PARSE-DUPLICATE-CLAUSE",
"duplicate FACET LIMIT clause",
self.peek()?.span,
));
}
TokenKind::Exact if exact.is_none() => {
self.advance()?;
match self.peek()?.kind {
TokenKind::True => {
self.advance()?;
exact = Some(true);
}
TokenKind::False => {
self.advance()?;
exact = Some(false);
}
_ => exact = Some(true),
}
}
TokenKind::Exact => {
return Err(QqlError::parse(
"QQL-PARSE-DUPLICATE-CLAUSE",
"duplicate FACET EXACT clause",
self.peek()?.span,
));
}
TokenKind::Shard if shard_key.is_none() => {
self.advance()?;
shard_key = Some(self.parse_shard_key_atom()?);
}
TokenKind::With => {
let with_span = self.peek()?.span;
self.advance()?;
let opts = self.parse_config_block()?;
for (k, v) in &opts {
if k.eq_ignore_ascii_case("exact") {
if exact.is_some() {
return Err(QqlError::parse(
"QQL-PARSE-DUPLICATE-CLAUSE",
"duplicate FACET EXACT clause",
with_span,
));
}
match v {
crate::ast::Value::Bool(b) => exact = Some(*b),
_ => {
return Err(QqlError::parse(
"QQL-PARSE-FACET-CONFIG",
"FACET 'exact' must be true or false",
with_span,
));
}
}
} else if k.eq_ignore_ascii_case("limit") {
if limit.is_some() || limit_param.is_some() {
return Err(QqlError::parse(
"QQL-PARSE-DUPLICATE-CLAUSE",
"duplicate FACET LIMIT clause",
with_span,
));
}
match v {
crate::ast::Value::Int(i) if *i > 0 => {
limit = Some(*i as u64);
}
_ => {
return Err(QqlError::parse(
"QQL-PARSE-FACET-CONFIG",
"FACET limit must be a positive integer",
with_span,
));
}
}
} else {
return Err(QqlError::parse(
"QQL-PARSE-FACET-CONFIG",
alloc::format!(
"unknown FACET parameter '{k}'. Expected: exact, limit"
),
with_span,
));
}
}
}
_ => break,
}
}
Ok(Stmt::Facet(Box::new(FacetStmt {
key,
collection,
filter,
limit,
exact,
shard_key,
limit_param,
limit_span,
})))
}
}