surrealpatch/lib/src/sql/idiom.rs

281 lines
6.6 KiB
Rust
Raw Normal View History

use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
2021-03-29 15:43:37 +00:00
use crate::err::Error;
2020-06-29 15:36:01 +00:00
use crate::sql::common::commas;
2022-01-16 20:31:50 +00:00
use crate::sql::error::IResult;
use crate::sql::part::{all, field, first, graph, index, last, part, Part};
use crate::sql::value::Value;
use nom::branch::alt;
use nom::multi::many0;
2021-03-29 15:43:37 +00:00
use nom::multi::separated_list1;
2020-06-29 15:36:01 +00:00
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::Deref;
2020-06-29 15:36:01 +00:00
use std::str;
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Idioms(pub Vec<Idiom>);
2020-06-29 15:36:01 +00:00
2022-04-09 09:09:01 +00:00
impl Deref for Idioms {
type Target = Vec<Idiom>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
2020-06-29 15:36:01 +00:00
impl fmt::Display for Idioms {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", "))
}
}
pub fn locals(i: &str) -> IResult<&str, Idioms> {
let (i, v) = separated_list1(commas, local)(i)?;
2020-06-29 15:36:01 +00:00
Ok((i, Idioms(v)))
}
2021-03-29 15:43:37 +00:00
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Idiom(pub Vec<Part>);
2020-06-29 15:36:01 +00:00
impl Deref for Idiom {
type Target = [Part];
fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
impl From<String> for Idiom {
fn from(v: String) -> Self {
Idiom(vec![Part::from(v)])
}
}
impl From<Vec<Part>> for Idiom {
fn from(v: Vec<Part>) -> Self {
Idiom(v)
2020-06-29 15:36:01 +00:00
}
}
impl Idiom {
// Appends a part to the end of this Idiom
pub(crate) fn push(mut self, n: Part) -> Idiom {
self.0.push(n);
self
}
// Convert this Idiom to a JSON Path string
pub(crate) fn to_path(&self) -> String {
format!("/{}", self).replace(']', "").replace(&['.', '['][..], "/")
}
// Simplifies this Idiom for use in object keys
pub(crate) fn simplify(&self) -> Idiom {
self.0
.iter()
.cloned()
.filter(|p| matches!(p, Part::Field(_) | Part::Graph(_)))
.collect::<Vec<_>>()
.into()
}
// Check if this is an expression with multiple yields
pub(crate) fn is_multi_yield(&self) -> bool {
self.iter().any(Self::split_multi_yield)
}
// Check if the path part is a yield in a multi-yield expression
pub(crate) fn split_multi_yield(v: &Part) -> bool {
matches!(v, Part::Graph(g) if g.alias.is_some())
}
2020-06-29 15:36:01 +00:00
}
2022-01-14 08:12:56 +00:00
impl Idiom {
pub(crate) async fn compute(
2022-01-14 08:12:56 +00:00
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
2022-01-14 08:12:56 +00:00
doc: Option<&Value>,
) -> Result<Value, Error> {
match doc {
// There is a current document
Some(v) => v.get(ctx, opt, txn, self).await?.compute(ctx, opt, txn, doc).await,
2022-01-14 08:12:56 +00:00
// There isn't any document
None => Ok(Value::None),
}
}
}
2020-06-29 15:36:01 +00:00
impl fmt::Display for Idiom {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.0
.iter()
.enumerate()
.map(|(i, p)| match (i, p) {
(0, Part::Field(v)) => format!("{}", v),
_ => format!("{}", p),
})
.collect::<Vec<_>>()
.join("")
)
2020-06-29 15:36:01 +00:00
}
}
// Used in a DEFINE FIELD and DEFINE INDEX clauses
pub fn local(i: &str) -> IResult<&str, Idiom> {
let (i, p) = first(i)?;
let (i, mut v) = many0(alt((all, index, field)))(i)?;
v.insert(0, p);
2020-06-29 15:36:01 +00:00
Ok((i, Idiom::from(v)))
}
// Used in a SPLIT, ORDER, and GROUP clauses
pub fn basic(i: &str) -> IResult<&str, Idiom> {
let (i, p) = first(i)?;
let (i, mut v) = many0(alt((all, last, index, field)))(i)?;
v.insert(0, p);
Ok((i, Idiom::from(v)))
}
// Used in a $param definition
pub fn param(i: &str) -> IResult<&str, Idiom> {
let (i, p) = first(i)?;
let (i, mut v) = many0(part)(i)?;
v.insert(0, p);
Ok((i, Idiom::from(v)))
2020-06-29 15:36:01 +00:00
}
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
let (i, p) = alt((first, graph))(i)?;
let (i, mut v) = many0(part)(i)?;
v.insert(0, p);
Ok((i, Idiom::from(v)))
2020-06-29 15:36:01 +00:00
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::expression::Expression;
use crate::sql::test::Parse;
2020-06-29 15:36:01 +00:00
#[test]
fn idiom_normal() {
let sql = "test";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test", format!("{}", out));
assert_eq!(out, Idiom(vec![Part::from("test")]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn idiom_quoted_backtick() {
let sql = "`test`";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test", format!("{}", out));
assert_eq!(out, Idiom(vec![Part::from("test")]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn idiom_quoted_brackets() {
let sql = "⟨test⟩";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test", format!("{}", out));
assert_eq!(out, Idiom(vec![Part::from("test")]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn idiom_nested() {
let sql = "test.temp";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test.temp", format!("{}", out));
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"),]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn idiom_nested_quoted() {
let sql = "test.`some key`";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test.`some key`", format!("{}", out));
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("some key"),]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn idiom_nested_array_all() {
let sql = "test.temp[*]";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test.temp[*]", format!("{}", out));
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"), Part::All,]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn idiom_nested_array_last() {
let sql = "test.temp[$]";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test.temp[$]", format!("{}", out));
assert_eq!(out, Idiom(vec![Part::from("test"), Part::from("temp"), Part::Last,]));
2020-06-29 15:36:01 +00:00
}
#[test]
fn idiom_nested_array_value() {
let sql = "test.temp[*].text";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test.temp[*].text", format!("{}", out));
assert_eq!(
out,
Idiom(vec![Part::from("test"), Part::from("temp"), Part::All, Part::from("text")])
2020-06-29 15:36:01 +00:00
);
}
#[test]
fn idiom_nested_array_question() {
let sql = "test.temp[? test = true].text";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test.temp[WHERE test = true].text", format!("{}", out));
assert_eq!(
out,
Idiom(vec![
Part::from("test"),
Part::from("temp"),
Part::from(Value::from(Expression::parse("test = true"))),
Part::from("text")
])
2020-06-29 15:36:01 +00:00
);
}
#[test]
fn idiom_nested_array_condition() {
let sql = "test.temp[WHERE test = true].text";
let res = idiom(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("test.temp[WHERE test = true].text", format!("{}", out));
assert_eq!(
out,
Idiom(vec![
Part::from("test"),
Part::from("temp"),
Part::from(Value::from(Expression::parse("test = true"))),
Part::from("text")
])
2020-06-29 15:36:01 +00:00
);
}
}