Skip to main content

surrealdb_sql/statements/
ifelse.rs

1use common::fmt::{Fmt, fmt_separated_by};
2use surrealdb_types::{SqlFormat, ToSql, write_sql};
3
4use crate::{CoverStmts, Expr};
5
6#[derive(Clone, Debug, Default, Eq, PartialEq)]
7#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
8pub struct IfelseStatement {
9	/// The first if condition followed by a body, followed by any number of
10	/// else if's
11	#[cfg_attr(feature = "arbitrary", arbitrary(with = crate::arbitrary::atleast_one))]
12	pub exprs: Vec<(Expr, Expr)>,
13	/// the final else body, if there is one
14	pub close: Option<Expr>,
15}
16
17impl IfelseStatement {
18	/// Check if the statement is bracketed
19	pub fn bracketed(&self) -> bool {
20		self.exprs.iter().all(|(_, v)| matches!(v, Expr::Block(_)))
21			&& self.close.as_ref().map(|v| matches!(v, Expr::Block(_))).unwrap_or(true)
22	}
23}
24
25impl ToSql for IfelseStatement {
26	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
27		if self.bracketed() {
28			// Helper to check if a block contains only simple expressions
29			let is_simple_block = |expr: &Expr| -> bool {
30				if let Expr::Block(block) = expr {
31					block.0.iter().all(|stmt| {
32						matches!(stmt, Expr::Literal(_) | Expr::Param(_) | Expr::Idiom(_))
33					})
34				} else {
35					false
36				}
37			};
38
39			// Check if this IF statement has any complex multi-statement blocks (determines overall
40			// formatting style)
41			let has_complex_multi = self.exprs.iter().any(
42				|(_, expr)| matches!(expr, Expr::Block(block) if block.0.len() > 1 && !is_simple_block(expr)),
43			) || self
44				.close
45				.as_ref()
46				.map(
47					|expr| matches!(expr, Expr::Block(block) if block.0.len() > 1 && !is_simple_block(expr)),
48				)
49				.unwrap_or(false);
50
51			// Helper to format block contents specially for IF statements
52			let fmt_block = |f: &mut String, fmt: SqlFormat, expr: &Expr, use_separated: bool| {
53				if let Expr::Block(block) = expr {
54					match block.0.len() {
55						0 => f.push_str("{;}"),
56						1 if !use_separated => {
57							// Single statement in inline mode: always compact
58							f.push_str("{ ");
59							block.0[0].fmt_sql(f, SqlFormat::SingleLine);
60							f.push_str(" }");
61						}
62						1 => {
63							// Single statement in separated mode (for complex IF statements)
64							f.push('{');
65							f.push(' ');
66							block.0[0].fmt_sql(f, SqlFormat::SingleLine);
67							f.push(' ');
68							f.push('}');
69						}
70						_ => {
71							// Multi-statement blocks
72							let needs_indent = is_simple_block(expr);
73
74							if fmt.is_pretty() && !needs_indent {
75								// Pretty mode with complex statements: custom formatting with
76								// double indent
77								f.push_str("{\n\n");
78								let inner_fmt = fmt.increment();
79								for (i, stmt) in block.0.iter().enumerate() {
80									if i > 0 {
81										f.push('\n');
82										f.push('\n');
83									}
84									inner_fmt.write_indent(f);
85									stmt.fmt_sql(f, SqlFormat::SingleLine);
86									f.push(';');
87								}
88								f.push('\n');
89								// Write indent at the block's level (outer fmt), not the content
90								// level
91								fmt.write_indent(f);
92								f.push('\n');
93								f.push('}');
94							} else if fmt.is_pretty() {
95								// Pretty mode with simple statements: custom simple formatting
96								f.push_str("{\n\n");
97								for (i, stmt) in block.0.iter().enumerate() {
98									if i > 0 {
99										f.push('\n');
100									}
101									f.push('\t');
102									stmt.fmt_sql(f, SqlFormat::SingleLine);
103									f.push(';');
104								}
105								f.push_str("\n}");
106							} else {
107								// Non-pretty mode
108								f.push_str("{\n");
109								for (i, stmt) in block.0.iter().enumerate() {
110									if i > 0 {
111										f.push('\n');
112									}
113									if needs_indent {
114										f.push('\t');
115									}
116									stmt.fmt_sql(f, SqlFormat::SingleLine);
117									f.push(';');
118								}
119								f.push_str("\n}");
120							}
121						}
122					}
123				} else {
124					expr.fmt_sql(f, fmt);
125				}
126			};
127
128			// In pretty mode: use separated format if we have complex multi-statement blocks,
129			// OR if we're nested (already indented)
130			let is_nested = matches!(fmt, SqlFormat::Indented(level) if level > 0);
131			let use_separated = fmt.is_pretty() && (has_complex_multi || is_nested);
132
133			write_sql!(
134				f,
135				fmt,
136				"{}",
137				&Fmt::new(
138					self.exprs.iter().map(|args| {
139						Fmt::new(args, |(cond, then), f, fmt| {
140							if use_separated {
141								// Separated format: condition and block on different lines
142								write_sql!(f, fmt, "IF {}", CoverStmts(cond));
143								f.push('\n');
144								// For nested IFs, use same indent level; for top-level complex IFs,
145								// increment
146								if is_nested {
147									fmt.write_indent(f);
148									fmt_block(f, fmt, then, true);
149								} else {
150									let fmt = fmt.increment();
151									fmt.write_indent(f);
152									fmt_block(f, fmt, then, true);
153								}
154							} else {
155								// Inline format: condition and block on same line
156								write_sql!(f, fmt, "IF {} ", CoverStmts(cond));
157								fmt_block(f, fmt, then, false);
158							}
159						})
160					}),
161					if use_separated {
162						fmt_separated_by("\nELSE ")
163					} else {
164						fmt_separated_by(" ELSE ")
165					},
166				),
167			);
168			if let Some(ref v) = self.close {
169				if use_separated {
170					// Separated format
171					f.push('\n');
172					write_sql!(f, fmt, "ELSE");
173					f.push('\n');
174					// For nested IFs, use same indent level; for top-level complex IFs, increment
175					if is_nested {
176						fmt.write_indent(f);
177						fmt_block(f, fmt, v, true);
178					} else {
179						let fmt = fmt.increment();
180						fmt.write_indent(f);
181						fmt_block(f, fmt, v, true);
182					}
183				} else {
184					write_sql!(f, fmt, " ELSE ");
185					fmt_block(f, fmt, v, false);
186				}
187			}
188		} else {
189			write_sql!(
190				f,
191				fmt,
192				"{}",
193				&Fmt::new(
194					self.exprs.iter().map(|args| {
195						Fmt::new(args, |(cond, then), f, fmt| {
196							if fmt.is_pretty() {
197								write_sql!(f, fmt, "IF {} THEN", CoverStmts(cond));
198								f.push('\n');
199								let fmt = fmt.increment();
200								fmt.write_indent(f);
201								if let Expr::IfElse(then) = then
202									&& then.bracketed()
203								{
204									write_sql!(f, fmt, "({then})");
205								} else {
206									write_sql!(f, fmt, "{then}");
207								}
208							} else {
209								write_sql!(f, fmt, "IF {} THEN ", CoverStmts(cond));
210								// Required to avoid confusion about which else if belongs to which
211								// statement.
212								if let Expr::IfElse(then) = then
213									&& then.bracketed()
214								{
215									write_sql!(f, fmt, "({then})");
216								} else {
217									write_sql!(f, fmt, "{then}");
218								}
219							}
220						})
221					}),
222					if fmt.is_pretty() {
223						fmt_separated_by("\nELSE ")
224					} else {
225						fmt_separated_by(" ELSE ")
226					},
227				),
228			);
229			if let Some(ref v) = self.close {
230				if fmt.is_pretty() {
231					f.push('\n');
232					write_sql!(f, fmt, "ELSE");
233					f.push('\n');
234					let fmt = fmt.increment();
235					fmt.write_indent(f);
236					write_sql!(f, fmt, "{}", CoverStmts(v));
237				} else {
238					write_sql!(f, fmt, " ELSE {}", CoverStmts(v));
239				}
240			}
241			if fmt.is_pretty() {
242				write_sql!(f, fmt, "END");
243			} else {
244				write_sql!(f, fmt, " END");
245			}
246		}
247	}
248}