Skip to main content

surql_parser/upstream/sql/statements/
ifelse.rs

1use crate::upstream::fmt::{CoverStmts, Fmt, fmt_separated_by};
2use crate::upstream::sql::Expr;
3use surrealdb_types::{SqlFormat, ToSql, write_sql};
4#[derive(Clone, Debug, Default, Eq, PartialEq)]
5#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
6pub struct IfelseStatement {
7	/// The first if condition followed by a body, followed by any number of
8	/// else if's
9	#[cfg_attr(
10        feature = "arbitrary",
11        arbitrary(with = crate::upstream::sql::arbitrary::atleast_one)
12    )]
13	pub exprs: Vec<(Expr, Expr)>,
14	/// the final else body, if there is one
15	pub close: Option<Expr>,
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
22				.close
23				.as_ref()
24				.map(|v| matches!(v, Expr::Block(_)))
25				.unwrap_or(true)
26	}
27}
28impl ToSql for IfelseStatement {
29	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
30		if self.bracketed() {
31			let is_simple_block = |expr: &Expr| -> bool {
32				if let Expr::Block(block) = expr {
33					block.0.iter().all(|stmt| {
34						matches!(stmt, Expr::Literal(_) | Expr::Param(_) | Expr::Idiom(_))
35					})
36				} else {
37					false
38				}
39			};
40			let has_complex_multi = self.exprs.iter().any(|(_, expr)| {
41				matches!(
42					expr, Expr::Block(block) if block.0.len() > 1 && !
43					is_simple_block(expr)
44				)
45			}) || self
46				.close
47				.as_ref()
48				.map(|expr| {
49					matches!(
50						expr, Expr::Block(block) if block.0.len() > 1 && !
51						is_simple_block(expr)
52					)
53				})
54				.unwrap_or(false);
55			let fmt_block = |f: &mut String, fmt: SqlFormat, expr: &Expr, use_separated: bool| {
56				if let Expr::Block(block) = expr {
57					match block.0.len() {
58						0 => f.push_str("{;}"),
59						1 if !use_separated => {
60							f.push_str("{ ");
61							block.0[0].fmt_sql(f, SqlFormat::SingleLine);
62							f.push_str(" }");
63						}
64						1 => {
65							f.push('{');
66							f.push(' ');
67							block.0[0].fmt_sql(f, SqlFormat::SingleLine);
68							f.push(' ');
69							f.push('}');
70						}
71						_ => {
72							let needs_indent = is_simple_block(expr);
73							if fmt.is_pretty() && !needs_indent {
74								f.push_str("{\n\n");
75								let inner_fmt = fmt.increment();
76								for (i, stmt) in block.0.iter().enumerate() {
77									if i > 0 {
78										f.push('\n');
79										f.push('\n');
80									}
81									inner_fmt.write_indent(f);
82									stmt.fmt_sql(f, SqlFormat::SingleLine);
83									f.push(';');
84								}
85								f.push('\n');
86								fmt.write_indent(f);
87								f.push('\n');
88								f.push('}');
89							} else if fmt.is_pretty() {
90								f.push_str("{\n\n");
91								for (i, stmt) in block.0.iter().enumerate() {
92									if i > 0 {
93										f.push('\n');
94									}
95									f.push('\t');
96									stmt.fmt_sql(f, SqlFormat::SingleLine);
97									f.push(';');
98								}
99								f.push_str("\n}");
100							} else {
101								f.push_str("{\n");
102								for (i, stmt) in block.0.iter().enumerate() {
103									if i > 0 {
104										f.push('\n');
105									}
106									if needs_indent {
107										f.push('\t');
108									}
109									stmt.fmt_sql(f, SqlFormat::SingleLine);
110									f.push(';');
111								}
112								f.push_str("\n}");
113							}
114						}
115					}
116				} else {
117					expr.fmt_sql(f, fmt);
118				}
119			};
120			let is_nested = matches!(fmt, SqlFormat::Indented(level) if level > 0);
121			let use_separated = fmt.is_pretty() && (has_complex_multi || is_nested);
122			write_sql!(
123				f,
124				fmt,
125				"{}",
126				&Fmt::new(
127					self.exprs.iter().map(|args| {
128						Fmt::new(args, |(cond, then), f, fmt| {
129							if use_separated {
130								write_sql!(f, fmt, "IF {}", CoverStmts(cond));
131								f.push('\n');
132								if is_nested {
133									fmt.write_indent(f);
134									fmt_block(f, fmt, then, true);
135								} else {
136									let fmt = fmt.increment();
137									fmt.write_indent(f);
138									fmt_block(f, fmt, then, true);
139								}
140							} else {
141								write_sql!(f, fmt, "IF {} ", CoverStmts(cond));
142								fmt_block(f, fmt, then, false);
143							}
144						})
145					}),
146					if use_separated {
147						fmt_separated_by("\nELSE ")
148					} else {
149						fmt_separated_by(" ELSE ")
150					},
151				),
152			);
153			if let Some(ref v) = self.close {
154				if use_separated {
155					f.push('\n');
156					write_sql!(f, fmt, "ELSE");
157					f.push('\n');
158					if is_nested {
159						fmt.write_indent(f);
160						fmt_block(f, fmt, v, true);
161					} else {
162						let fmt = fmt.increment();
163						fmt.write_indent(f);
164						fmt_block(f, fmt, v, true);
165					}
166				} else {
167					write_sql!(f, fmt, " ELSE ");
168					fmt_block(f, fmt, v, false);
169				}
170			}
171		} else {
172			write_sql!(
173				f,
174				fmt,
175				"{}",
176				&Fmt::new(
177					self.exprs.iter().map(|args| {
178						Fmt::new(args, |(cond, then), f, fmt| {
179							if fmt.is_pretty() {
180								write_sql!(f, fmt, "IF {} THEN", CoverStmts(cond));
181								f.push('\n');
182								let fmt = fmt.increment();
183								fmt.write_indent(f);
184								if let Expr::IfElse(then) = then
185									&& then.bracketed()
186								{
187									write_sql!(f, fmt, "({then})");
188								} else {
189									write_sql!(f, fmt, "{then}");
190								}
191							} else {
192								write_sql!(f, fmt, "IF {} THEN ", CoverStmts(cond));
193								if let Expr::IfElse(then) = then
194									&& then.bracketed()
195								{
196									write_sql!(f, fmt, "({then})");
197								} else {
198									write_sql!(f, fmt, "{then}");
199								}
200							}
201						})
202					}),
203					if fmt.is_pretty() {
204						fmt_separated_by("\nELSE ")
205					} else {
206						fmt_separated_by(" ELSE ")
207					},
208				),
209			);
210			if let Some(ref v) = self.close {
211				if fmt.is_pretty() {
212					f.push('\n');
213					write_sql!(f, fmt, "ELSE");
214					f.push('\n');
215					let fmt = fmt.increment();
216					fmt.write_indent(f);
217					write_sql!(f, fmt, "{}", CoverStmts(v));
218				} else {
219					write_sql!(f, fmt, " ELSE {}", CoverStmts(v));
220				}
221			}
222			if fmt.is_pretty() {
223				write_sql!(f, fmt, "END");
224			} else {
225				write_sql!(f, fmt, " END");
226			}
227		}
228	}
229}