1use itertools::Itertools;
33use std::fmt::{self, Display};
34use std::marker::PhantomData;
35
36mod predicates;
37pub use predicates::*;
38mod data_type;
39pub use data_type::*;
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
47pub struct ObjectConcat<'i>(pub &'i [&'i str]);
48
49impl fmt::Display for ObjectConcat<'_> {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 if self.0.iter().any(|o| o.contains("'") || o.contains("\\")) {
52 return Err(fmt::Error);
54 }
55
56 if self.0.iter().any(|o| o.contains(" ") || o.contains("\"")) {
57 f.write_str("\"")?;
58 for part in self.0.iter().flat_map(|o| o.split("\"").intersperse("\"\"")) {
59 f.write_str(part)?;
60 }
61 f.write_str("\"")?;
62 } else {
63 for o in self.0.iter() {
64 f.write_str(o)?;
65 }
66 }
67 Ok(())
68 }
69}
70
71pub struct ObjectConcatDisplay<'i>(Box<[&'i str]>);
74
75impl<'i> ObjectConcatDisplay<'i> {
76 pub fn as_quoted_data(self) -> QuotedDataConcatDisplay<'i> {
77 self.into()
78 }
79}
80
81impl fmt::Display for ObjectConcatDisplay<'_> {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 ObjectConcat(&self.0).fmt(f)
84 }
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
93pub struct QuotedDataConcat<'i>(pub &'i [&'i str]);
94
95impl fmt::Display for QuotedDataConcat<'_> {
96 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97 f.write_str("'")?;
98 for part in self.0.iter().flat_map(|o| o.split("'").intersperse("''")) {
99 for part in part.split("\\").intersperse("\\\\") {
100 f.write_str(part)?;
101 }
102 }
103 f.write_str("'")?;
104 Ok(())
105 }
106}
107
108pub struct QuotedDataConcatDisplay<'i>(Box<[&'i str]>);
111
112impl fmt::Display for QuotedDataConcatDisplay<'_> {
113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 QuotedDataConcat(&self.0).fmt(f)
115 }
116}
117
118impl<'i> From<ObjectConcatDisplay<'i>> for QuotedDataConcatDisplay<'i> {
119 fn from(v: ObjectConcatDisplay<'i>) -> QuotedDataConcatDisplay<'i> {
120 QuotedDataConcatDisplay(v.0)
121 }
122}
123
124#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
126pub struct Object<'i>(pub &'i str);
127
128impl<'i> Object<'i> {
129 pub fn as_str(&self) -> &'i str {
131 self.0
132 }
133
134 pub fn as_quoted_data(&self) -> QuotedDataConcatDisplay<'i> {
136 QuotedDataConcatDisplay(Box::new([self.as_str()]))
137 }
138}
139
140impl<'i> From<&'i str> for Object<'i> {
141 fn from(value: &'i str) -> Object<'i> {
142 Object(value.into())
143 }
144}
145
146impl fmt::Display for Object<'_> {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 ObjectConcat(&[self.0]).fmt(f)
149 }
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
154pub struct QuotedData<'i>(pub &'i str);
155
156impl<'i> From<&'i str> for QuotedData<'i> {
157 fn from(value: &'i str) -> QuotedData<'i> {
158 QuotedData(value)
159 }
160}
161
162impl<'i> QuotedData<'i> {
163 pub fn map<F>(self, f: F) -> MapQuotedData<'i, F>
164 where
165 F: Fn(&'i str) -> String,
166 {
167 MapQuotedData(self.0, f)
168 }
169
170 pub fn as_str(&self) -> &'i str {
172 self.0
173 }
174}
175
176impl fmt::Display for QuotedData<'_> {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 QuotedDataConcat(&[self.0]).fmt(f)
179 }
180}
181
182pub struct MapQuotedData<'i, F>(&'i str, F);
184
185impl<'i, F> fmt::Display for MapQuotedData<'i, F>
186where
187 F: Fn(&'i str) -> String,
188{
189 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190 let data = self.1(self.0);
191 QuotedData(&data).fmt(f)
192 }
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
197pub struct Schema<'i>(pub Object<'i>);
198
199impl<'i> Schema<'i> {
200 pub fn as_str(&self) -> &'i str {
202 self.0.as_str()
203 }
204
205 pub fn as_quoted_data(&self) -> QuotedDataConcatDisplay<'i> {
207 self.0.as_quoted_data()
208 }
209}
210
211impl<'i, O: Into<Object<'i>> > From<O> for Schema<'i> {
212 fn from(value: O) -> Schema<'i> {
213 Schema(value.into())
214 }
215}
216
217impl fmt::Display for Schema<'_> {
218 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 self.0.fmt(f)
220 }
221}
222
223#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
225pub struct Table<'i>(pub Object<'i>);
226
227impl<'i> Table<'i> {
228 pub fn with_schema(self, schema: impl Into<Schema<'i>>) -> SchemaTable<'i> {
230 SchemaTable(schema.into(), self)
231 }
232
233 pub fn with_postfix(&self, postfix: &'i str) -> ObjectConcatDisplay<'i> {
235 ObjectConcatDisplay(Box::new([self.as_str(), postfix]))
236 }
237
238 pub fn with_postfix_sep(&self, postfix: &'i str, separator: &'i str) -> ObjectConcatDisplay<'i> {
241 ObjectConcatDisplay(Box::new([self.as_str(), separator, postfix]))
242 }
243
244 pub fn as_str(&self) -> &'i str {
246 self.0.as_str()
247 }
248
249 pub fn as_quoted_data(&self) -> QuotedDataConcatDisplay<'i> {
251 self.0.as_quoted_data()
252 }
253}
254
255impl<'i, O: Into<Object<'i>> > From<O> for Table<'i> {
256 fn from(table: O) -> Table<'i> {
257 Table(table.into())
258 }
259}
260
261impl fmt::Display for Table<'_> {
262 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263 self.0.fmt(f)
264 }
265}
266
267#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
269pub struct SchemaTable<'i>(pub Schema<'i>, pub Table<'i>);
270
271impl<'i> SchemaTable<'i> {
272 pub fn schema(&self) -> Schema<'i> {
274 self.0
275 }
276
277 pub fn table(&self) -> Table<'i> {
279 self.1
280 }
281
282 fn as_array(&self) -> [&'i str; 3] {
283 [self.0.as_str(), ".", self.1.as_str()]
284 }
285
286 pub fn with_postfix(&self, postfix: &'i str) -> impl Display + 'i {
288 let a = self.as_array();
289 ObjectConcatDisplay(Box::new([a[0], a[1], a[2], postfix]))
290 }
291
292 pub fn with_postfix_sep(&self, postfix: &'i str, separator: &'i str) -> ObjectConcatDisplay<'i> {
295 let a = self.as_array();
296 ObjectConcatDisplay(Box::new([a[0], a[1], a[2], separator, postfix]))
297 }
298
299 pub fn as_quoted_data(&self) -> QuotedDataConcatDisplay<'i> {
301 QuotedDataConcatDisplay(Box::new(self.as_array()))
302 }
303}
304
305impl<'i, S: Into<Schema<'i>>, T: Into<Table<'i>>> From<(S, T)> for SchemaTable<'i> {
306 fn from((schema, table): (S, T)) -> SchemaTable<'i> {
307 SchemaTable(schema.into(), table.into())
308 }
309}
310
311impl fmt::Display for SchemaTable<'_> {
312 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313 ObjectConcat(&self.as_array()).fmt(f)
314 }
315}
316
317#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
319pub struct Column<'i>(pub Object<'i>);
320
321impl<'i> Column<'i> {
322 pub fn as_str(&self) -> &'i str {
324 self.0.as_str()
325 }
326
327 pub fn as_quoted_data(&self) -> QuotedDataConcatDisplay<'i> {
329 self.0.as_quoted_data()
330 }
331}
332
333impl<'i, O: Into<Object<'i>>> From<O> for Column<'i> {
334 fn from(value: O) -> Column<'i> {
335 Column(value.into())
336 }
337}
338
339impl fmt::Display for Column<'_> {
340 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
341 self.0.fmt(f)
342 }
343}
344
345#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
347pub struct ColumnType<D: Dialect>(pub Object<'static>, pub PhantomData<D>);
348
349impl<D: Dialect> ColumnType<D> {
350 pub fn as_str(&self) -> &'static str {
352 self.0.as_str()
353 }
354}
355
356impl<D, O: Into<Object<'static>>> From<O> for ColumnType<D> where D: Dialect {
357 fn from(column_type: O) -> ColumnType<D> {
358 ColumnType(column_type.into(), PhantomData)
359 }
360}
361
362impl<D: Dialect> fmt::Display for ColumnType<D> {
363 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
364 self.0.fmt(f)
365 }
366}
367
368#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
370pub struct ColumnSchema<'i, D: Dialect>(pub Column<'i>, pub ColumnType<D>);
371
372impl<'i, D: Dialect> ColumnSchema<'i, D> {
373 pub fn column(&self) -> &Column<'i> {
375 &self.0
376 }
377
378 pub fn column_type(&self) -> &ColumnType<D> {
380 &self.1
381 }
382}
383
384impl<'i, D: Dialect, C: Into<Column<'i>>, T: Into<ColumnType<D>>> From<(C, T)> for ColumnSchema<'i, D> {
385 fn from((name, r#type): (C, T)) -> ColumnSchema<'i, D> {
386 ColumnSchema(name.into(), r#type.into())
387 }
388}
389
390impl<D: Dialect> fmt::Display for ColumnSchema<'_, D> {
391 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392 write!(f, "{} {}", self.0, self.1)
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn build_select() {
402 assert_eq!(
403 r#"SELECT "foo bar" FROM foo.baz_quix WHERE blah = 'hello ''world'' foo'"#,
404 &format!(
405 "SELECT {} FROM {} WHERE {} = {}",
406 Column("foo bar".into()),
407 SchemaTable("foo".into(), "baz".into()).with_postfix("_quix"),
408 Column("blah".into()),
409 QuotedData("hello 'world' foo")
410 )
411 )
412 }
413
414 #[test]
415 fn build_object_concat() {
416 assert_eq!(
417 r#""hello ""world"" foo_""quix""""#,
418 &format!(
419 "{}",
420 ObjectConcat(&[r#"hello "world" foo"#, r#"_"quix""#])
421 )
422 );
423
424 assert_eq!(
425 "foo_bar_baz",
426 &format!(
427 "{}",
428 ObjectConcat(&["foo_", "bar", "_baz"])
429 )
430 );
431 }
432}