surrealpatch/lib/src/sql/graph.rs
2022-06-15 08:49:57 +01:00

171 lines
3.8 KiB
Rust

use crate::sql::comment::mightbespace;
use crate::sql::comment::shouldbespace;
use crate::sql::cond::{cond, Cond};
use crate::sql::dir::{dir, Dir};
use crate::sql::error::IResult;
use crate::sql::idiom::{idiom, Idiom};
use crate::sql::table::{table, tables, Tables};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::char;
use nom::combinator::map;
use nom::combinator::opt;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Graph {
pub dir: Dir,
pub what: Tables,
pub cond: Option<Cond>,
pub alias: Option<Idiom>,
}
impl Graph {
pub fn to_raw(&self) -> String {
self.to_string()
}
}
impl fmt::Display for Graph {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.what.0.len() <= 1 && self.cond.is_none() && self.alias.is_none() {
write!(f, "{}", self.dir)?;
match self.what.len() {
0 => write!(f, "?"),
_ => write!(f, "{}", self.what),
}
} else {
write!(f, "{}(", self.dir)?;
match self.what.len() {
0 => write!(f, "?"),
_ => write!(f, "{}", self.what),
}?;
if let Some(ref v) = self.cond {
write!(f, " {}", v)?
}
if let Some(ref v) = self.alias {
write!(f, " AS {}", v)?
}
write!(f, ")")
}
}
}
pub fn graph(i: &str) -> IResult<&str, Graph> {
let (i, dir) = dir(i)?;
let (i, (what, cond, alias)) = alt((simple, custom))(i)?;
Ok((
i,
Graph {
dir,
what,
cond,
alias,
},
))
}
fn simple(i: &str) -> IResult<&str, (Tables, Option<Cond>, Option<Idiom>)> {
let (i, w) = alt((any, one))(i)?;
Ok((i, (w, None, None)))
}
fn custom(i: &str) -> IResult<&str, (Tables, Option<Cond>, Option<Idiom>)> {
let (i, _) = char('(')(i)?;
let (i, _) = mightbespace(i)?;
let (i, w) = alt((any, tables))(i)?;
let (i, c) = opt(|i| {
let (i, _) = shouldbespace(i)?;
let (i, v) = cond(i)?;
Ok((i, v))
})(i)?;
let (i, a) = opt(|i| {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("AS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = idiom(i)?;
Ok((i, v))
})(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = char(')')(i)?;
Ok((i, (w, c, a)))
}
fn one(i: &str) -> IResult<&str, Tables> {
let (i, v) = table(i)?;
Ok((i, Tables::from(v)))
}
fn any(i: &str) -> IResult<&str, Tables> {
map(char('?'), |_| Tables::default())(i)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn graph_in() {
let sql = "<-likes";
let res = graph(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<-likes", format!("{}", out));
}
#[test]
fn graph_out() {
let sql = "->likes";
let res = graph(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("->likes", format!("{}", out));
}
#[test]
fn graph_both() {
let sql = "<->likes";
let res = graph(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<->likes", format!("{}", out));
}
#[test]
fn graph_multiple() {
let sql = "->(likes, follows)";
let res = graph(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("->(likes, follows)", format!("{}", out));
}
#[test]
fn graph_aliases() {
let sql = "->(likes, follows AS connections)";
let res = graph(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("->(likes, follows AS connections)", format!("{}", out));
}
#[test]
fn graph_conditions() {
let sql = "->(likes, follows WHERE influencer = true)";
let res = graph(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("->(likes, follows WHERE influencer = true)", format!("{}", out));
}
#[test]
fn graph_conditions_aliases() {
let sql = "->(likes, follows WHERE influencer = true AS connections)";
let res = graph(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("->(likes, follows WHERE influencer = true AS connections)", format!("{}", out));
}
}