surrealdb-sql 1.1.0

Full type definitions for the SurrealQL query language
Documentation
use super::super::{block::block, comment::shouldbespace, value::value, IResult};
use crate::{statements::IfelseStatement, Value};
use nom::{
	bytes::complete::tag_no_case,
	combinator::{cut, into, opt},
	sequence::terminated,
};

pub fn ifelse(i: &str) -> IResult<&str, IfelseStatement> {
	use super::super::depth;
	// Limit recursion depth.
	let _diving = depth::dive(i)?;
	let (i, _) = tag_no_case("IF")(i)?;
	let (i, _) = shouldbespace(i)?;
	let (i, cond) = value(i)?;
	let (i, _) = shouldbespace(i)?;
	if let (i, Some(_)) = opt(terminated(tag_no_case("THEN"), shouldbespace))(i)? {
		worded(i, cond)
	} else {
		bracketed(i, cond)
	}
}

fn worded(i: &str, initial_cond: Value) -> IResult<&str, IfelseStatement> {
	//
	fn expr(i: &str) -> IResult<&str, (Value, Value)> {
		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 split(i: &str) -> IResult<&str, ()> {
		let (i, _) = shouldbespace(i)?;
		let (i, _) = tag_no_case("ELSE")(i)?;
		let (i, _) = shouldbespace(i)?;
		Ok((i, ()))
	}

	let (mut input, then) = value(i)?;
	let mut exprs = vec![(initial_cond, then)];
	let mut close = None;

	loop {
		let (i, Some(_)) = opt(split)(input)? else {
			break;
		};
		let (i, Some(_)) = opt(terminated(tag_no_case("IF"), shouldbespace))(i)? else {
			let (i, v) = cut(value)(i)?;
			close = Some(v);
			input = i;
			break;
		};
		let (i, branch) = cut(expr)(i)?;
		exprs.push(branch);
		input = i;
	}

	let (i, _) = shouldbespace(input)?;
	let (i, _) = tag_no_case("END")(i)?;
	Ok((
		i,
		IfelseStatement {
			exprs,
			close,
		},
	))
}

fn bracketed(i: &str, initial_cond: Value) -> IResult<&str, IfelseStatement> {
	//
	fn expr(i: &str) -> IResult<&str, (Value, Value)> {
		let (i, cond) = value(i)?;
		let (i, _) = shouldbespace(i)?;
		let (i, then) = into(block)(i)?;
		Ok((i, (cond, then)))
	}
	//
	fn split(i: &str) -> IResult<&str, ()> {
		let (i, _) = shouldbespace(i)?;
		let (i, _) = tag_no_case("ELSE")(i)?;
		let (i, _) = shouldbespace(i)?;
		Ok((i, ()))
	}

	let (mut input, then) = into(block)(i)?;
	let mut exprs = vec![(initial_cond, then)];
	let mut close = None;

	loop {
		let (i, Some(_)) = opt(split)(input)? else {
			break;
		};
		let (i, Some(_)) = opt(terminated(tag_no_case("IF"), shouldbespace))(i)? else {
			let (i, c) = into(cut(block))(i)?;
			close = Some(c);
			input = i;
			break;
		};
		let (i, branch) = cut(expr)(i)?;
		exprs.push(branch);
		input = i;
	}

	Ok((
		input,
		IfelseStatement {
			exprs,
			close,
		},
	))
}

#[cfg(test)]
mod tests {

	use super::*;

	#[test]
	fn ifelse_worded_statement_first() {
		let sql = "IF this THEN that END";
		let res = ifelse(sql);
		let out = res.unwrap().1;
		assert_eq!(sql, format!("{}", out))
	}

	#[test]
	fn ifelse_worded_statement_close() {
		let sql = "IF this THEN that ELSE that END";
		let res = ifelse(sql);
		let out = res.unwrap().1;
		assert_eq!(sql, format!("{}", out))
	}

	#[test]
	fn ifelse_worded_statement_other() {
		let sql = "IF this THEN that ELSE IF this THEN that END";
		let res = ifelse(sql);
		let out = res.unwrap().1;
		assert_eq!(sql, format!("{}", out))
	}

	#[test]
	fn ifelse_worded_statement_other_close() {
		let sql = "IF this THEN that ELSE IF this THEN that ELSE that END";
		let res = ifelse(sql);
		let out = res.unwrap().1;
		assert_eq!(sql, format!("{}", out))
	}

	#[test]
	fn ifelse_bracketed_statement_first() {
		let sql = "IF this { that }";
		let res = ifelse(sql);
		let res = res.unwrap().1.to_string();
		assert_eq!(sql, res)
	}

	#[test]
	fn ifelse_bracketed_statement_close() {
		let sql = "IF this { that } ELSE { that }";
		let res = ifelse(sql);
		let out = res.unwrap().1;
		assert_eq!(sql, format!("{}", out))
	}

	#[test]
	fn ifelse_bracketed_statement_other() {
		let sql = "IF this { that } ELSE IF this { that }";
		let res = ifelse(sql);
		let out = res.unwrap().1;
		assert_eq!(sql, format!("{}", out))
	}

	#[test]
	fn ifelse_bracketed_statement_other_close() {
		let sql = "IF this { that } ELSE IF this { that } ELSE { that }";
		let res = ifelse(sql);
		let out = res.unwrap().1;
		assert_eq!(sql, format!("{}", out))
	}
}