surrealpatch/src/sql/idiom.rs

283 lines
5.8 KiB
Rust
Raw Normal View History

2021-03-29 15:43:37 +00:00
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Runtime;
2021-03-29 15:43:37 +00:00
use crate::doc::Document;
use crate::err::Error;
2020-06-29 15:36:01 +00:00
use crate::sql::common::commas;
use crate::sql::common::{escape, val_char};
use crate::sql::filter::{filter, Filter};
use crate::sql::ident::ident_raw;
2021-03-29 15:43:37 +00:00
use crate::sql::literal::Literal;
2020-06-29 15:36:01 +00:00
use nom::bytes::complete::tag;
use nom::combinator::opt;
2021-03-29 15:43:37 +00:00
use nom::multi::separated_list1;
2020-06-29 15:36:01 +00:00
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
2021-03-29 15:43:37 +00:00
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
2020-06-29 15:36:01 +00:00
pub struct Idioms(Vec<Idiom>);
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 idioms(i: &str) -> IResult<&str, Idioms> {
2021-03-29 15:43:37 +00:00
let (i, v) = separated_list1(commas, idiom)(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)]
2020-06-29 15:36:01 +00:00
pub struct Idiom {
pub parts: Vec<(String, Option<Filter>)>,
}
impl<'a> From<&'a str> for Idiom {
fn from(s: &str) -> Self {
idiom(s).unwrap().1
}
}
impl From<Vec<(String, Option<Filter>)>> for Idiom {
fn from(v: Vec<(String, Option<Filter>)>) -> Self {
Idiom {
parts: v,
}
}
}
impl fmt::Display for Idiom {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.parts.len() == 1 {
match self.parts.first().unwrap() {
(i, Some(ref a)) => write!(f, "{}[{}]", i, a),
(i, None) => write!(f, "{}", escape(&i, &val_char, "`")),
}
} else {
write!(
f,
"{}",
self.parts
.iter()
.map(|(ref i, ref a)| match a {
Some(ref a) => format!("{}[{}]", i, a),
None => format!("{}", escape(&i, &val_char, "`")),
})
.collect::<Vec<_>>()
.join(".")
)
}
}
}
2021-03-29 15:43:37 +00:00
impl dbs::Process for Idiom {
fn process(
&self,
ctx: &Runtime,
2021-03-29 15:43:37 +00:00
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
2020-06-29 15:36:01 +00:00
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
2021-03-29 15:43:37 +00:00
let (i, v) = separated_list1(tag("."), all)(i)?;
2020-06-29 15:36:01 +00:00
Ok((i, Idiom::from(v)))
}
fn all(i: &str) -> IResult<&str, (String, Option<Filter>)> {
let (i, v) = raw(i)?;
let (i, a) = opt(fil)(i)?;
Ok((i, (v, a)))
}
fn raw(i: &str) -> IResult<&str, String> {
let (i, v) = ident_raw(i)?;
Ok((i, String::from(v)))
}
fn fil(i: &str) -> IResult<&str, Filter> {
let (i, _) = tag("[")(i)?;
let (i, v) = filter(i)?;
let (i, _) = tag("]")(i)?;
Ok((i, v))
}
#[cfg(test)]
mod tests {
use super::*;
#[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 {
parts: vec![(String::from("test"), None),],
}
);
}
#[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 {
parts: vec![(String::from("test"), None),],
}
);
}
#[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 {
parts: vec![(String::from("test"), None),],
}
);
}
#[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 {
parts: vec![(String::from("test"), None), (String::from("temp"), None),],
}
);
}
#[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 {
parts: vec![(String::from("test"), None), (String::from("some key"), None),],
}
);
}
#[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 {
parts: vec![
(String::from("test"), None),
(String::from("temp"), Some(Filter::from("*"))),
],
}
);
}
#[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 {
parts: vec![
(String::from("test"), None),
(String::from("temp"), Some(Filter::from("$"))),
],
}
);
}
#[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 {
parts: vec![
(String::from("test"), None),
(String::from("temp"), Some(Filter::from("*"))),
(String::from("text"), None),
],
}
);
}
#[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 {
parts: vec![
(String::from("test"), None),
(String::from("temp"), Some(Filter::from("WHERE test = true"))),
(String::from("text"), None),
],
}
);
}
#[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 {
parts: vec![
(String::from("test"), None),
(String::from("temp"), Some(Filter::from("WHERE test = true"))),
(String::from("text"), None),
],
}
);
}
}