Skip to main content

surql_parser/upstream/fmt/
mod.rs

1//! SurrealQL formatting utilities.
2mod escape;
3use crate::compat::fmt::fmt_non_finite_f64;
4use crate::upstream::sql;
5pub use escape::{
6	EscapeIdent, EscapeKwFreeIdent, EscapeKwIdent, EscapeObjectKey, EscapeRidKey, QuoteStr,
7};
8use std::cell::Cell;
9use std::fmt::Display;
10use surrealdb_types::{SqlFormat, ToSql};
11/// Implements ToSql by calling formatter on contents.
12pub struct Fmt<T, F> {
13	contents: Cell<Option<T>>,
14	formatter: F,
15}
16impl<T, F: Fn(T, &mut String, SqlFormat)> Fmt<T, F> {
17	pub fn new(t: T, formatter: F) -> Self {
18		Self {
19			contents: Cell::new(Some(t)),
20			formatter,
21		}
22	}
23}
24impl<T, F: Fn(T, &mut String, SqlFormat)> ToSql for Fmt<T, F> {
25	/// fmt is single-use only.
26	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
27		let contents = self
28			.contents
29			.replace(None)
30			.expect("only call Fmt::fmt once");
31		(self.formatter)(contents, f, fmt)
32	}
33}
34impl<I: IntoIterator<Item = T>, T: ToSql> Fmt<I, fn(I, &mut String, SqlFormat)> {
35	/// Formats values with a comma and a space separating them.
36	pub fn comma_separated(into_iter: I) -> Self {
37		Self::new(into_iter, fmt_comma_separated)
38	}
39	/// Formats values with a verbar and a space separating them.
40	pub fn verbar_separated(into_iter: I) -> Self {
41		Self::new(into_iter, fmt_verbar_separated)
42	}
43	/// Formats values with a comma and a space separating them or, if pretty
44	/// printing is in effect, a comma, a newline, and indentation.
45	pub fn pretty_comma_separated(into_iter: I) -> Self {
46		Self::new(into_iter, fmt_pretty_comma_separated)
47	}
48	/// Formats values with a new line separating them.
49	pub fn one_line_separated(into_iter: I) -> Self {
50		Self::new(into_iter, fmt_one_line_separated)
51	}
52}
53fn fmt_comma_separated<T: ToSql, I: IntoIterator<Item = T>>(
54	into_iter: I,
55	f: &mut String,
56	fmt: SqlFormat,
57) {
58	for (i, v) in into_iter.into_iter().enumerate() {
59		if i > 0 {
60			f.push_str(", ");
61		}
62		v.fmt_sql(f, fmt);
63	}
64}
65fn fmt_verbar_separated<T: ToSql, I: IntoIterator<Item = T>>(
66	into_iter: I,
67	f: &mut String,
68	fmt: SqlFormat,
69) {
70	for (i, v) in into_iter.into_iter().enumerate() {
71		if i > 0 {
72			f.push_str(" | ");
73		}
74		v.fmt_sql(f, fmt);
75	}
76}
77fn fmt_pretty_comma_separated<T: ToSql, I: IntoIterator<Item = T>>(
78	into_iter: I,
79	f: &mut String,
80	fmt: SqlFormat,
81) {
82	for (i, v) in into_iter.into_iter().enumerate() {
83		if i > 0 {
84			if fmt.is_pretty() {
85				f.push_str(",\n");
86			} else {
87				f.push_str(", ");
88			}
89		}
90		v.fmt_sql(f, fmt);
91	}
92}
93fn fmt_one_line_separated<T: ToSql, I: IntoIterator<Item = T>>(
94	into_iter: I,
95	f: &mut String,
96	fmt: SqlFormat,
97) {
98	for (i, v) in into_iter.into_iter().enumerate() {
99		if i > 0 {
100			f.push('\n');
101		}
102		v.fmt_sql(f, fmt);
103	}
104}
105/// Creates a formatting function that joins iterators with an arbitrary
106/// separator.
107pub fn fmt_separated_by<T: ToSql, I: IntoIterator<Item = T>>(
108	separator: impl Display,
109) -> impl Fn(I, &mut String, SqlFormat) {
110	move |into_iter: I, f: &mut String, fmt: SqlFormat| {
111		let separator = separator.to_string();
112		for (i, v) in into_iter.into_iter().enumerate() {
113			if i > 0 {
114				f.push_str(&separator);
115			}
116			v.fmt_sql(f, fmt);
117		}
118	}
119}
120pub struct CoverStmts<'a>(pub &'a sql::Expr);
121impl ToSql for CoverStmts<'_> {
122	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
123		match self.0 {
124			sql::Expr::Literal(_)
125			| sql::Expr::Param(_)
126			| sql::Expr::Idiom(_)
127			| sql::Expr::Table(_)
128			| sql::Expr::Mock(_)
129			| sql::Expr::Block(_)
130			| sql::Expr::Constant(_)
131			| sql::Expr::Prefix { .. }
132			| sql::Expr::Postfix { .. }
133			| sql::Expr::Binary { .. }
134			| sql::Expr::FunctionCall(_)
135			| sql::Expr::Closure(_)
136			| sql::Expr::Break
137			| sql::Expr::Continue
138			| sql::Expr::Throw(_) => self.0.fmt_sql(f, fmt),
139			sql::Expr::Return(x) => {
140				if x.fetch.is_some() {
141					f.push('(');
142					self.0.fmt_sql(f, fmt);
143					f.push(')')
144				} else {
145					self.0.fmt_sql(f, fmt);
146				}
147			}
148			sql::Expr::IfElse(_)
149			| sql::Expr::Select(_)
150			| sql::Expr::Create(_)
151			| sql::Expr::Update(_)
152			| sql::Expr::Upsert(_)
153			| sql::Expr::Delete(_)
154			| sql::Expr::Relate(_)
155			| sql::Expr::Insert(_)
156			| sql::Expr::Define(_)
157			| sql::Expr::Remove(_)
158			| sql::Expr::Rebuild(_)
159			| sql::Expr::Alter(_)
160			| sql::Expr::Info(_)
161			| sql::Expr::Foreach(_)
162			| sql::Expr::Let(_)
163			| sql::Expr::Sleep(_)
164			| sql::Expr::Explain { .. } => {
165				f.push('(');
166				self.0.fmt_sql(f, fmt);
167				f.push(')')
168			}
169		}
170	}
171}
172pub struct Float(pub f64);
173impl ToSql for Float {
174	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
175		match fmt_non_finite_f64(self.0) {
176			Some(special) => f.push_str(special),
177			None => {
178				self.0.fmt_sql(f, fmt);
179				f.push('f');
180			}
181		}
182	}
183}