surrealdb_core/sql/statements/
ifelse.rs

1use crate::ctx::Context;
2use crate::dbs::Options;
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::sql::fmt::{fmt_separated_by, is_pretty, pretty_indent, Fmt, Pretty};
6use crate::sql::Value;
7
8use reblessive::tree::Stk;
9use revision::revisioned;
10use serde::{Deserialize, Serialize};
11use std::fmt::{self, Display, Write};
12
13#[revisioned(revision = 1)]
14#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
15#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
16#[non_exhaustive]
17pub struct IfelseStatement {
18	/// The first if condition followed by a body, followed by any number of else if's
19	pub exprs: Vec<(Value, Value)>,
20	/// the final else body, if there is one
21	pub close: Option<Value>,
22}
23
24impl IfelseStatement {
25	/// Check if we require a writeable transaction
26	pub(crate) fn writeable(&self) -> bool {
27		for (cond, then) in self.exprs.iter() {
28			if cond.writeable() || then.writeable() {
29				return true;
30			}
31		}
32		self.close.as_ref().is_some_and(Value::writeable)
33	}
34	/// Check if we require a writeable transaction
35	pub(crate) fn bracketed(&self) -> bool {
36		self.exprs.iter().all(|(_, v)| matches!(v, Value::Block(_)))
37			&& (self.close.as_ref().is_none()
38				|| self.close.as_ref().is_some_and(|v| matches!(v, Value::Block(_))))
39	}
40	/// Process this type returning a computed simple Value
41	pub(crate) async fn compute(
42		&self,
43		stk: &mut Stk,
44		ctx: &Context,
45		opt: &Options,
46		doc: Option<&CursorDoc>,
47	) -> Result<Value, Error> {
48		for (ref cond, ref then) in &self.exprs {
49			let v = cond.compute(stk, ctx, opt, doc).await?;
50			if v.is_truthy() {
51				return then.compute_unbordered(stk, ctx, opt, doc).await;
52			}
53		}
54		match self.close {
55			Some(ref v) => v.compute_unbordered(stk, ctx, opt, doc).await,
56			None => Ok(Value::None),
57		}
58	}
59}
60
61impl Display for IfelseStatement {
62	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63		let mut f = Pretty::from(f);
64		match self.bracketed() {
65			true => {
66				write!(
67					f,
68					"{}",
69					&Fmt::new(
70						self.exprs.iter().map(|args| {
71							Fmt::new(args, |(cond, then), f| {
72								if is_pretty() {
73									write!(f, "IF {cond}")?;
74									let indent = pretty_indent();
75									write!(f, "{then}")?;
76									drop(indent);
77								} else {
78									write!(f, "IF {cond} {then}")?;
79								}
80								Ok(())
81							})
82						}),
83						if is_pretty() {
84							fmt_separated_by("ELSE ")
85						} else {
86							fmt_separated_by(" ELSE ")
87						},
88					),
89				)?;
90				if let Some(ref v) = self.close {
91					if is_pretty() {
92						write!(f, "ELSE")?;
93						let indent = pretty_indent();
94						write!(f, "{v}")?;
95						drop(indent);
96					} else {
97						write!(f, " ELSE {v}")?;
98					}
99				}
100				Ok(())
101			}
102			false => {
103				write!(
104					f,
105					"{}",
106					&Fmt::new(
107						self.exprs.iter().map(|args| {
108							Fmt::new(args, |(cond, then), f| {
109								if is_pretty() {
110									write!(f, "IF {cond} THEN")?;
111									let indent = pretty_indent();
112									write!(f, "{then}")?;
113									drop(indent);
114								} else {
115									write!(f, "IF {cond} THEN {then}")?;
116								}
117								Ok(())
118							})
119						}),
120						if is_pretty() {
121							fmt_separated_by("ELSE ")
122						} else {
123							fmt_separated_by(" ELSE ")
124						},
125					),
126				)?;
127				if let Some(ref v) = self.close {
128					if is_pretty() {
129						write!(f, "ELSE")?;
130						let indent = pretty_indent();
131						write!(f, "{v}")?;
132						drop(indent);
133					} else {
134						write!(f, " ELSE {v}")?;
135					}
136				}
137				if is_pretty() {
138					f.write_str("END")?;
139				} else {
140					f.write_str(" END")?;
141				}
142				Ok(())
143			}
144		}
145	}
146}
147
148#[cfg(test)]
149mod tests {
150	use crate::syn::parse;
151
152	#[test]
153	fn format_pretty() {
154		let query = parse("IF 1 { 1 } ELSE IF 2 { 2 }").unwrap();
155		assert_eq!(format!("{}", query), "IF 1 { 1 } ELSE IF 2 { 2 };");
156		assert_eq!(format!("{:#}", query), "IF 1\n\t{ 1 }\nELSE IF 2\n\t{ 2 }\n;");
157	}
158}