1use std::sync::Arc;
4
5pub use surrealdb_types_derive::write_sql;
6
7use crate as surrealdb_types;
8use crate::utils::escape::QuoteStr;
9
10pub trait ToSql {
32 fn to_sql(&self) -> String {
34 let mut f = String::new();
35 self.fmt_sql(&mut f, SqlFormat::SingleLine);
36 f
37 }
38
39 fn to_sql_pretty(&self) -> String {
41 let mut f = String::new();
42 self.fmt_sql(&mut f, SqlFormat::Indented(0));
43 f
44 }
45
46 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat);
48}
49
50#[derive(Debug, Clone, Copy)]
52pub enum SqlFormat {
53 SingleLine,
55 Indented(u8),
57}
58
59impl SqlFormat {
60 pub fn is_pretty(&self) -> bool {
62 matches!(self, SqlFormat::Indented(_))
63 }
64
65 pub fn increment(&self) -> Self {
67 match self {
68 SqlFormat::SingleLine => SqlFormat::SingleLine,
69 SqlFormat::Indented(level) => SqlFormat::Indented(level.saturating_add(1)),
70 }
71 }
72
73 pub fn write_indent(&self, f: &mut String) {
75 if let SqlFormat::Indented(level) = self {
76 for _ in 0..*level {
77 f.push('\t');
78 }
79 }
80 }
81
82 pub fn write_separator(&self, f: &mut String) {
84 match self {
85 SqlFormat::SingleLine => f.push_str(", "),
86 SqlFormat::Indented(_) => {
87 f.push(',');
88 f.push('\n');
89 self.write_indent(f);
90 }
91 }
92 }
93}
94
95pub fn fmt_sql_comma_separated<T: ToSql>(items: &[T], f: &mut String, fmt: SqlFormat) {
97 if fmt.is_pretty() && !items.is_empty() {
98 f.push('\n');
99 fmt.write_indent(f);
100 }
101 for (i, item) in items.iter().enumerate() {
102 if i > 0 {
103 fmt.write_separator(f);
104 }
105 item.fmt_sql(f, fmt);
106 }
107 if fmt.is_pretty() && !items.is_empty() {
108 f.push('\n');
109 if let SqlFormat::Indented(level) = fmt
111 && level > 0
112 {
113 for _ in 0..(level - 1) {
114 f.push('\t');
115 }
116 }
117 }
118}
119
120pub fn fmt_sql_key_value<'a, V: ToSql + 'a>(
122 pairs: impl IntoIterator<Item = (impl AsRef<str>, &'a V)>,
123 f: &mut String,
124 fmt: SqlFormat,
125) {
126 use crate::utils::escape::EscapeObjectKey;
127
128 let pairs: Vec<_> = pairs.into_iter().collect();
129
130 if fmt.is_pretty() && !pairs.is_empty() {
131 f.push('\n');
132 fmt.write_indent(f);
133 }
134 for (i, (key, value)) in pairs.iter().enumerate() {
135 if i > 0 {
136 fmt.write_separator(f);
137 }
138 write_sql!(f, fmt, "{}: {}", EscapeObjectKey(key.as_ref()), value);
139 }
140 if fmt.is_pretty() && !pairs.is_empty() {
141 f.push('\n');
142 if let SqlFormat::Indented(level) = fmt
144 && level > 0
145 {
146 for _ in 0..(level - 1) {
147 f.push('\t');
148 }
149 }
150 }
151}
152
153impl ToSql for String {
154 #[inline]
155 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
156 f.push_str(self.as_str());
157 }
158}
159
160impl ToSql for str {
161 #[inline]
162 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
163 f.push_str(self);
164 }
165}
166
167impl ToSql for &str {
168 #[inline]
169 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
170 f.push_str(self);
171 }
172}
173
174impl ToSql for char {
175 #[inline]
176 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
177 f.push(*self);
178 }
179}
180
181impl ToSql for bool {
182 #[inline]
183 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
184 f.push_str(if *self {
185 "true"
186 } else {
187 "false"
188 })
189 }
190}
191
192macro_rules! impl_to_sql_for_numeric {
193 ($($t:ty),+) => {
194 $(
195 impl ToSql for $t {
196 #[inline]
197 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
198 f.push_str(&self.to_string())
199 }
200 }
201 )+
202 };
203}
204
205impl_to_sql_for_numeric!(u8, u16, u32, u64, i8, i16, i32, i64, usize, isize, f32, f64);
206
207impl<T: ToSql> ToSql for &T {
208 #[inline]
209 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
210 (**self).fmt_sql(f, fmt)
211 }
212}
213
214impl<T: ToSql + ?Sized> ToSql for Box<T> {
216 #[inline]
217 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
218 (**self).fmt_sql(f, fmt)
219 }
220}
221
222impl<T: ToSql + ?Sized> ToSql for Arc<T> {
224 #[inline]
225 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
226 (**self).fmt_sql(f, fmt)
227 }
228}
229
230impl ToSql for uuid::Uuid {
231 #[inline]
232 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
233 f.push('u');
234 QuoteStr(&self.to_string()).fmt_sql(f, fmt);
235 }
236}
237
238impl ToSql for rust_decimal::Decimal {
239 #[inline]
240 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
241 self.to_string().fmt_sql(f, fmt);
242 f.push_str("dec");
243 }
244}