use std::collections::BTreeMap;
use reblessive::Stk;
use super::mac::unexpected;
use super::{ParseResult, Parser};
use crate::sql::Kind;
use crate::sql::kind::{GeometryKind, KindLiteral};
use crate::syn::lexer::compound;
use crate::syn::parser::enter_object_recursion;
use crate::syn::parser::mac::expected;
use crate::syn::token::{Keyword, Span, TokenKind, t};
use crate::types::PublicDuration;
impl Parser<'_> {
pub(crate) async fn parse_kind(&mut self, stk: &mut Stk, delim: Span) -> ParseResult<Kind> {
let kind = self.parse_inner_kind(stk).await?;
self.expect_closing_delimiter(t!(">"), delim)?;
Ok(kind)
}
pub(crate) async fn parse_inner_kind(&mut self, stk: &mut Stk) -> ParseResult<Kind> {
match self.parse_inner_single_kind(stk).await? {
Kind::Any => Ok(Kind::Any),
first => {
if self.peek_kind() == t!("|") {
let mut kind = vec![first];
while self.eat(t!("|")) {
kind.push(stk.run(|ctx| self.parse_concrete_kind(ctx)).await?);
}
let kind = Kind::either(kind);
Ok(kind)
} else {
Ok(first)
}
}
}
}
pub(super) async fn parse_inner_single_kind(&mut self, stk: &mut Stk) -> ParseResult<Kind> {
match self.peek_kind() {
t!("ANY") => {
self.pop_peek();
Ok(Kind::Any)
}
t!("OPTION") => {
self.pop_peek();
enter_object_recursion!(this = self => {
let delim = expected!(this, t!("<")).span;
let mut kinds =
vec![Kind::None, stk.run(|ctx| this.parse_concrete_kind(ctx)).await?];
if this.peek_kind() == t!("|") {
while this.eat(t!("|")) {
kinds.push(stk.run(|ctx| this.parse_concrete_kind(ctx)).await?);
}
}
this.expect_closing_delimiter(t!(">"), delim)?;
Ok(Kind::either(kinds))
})
}
_ => stk.run(|ctx| self.parse_concrete_kind(ctx)).await,
}
}
async fn parse_concrete_kind(&mut self, stk: &mut Stk) -> ParseResult<Kind> {
enter_object_recursion!(this = self => {
let next = this.next();
match next.kind {
t!("true") => Ok(Kind::Literal(KindLiteral::Bool(true))),
t!("false") => Ok(Kind::Literal(KindLiteral::Bool(false))),
t!("'") | t!("\"") => {
let str = this.unescape_string_span(next.span)?;
Ok(Kind::Literal(KindLiteral::String(str.into())))
}
TokenKind::NaN => Ok(Kind::Literal(KindLiteral::Float(f64::NAN))),
TokenKind::Infinity => Ok(Kind::Literal(KindLiteral::Float(f64::INFINITY))),
t!("+") | t!("-") => {
let compound = this.lex_compound(next, compound::number)?;
let kind = match compound.value {
compound::Numeric::Float(f) => KindLiteral::Float(f),
compound::Numeric::Integer(int) => {
KindLiteral::Integer(int.into_int(compound.span)?)
}
compound::Numeric::Decimal(decimal) => KindLiteral::Decimal(decimal),
compound::Numeric::Duration(_) => unreachable!(),
};
Ok(Kind::Literal(kind))
}
TokenKind::Digits => {
let compound = this.lex_compound(next, compound::numeric)?;
let v = match compound.value {
compound::Numeric::Integer(x) => {
KindLiteral::Integer(x.into_int(compound.span)?)
}
compound::Numeric::Float(x) => KindLiteral::Float(x),
compound::Numeric::Decimal(x) => KindLiteral::Decimal(x),
compound::Numeric::Duration(x) => {
KindLiteral::Duration(PublicDuration::from_std(x))
}
};
Ok(Kind::Literal(v))
}
t!("{") => {
let mut obj = BTreeMap::new();
while !this.eat(t!("}")) {
let key = this.parse_object_key()?;
expected!(this, t!(":"));
let kind = stk.run(|ctx| this.parse_inner_kind(ctx)).await?;
obj.insert(key.into(), kind);
this.eat(t!(","));
}
Ok(Kind::Literal(KindLiteral::Object(obj)))
}
t!("[") => {
let mut arr = Vec::new();
while !this.eat(t!("]")) {
let kind = stk.run(|ctx| this.parse_inner_kind(ctx)).await?;
arr.push(kind);
this.eat(t!(","));
}
Ok(Kind::Literal(KindLiteral::Array(arr)))
}
t!("BOOL") => Ok(Kind::Bool),
t!("NONE") => Ok(Kind::None),
t!("NULL") => Ok(Kind::Null),
t!("BYTES") => Ok(Kind::Bytes),
t!("DATETIME") => Ok(Kind::Datetime),
t!("DECIMAL") => Ok(Kind::Decimal),
t!("DURATION") => Ok(Kind::Duration),
t!("FLOAT") => Ok(Kind::Float),
t!("INT") => Ok(Kind::Int),
t!("NUMBER") => Ok(Kind::Number),
t!("OBJECT") => Ok(Kind::Object),
t!("POINT") => Ok(Kind::Geometry(vec![GeometryKind::Point])),
t!("STRING") => Ok(Kind::String),
t!("UUID") => Ok(Kind::Uuid),
t!("RANGE") => Ok(Kind::Range),
t!("REGEX") => Ok(Kind::Regex),
t!("FUNCTION") => Ok(Kind::Function(Default::default(), Default::default())),
t!("RECORD") => {
let span = this.peek().span;
if this.eat(t!("<")) {
let mut tables = vec![this.parse_ident_str()?.into()];
while this.eat(t!("|")) {
tables.push(this.parse_ident_str()?.into());
}
this.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Record(tables))
} else {
Ok(Kind::Record(Vec::new()))
}
}
t!("TABLE") => {
let span = this.peek().span;
if this.eat(t!("<")) {
let mut tables = vec![this.parse_ident_str()?.into()];
while this.eat(t!("|")) {
tables.push(this.parse_ident_str()?.into());
}
this.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Table(tables))
} else {
Ok(Kind::Table(Vec::new()))
}
}
t!("GEOMETRY") => {
let span = this.peek().span;
if this.eat(t!("<")) {
let mut kind = vec![this.parse_geometry_kind()?];
while this.eat(t!("|")) {
kind.push(this.parse_geometry_kind()?);
}
this.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Geometry(kind))
} else {
Ok(Kind::Geometry(Vec::new()))
}
}
t!("ARRAY") => {
let span = this.peek().span;
if this.eat(t!("<")) {
let kind = stk.run(|ctx| this.parse_inner_kind(ctx)).await?;
let size = this.eat(t!(",")).then(|| this.next_token_value()).transpose()?;
this.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Array(Box::new(kind), size))
} else {
Ok(Kind::Array(Box::new(Kind::Any), None))
}
}
t!("SET") => {
let span = this.peek().span;
if this.eat(t!("<")) {
let kind = stk.run(|ctx| this.parse_inner_kind(ctx)).await?;
let size = this.eat(t!(",")).then(|| this.next_token_value()).transpose()?;
this.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Set(Box::new(kind), size))
} else {
Ok(Kind::Set(Box::new(Kind::Any), None))
}
}
t!("FILE") => {
let span = this.peek().span;
if this.eat(t!("<")) {
let mut buckets = vec![this.parse_ident()?.into_string()];
while this.eat(t!("|")) {
buckets.push(this.parse_ident()?.into_string());
}
this.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::File(buckets))
} else {
Ok(Kind::File(Vec::new()))
}
}
_ => unexpected!(this, next, "a kind name"),
}
})
}
fn parse_geometry_kind(&mut self) -> ParseResult<GeometryKind> {
let next = self.next();
match next.kind {
TokenKind::Keyword(keyword) => match keyword {
Keyword::Point => Ok(GeometryKind::Point),
Keyword::Line => Ok(GeometryKind::Line),
Keyword::Polygon => Ok(GeometryKind::Polygon),
Keyword::MultiPoint => Ok(GeometryKind::MultiPoint),
Keyword::MultiLine => Ok(GeometryKind::MultiLine),
Keyword::MultiPolygon => Ok(GeometryKind::MultiPolygon),
Keyword::Collection => Ok(GeometryKind::Collection),
_ => unexpected!(self, next, "a geometry kind name"),
},
_ => unexpected!(self, next, "a geometry kind name"),
}
}
}
#[cfg(test)]
mod tests {
use reblessive::Stack;
use rstest::rstest;
use surrealdb_types::ToSql;
use super::*;
fn kind(i: &str) -> ParseResult<Kind> {
let mut parser = Parser::new(i.as_bytes());
let mut stack = Stack::new();
stack.enter(|ctx| parser.parse_inner_kind(ctx)).finish()
}
#[rstest]
#[case::any("any", "any", Kind::Any)]
#[case::none("none", "none", Kind::None)]
#[case::null("null", "null", Kind::Null)]
#[case::bool("bool", "bool", Kind::Bool)]
#[case::bytes("bytes", "bytes", Kind::Bytes)]
#[case::datetime("datetime", "datetime", Kind::Datetime)]
#[case::decimal("decimal", "decimal", Kind::Decimal)]
#[case::duration("duration", "duration", Kind::Duration)]
#[case::float("float", "float", Kind::Float)]
#[case::number("number", "number", Kind::Number)]
#[case::object("object", "object", Kind::Object)]
#[case::point("point", "geometry<point>", Kind::Geometry(vec![GeometryKind::Point]))]
#[case::string("string", "string", Kind::String)]
#[case::uuid("uuid", "uuid", Kind::Uuid)]
#[case::either("int | float", "int | float", Kind::Either(vec![Kind::Int, Kind::Float]))]
#[case::record("record", "record", Kind::Record(vec![]))]
#[case::record_one("record<person>", "record<person>", Kind::Record(vec!["person".into()]))]
#[case::record_many("record<person | animal>", "record<person | animal>", Kind::Record(vec!["person".into(), "animal".into()]))]
#[case::table("table", "table", Kind::Table(vec![]))]
#[case::table_one("table<person>", "table<person>", Kind::Table(vec!["person".into()]))]
#[case::table_many("table<person | animal>", "table<person | animal>", Kind::Table(vec!["person".into(), "animal".into()]))]
#[case::geometry("geometry", "geometry", Kind::Geometry(vec![]))]
#[case::geometry_one("geometry<point>", "geometry<point>", Kind::Geometry(vec![GeometryKind::Point]))]
#[case::geometry_many("geometry<point | multipoint>", "geometry<point | multipoint>", Kind::Geometry(vec![GeometryKind::Point, GeometryKind::MultiPoint]))]
#[case::option_one("option<int>", "none | int", Kind::Either(vec![Kind::None, Kind::Int]))]
#[case::option_many("option<int | float>", "none | int | float", Kind::Either(vec![Kind::None, Kind::Int, Kind::Float]))]
#[case::none_tuple("none | int | float", "none | int | float", Kind::Either(vec![Kind::None, Kind::Int, Kind::Float]))]
#[case::array_any("array", "array", Kind::Array(Box::new(Kind::Any), None))]
#[case::array_some("array<float>", "array<float>", Kind::Array(Box::new(Kind::Float), None))]
#[case::array_some_size(
"array<float, 10>",
"array<float, 10>",
Kind::Array(Box::new(Kind::Float), Some(10))
)]
#[case::set_any("set", "set", Kind::Set(Box::new(Kind::Any), None))]
#[case::set_some("set<float>", "set<float>", Kind::Set(Box::new(Kind::Float), None))]
#[case::set_some_size(
"set<float, 10>",
"set<float, 10>",
Kind::Set(Box::new(Kind::Float), Some(10))
)]
#[case::function_any("function", "function", Kind::Function(None, None))]
#[case::file_record_any("file", "file", Kind::File(vec![]))]
#[case::file_record_one("file<one>", "file<one>", Kind::File(vec!["one".to_owned()]))]
#[case::file_record_many("file<one | two>", "file<one | two>", Kind::File(vec!["one".to_string(), "two".to_string()]))]
fn test_kind(#[case] sql: &str, #[case] expected_str: &str, #[case] expected_kind: Kind) {
let res = kind(sql);
let out = res.unwrap();
assert_eq!(expected_str, out.to_sql());
assert_eq!(expected_kind, out);
}
}