surrealpatch/lib/src/sql/statements/ifelse.rs
2022-02-26 00:34:05 +00:00

141 lines
3.2 KiB
Rust

use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::value::{value, Value};
use derive::Store;
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::separated_list0;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)]
pub struct IfelseStatement {
pub exprs: Vec<(Value, Value)>,
pub close: Option<Value>,
}
impl IfelseStatement {
pub async fn compute(
&self,
ctx: &Runtime,
opt: &Options,
txn: &Transaction,
doc: Option<&Value>,
) -> Result<Value, Error> {
for (ref cond, ref then) in &self.exprs {
let v = cond.compute(ctx, opt, txn, doc).await?;
if v.is_truthy() {
return then.compute(ctx, opt, txn, doc).await;
}
}
match self.close {
Some(ref v) => v.compute(ctx, opt, txn, doc).await,
None => Ok(Value::None),
}
}
}
impl fmt::Display for IfelseStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.exprs
.iter()
.map(|(ref cond, ref then)| format!("IF {} THEN {}", cond, then))
.collect::<Vec<_>>()
.join(" ELSE ")
)?;
if let Some(ref v) = self.close {
write!(f, " ELSE {}", v)?
}
write!(f, " END")?;
Ok(())
}
}
pub fn ifelse(i: &str) -> IResult<&str, IfelseStatement> {
let (i, exprs) = separated_list0(split, exprs)(i)?;
let (i, close) = opt(close)(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("END")(i)?;
Ok((
i,
IfelseStatement {
exprs,
close,
},
))
}
fn exprs(i: &str) -> IResult<&str, (Value, Value)> {
let (i, _) = tag_no_case("IF")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, cond) = value(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("THEN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, then) = value(i)?;
Ok((i, (cond, then)))
}
fn close(i: &str) -> IResult<&str, Value> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ELSE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, then) = value(i)?;
Ok((i, then))
}
fn split(i: &str) -> IResult<&str, ()> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ELSE")(i)?;
let (i, _) = shouldbespace(i)?;
Ok((i, ()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ifelse_statement_first() {
let sql = "IF this THEN that END";
let res = ifelse(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out))
}
#[test]
fn ifelse_statement_close() {
let sql = "IF this THEN that ELSE that END";
let res = ifelse(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out))
}
#[test]
fn ifelse_statement_other() {
let sql = "IF this THEN that ELSE IF this THEN that END";
let res = ifelse(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out))
}
#[test]
fn ifelse_statement_other_close() {
let sql = "IF this THEN that ELSE IF this THEN that ELSE that END";
let res = ifelse(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out))
}
}