1use std::fmt;
2
3use crate::types::SurrealRecord;
4
5pub trait DynExpr: fmt::Debug + Send + Sync {
10 fn render_dyn(&self, buf: &mut String);
11}
12
13pub type DynExprBox = Box<dyn DynExpr>;
16
17impl DynExpr for Box<dyn DynExpr> {
18 fn render_dyn(&self, buf: &mut String) {
19 (**self).render_dyn(buf);
20 }
21}
22
23pub trait Expr: DynExpr {
28 fn ty_hint(&self) -> &'static str;
29}
30
31impl<E: DynExpr> Expr for E {
34 fn ty_hint(&self) -> &'static str {
35 "any"
36 }
37}
38
39pub trait SurrealQL: fmt::Debug + Clone + Send + Sync + 'static {
44 fn surreal_type() -> &'static str;
45 fn render_literal(value: &Self, buf: &mut String);
46}
47
48impl SurrealQL for String {
49 fn surreal_type() -> &'static str {
50 "string"
51 }
52 fn render_literal(value: &Self, buf: &mut String) {
53 let escaped = value.replace('\\', "\\\\").replace('\'', "\\'");
54 buf.push('\'');
55 buf.push_str(&escaped);
56 buf.push('\'');
57 }
58}
59
60impl SurrealQL for bool {
61 fn surreal_type() -> &'static str {
62 "bool"
63 }
64 fn render_literal(value: &Self, buf: &mut String) {
65 buf.push_str(if *value { "true" } else { "false" });
66 }
67}
68
69macro_rules! surreal_display {
70 ($t:ty, $name:literal) => {
71 impl SurrealQL for $t {
72 fn surreal_type() -> &'static str {
73 $name
74 }
75 fn render_literal(value: &Self, buf: &mut String) {
76 use std::fmt::Write;
77 let _ = write!(buf, "{value}");
78 }
79 }
80 };
81}
82surreal_display!(i64, "int");
83surreal_display!(i32, "int");
84surreal_display!(i16, "int");
85surreal_display!(i8, "int");
86surreal_display!(f64, "float");
87surreal_display!(f32, "float");
88surreal_display!(u32, "int");
89surreal_display!(u64, "int");
90surreal_display!(u16, "int");
91surreal_display!(u8, "int");
92
93impl SurrealQL for chrono::DateTime<chrono::Utc> {
94 fn surreal_type() -> &'static str {
95 "datetime"
96 }
97 fn render_literal(value: &Self, buf: &mut String) {
98 buf.push('\'');
99 buf.push_str(&value.to_rfc3339());
100 buf.push('\'');
101 }
102}
103
104impl SurrealQL for uuid::Uuid {
105 fn surreal_type() -> &'static str {
106 "uuid"
107 }
108 fn render_literal(value: &Self, buf: &mut String) {
109 use std::fmt::Write;
110 buf.push('\'');
111 let _ = write!(buf, "{value}");
112 buf.push('\'');
113 }
114}
115
116impl SurrealQL for serde_json::Value {
117 fn surreal_type() -> &'static str {
118 "object"
119 }
120 fn render_literal(value: &Self, buf: &mut String) {
121 use std::fmt::Write;
125 let _ = write!(buf, "{value}");
126 }
127}
128
129impl<T: SurrealQL> SurrealQL for Option<T> {
130 fn surreal_type() -> &'static str {
131 T::surreal_type()
132 }
133 fn render_literal(value: &Self, buf: &mut String) {
134 match value {
135 Some(v) => T::render_literal(v, buf),
136 None => buf.push_str("NONE"),
137 }
138 }
139}
140
141impl<T: crate::types::SurrealRecord> SurrealQL for crate::types::Thing<T> {
142 fn surreal_type() -> &'static str {
143 "record"
144 }
145 fn render_literal(value: &Self, buf: &mut String) {
146 use std::fmt::Write;
147 let _ = write!(buf, "{}:{}", T::table_name(), value.key);
148 }
149}
150
151#[derive(Debug, Clone)]
156pub struct Literal<V: SurrealQL>(pub V);
157
158impl<V: SurrealQL> DynExpr for Literal<V> {
159 fn render_dyn(&self, buf: &mut String) {
160 V::render_literal(&self.0, buf);
161 }
162}
163
164pub struct Column<T: SurrealRecord, V: SurrealQL> {
169 pub name: &'static str,
170 pub surreal_type: &'static str,
171 pub _marker: std::marker::PhantomData<(T, V)>,
172}
173
174impl<T: SurrealRecord, V: SurrealQL> std::fmt::Debug for Column<T, V> {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 f.debug_struct("Column").field("name", &self.name).finish()
177 }
178}
179
180impl<T: SurrealRecord, V: SurrealQL> Clone for Column<T, V> {
181 fn clone(&self) -> Self {
182 *self
183 }
184}
185impl<T: SurrealRecord, V: SurrealQL> Copy for Column<T, V> {}
186
187impl<T: SurrealRecord, V: SurrealQL> DynExpr for Column<T, V> {
188 fn render_dyn(&self, buf: &mut String) {
189 buf.push_str(self.name);
190 }
191}
192
193#[derive(Debug, Clone, Copy)]
201pub struct Ident(pub &'static str);
202
203pub fn ident(name: &'static str) -> Ident {
205 Ident(name)
206}
207
208impl Ident {
209 fn dyn_box(&self) -> Box<dyn DynExpr> {
210 Box::new(Raw(self.0.to_string()))
211 }
212
213 pub fn eq<V: SurrealQL>(&self, v: V) -> EqExpr {
214 EqExpr {
215 left: self.dyn_box(),
216 right: Box::new(Literal(v)),
217 }
218 }
219 pub fn ne<V: SurrealQL>(&self, v: V) -> NeExpr {
220 NeExpr {
221 left: self.dyn_box(),
222 right: Box::new(Literal(v)),
223 }
224 }
225 pub fn gt<V: SurrealQL>(&self, v: V) -> GtExpr {
226 GtExpr {
227 left: self.dyn_box(),
228 right: Box::new(Literal(v)),
229 }
230 }
231 pub fn lt<V: SurrealQL>(&self, v: V) -> LtExpr {
232 LtExpr {
233 left: self.dyn_box(),
234 right: Box::new(Literal(v)),
235 }
236 }
237 pub fn gte<V: SurrealQL>(&self, v: V) -> GteExpr {
238 GteExpr {
239 left: self.dyn_box(),
240 right: Box::new(Literal(v)),
241 }
242 }
243 pub fn lte<V: SurrealQL>(&self, v: V) -> LteExpr {
244 LteExpr {
245 left: self.dyn_box(),
246 right: Box::new(Literal(v)),
247 }
248 }
249 pub fn contains<V: SurrealQL>(&self, v: V) -> ContainsExpr {
250 ContainsExpr {
251 haystack: self.dyn_box(),
252 needle: Box::new(Literal(v)),
253 }
254 }
255 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
257 EqExpr {
258 left: self.dyn_box(),
259 right: Box::new(rhs),
260 }
261 }
262 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
263 NeExpr {
264 left: self.dyn_box(),
265 right: Box::new(rhs),
266 }
267 }
268 pub fn is_none(&self) -> Raw {
270 Raw(format!("{} IS NONE", self.0))
271 }
272}
273
274impl DynExpr for Ident {
275 fn render_dyn(&self, buf: &mut String) {
276 buf.push_str(self.0);
277 }
278}
279
280#[derive(Debug, Clone)]
287pub struct Raw(pub String);
288
289impl Raw {
290 pub fn new(s: impl Into<String>) -> Self {
291 Self(s.into())
292 }
293}
294
295impl DynExpr for Raw {
296 fn render_dyn(&self, buf: &mut String) {
297 buf.push_str(&self.0);
298 }
299}
300
301#[derive(Debug, Clone)]
303pub struct NoneLit;
304impl DynExpr for NoneLit {
305 fn render_dyn(&self, buf: &mut String) {
306 buf.push_str("NONE");
307 }
308}
309
310#[derive(Debug)]
317pub struct RecordLink {
318 table: &'static str,
319 key: Box<dyn DynExpr>,
320}
321
322impl RecordLink {
323 pub fn new<V: SurrealQL>(table: &'static str, key: V) -> Self {
325 Self {
326 table,
327 key: Box::new(Literal(key)),
328 }
329 }
330 pub fn from_expr(table: &'static str, key: impl DynExpr + 'static) -> Self {
332 Self {
333 table,
334 key: Box::new(key),
335 }
336 }
337}
338
339impl DynExpr for RecordLink {
340 fn render_dyn(&self, buf: &mut String) {
341 buf.push_str("type::record('");
342 buf.push_str(self.table);
343 buf.push_str("', ");
344 self.key.render_dyn(buf);
345 buf.push(')');
346 }
347}
348
349#[derive(Debug)]
355pub struct Func {
356 name: &'static str,
357 args: Vec<Box<dyn DynExpr>>,
358}
359
360impl Func {
361 pub fn new(name: &'static str, args: Vec<Box<dyn DynExpr>>) -> Self {
362 Self { name, args }
363 }
364 pub fn of(name: &'static str, ident: &'static str) -> Self {
367 Self {
368 name,
369 args: vec![Box::new(Raw(ident.to_string()))],
370 }
371 }
372}
373
374impl DynExpr for Func {
375 fn render_dyn(&self, buf: &mut String) {
376 buf.push_str(self.name);
377 buf.push('(');
378 for (i, a) in self.args.iter().enumerate() {
379 if i > 0 {
380 buf.push_str(", ");
381 }
382 a.render_dyn(buf);
383 }
384 buf.push(')');
385 }
386}
387
388macro_rules! binop {
393 ($name:ident, $op:literal) => {
394 #[derive(Debug)]
395 pub struct $name {
396 pub(crate) left: Box<dyn DynExpr>,
397 pub(crate) right: Box<dyn DynExpr>,
398 }
399 impl DynExpr for $name {
400 fn render_dyn(&self, buf: &mut String) {
401 self.left.render_dyn(buf);
402 buf.push(' ');
403 buf.push_str($op);
404 buf.push(' ');
405 self.right.render_dyn(buf);
406 }
407 }
408 };
409}
410
411binop!(EqExpr, "=");
412binop!(NeExpr, "!=");
413binop!(GtExpr, ">");
414binop!(LtExpr, "<");
415binop!(GteExpr, ">=");
416binop!(LteExpr, "<=");
417binop!(AndExpr, "AND");
418binop!(OrExpr, "OR");
419
420#[derive(Debug)]
421pub struct NotExpr {
422 pub(crate) inner: Box<dyn DynExpr>,
423}
424
425impl DynExpr for NotExpr {
426 fn render_dyn(&self, buf: &mut String) {
427 buf.push_str("NOT ");
428 self.inner.render_dyn(buf);
429 }
430}
431
432#[derive(Debug)]
433pub struct ContainsExpr {
434 pub(crate) haystack: Box<dyn DynExpr>,
435 pub(crate) needle: Box<dyn DynExpr>,
436}
437
438impl DynExpr for ContainsExpr {
439 fn render_dyn(&self, buf: &mut String) {
440 self.haystack.render_dyn(buf);
441 buf.push_str(" CONTAINS ");
442 self.needle.render_dyn(buf);
443 }
444}
445
446impl<T: SurrealRecord, V: SurrealQL> Column<T, V> {
451 pub fn eq(&self, value: V) -> EqExpr {
452 EqExpr {
453 left: self.dyn_box(),
454 right: Box::new(Literal(value)),
455 }
456 }
457 pub fn ne(&self, value: V) -> NeExpr {
458 NeExpr {
459 left: self.dyn_box(),
460 right: Box::new(Literal(value)),
461 }
462 }
463 pub fn gt(&self, value: V) -> GtExpr {
464 GtExpr {
465 left: self.dyn_box(),
466 right: Box::new(Literal(value)),
467 }
468 }
469 pub fn lt(&self, value: V) -> LtExpr {
470 LtExpr {
471 left: self.dyn_box(),
472 right: Box::new(Literal(value)),
473 }
474 }
475 pub fn gte(&self, value: V) -> GteExpr {
476 GteExpr {
477 left: self.dyn_box(),
478 right: Box::new(Literal(value)),
479 }
480 }
481 pub fn lte(&self, value: V) -> LteExpr {
482 LteExpr {
483 left: self.dyn_box(),
484 right: Box::new(Literal(value)),
485 }
486 }
487 pub fn contains(&self, value: V) -> ContainsExpr {
488 ContainsExpr {
489 haystack: self.dyn_box(),
490 needle: Box::new(Literal(value)),
491 }
492 }
493
494 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
497 EqExpr {
498 left: self.dyn_box(),
499 right: Box::new(rhs),
500 }
501 }
502 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
503 NeExpr {
504 left: self.dyn_box(),
505 right: Box::new(rhs),
506 }
507 }
508 pub fn is_none(&self) -> Raw {
510 Raw(format!("{} IS NONE", self.name))
511 }
512
513 fn dyn_box(&self) -> Box<dyn DynExpr> {
514 Box::new(Self {
515 name: self.name,
516 surreal_type: self.surreal_type,
517 _marker: self._marker,
518 })
519 }
520}
521
522macro_rules! combinators {
524 ($($t:ty),* $(,)?) => {$(
525 impl $t {
526 pub fn and(self, other: impl DynExpr + 'static) -> AndExpr {
527 AndExpr { left: Box::new(self), right: Box::new(other) }
528 }
529 pub fn or(self, other: impl DynExpr + 'static) -> OrExpr {
530 OrExpr { left: Box::new(self), right: Box::new(other) }
531 }
532 }
533 )*};
534}
535combinators!(
536 EqExpr,
537 NeExpr,
538 GtExpr,
539 LtExpr,
540 GteExpr,
541 LteExpr,
542 AndExpr,
543 OrExpr,
544 ContainsExpr,
545 NotExpr,
546 Raw
547);
548
549#[derive(Debug)]
551pub struct Grouped(pub Box<dyn DynExpr>);
552
553impl Grouped {
554 pub fn new(inner: impl DynExpr + 'static) -> Self {
555 Self(Box::new(inner))
556 }
557}
558
559impl DynExpr for Grouped {
560 fn render_dyn(&self, buf: &mut String) {
561 buf.push('(');
562 self.0.render_dyn(buf);
563 buf.push(')');
564 }
565}
566
567combinators!(Grouped, Func);
568
569#[derive(Debug)]
575pub struct Projection {
576 expr: Box<dyn DynExpr>,
577 alias: Option<&'static str>,
578}
579
580impl Projection {
581 pub fn new(expr: impl DynExpr + 'static) -> Self {
583 Self {
584 expr: Box::new(expr),
585 alias: None,
586 }
587 }
588 pub fn aliased(expr: impl DynExpr + 'static, alias: &'static str) -> Self {
590 Self {
591 expr: Box::new(expr),
592 alias: Some(alias),
593 }
594 }
595 pub fn render(&self, buf: &mut String) {
596 self.expr.render_dyn(buf);
597 if let Some(a) = self.alias {
598 buf.push_str(" AS ");
599 buf.push_str(a);
600 }
601 }
602}
603
604pub fn col(name: &'static str) -> Projection {
606 Projection::new(Raw(name.to_string()))
607}
608
609pub fn field(raw: &'static str, alias: &'static str) -> Projection {
611 Projection::aliased(Raw(raw.to_string()), alias)
612}
613
614#[derive(Debug, Clone)]
619pub struct ColumnMeta {
620 pub name: &'static str,
621 pub surreal_type: &'static str,
622}
623
624pub struct ColumnSet<T: SurrealRecord> {
626 pub cols: &'static [ColumnMeta],
627 pub _marker: std::marker::PhantomData<T>,
628}
629
630impl<T: SurrealRecord> std::fmt::Debug for ColumnSet<T> {
631 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
632 f.debug_struct("ColumnSet")
633 .field("cols", &self.cols)
634 .finish()
635 }
636}
637
638impl<T: SurrealRecord> DynExpr for ColumnSet<T> {
639 fn render_dyn(&self, buf: &mut String) {
640 buf.push('*');
641 }
642}
643
644#[derive(Debug, Clone, Copy)]
649pub enum Order {
650 Asc,
651 Desc,
652}
653
654impl Order {
655 pub fn render_suffix(&self) -> &'static str {
656 match self {
657 Order::Asc => "ASC",
658 Order::Desc => "DESC",
659 }
660 }
661}
662
663impl std::fmt::Display for Order {
664 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
665 f.write_str(self.render_suffix())
666 }
667}