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_str("d'");
102 buf.push_str(&value.to_rfc3339());
103 buf.push('\'');
104 }
105}
106
107impl SurrealQL for uuid::Uuid {
108 fn surreal_type() -> &'static str {
109 "uuid"
110 }
111 fn render_literal(value: &Self, buf: &mut String) {
112 use std::fmt::Write;
113 buf.push_str("u'");
116 let _ = write!(buf, "{value}");
117 buf.push('\'');
118 }
119}
120
121impl SurrealQL for serde_json::Value {
122 fn surreal_type() -> &'static str {
123 "object"
124 }
125 fn render_literal(value: &Self, buf: &mut String) {
126 use std::fmt::Write;
130 let _ = write!(buf, "{value}");
131 }
132}
133
134macro_rules! geometry_surrealql {
137 ($t:ident, $name:literal) => {
138 impl SurrealQL for crate::types::$t {
139 fn surreal_type() -> &'static str {
140 $name
141 }
142 fn render_literal(value: &Self, buf: &mut String) {
143 if let Ok(s) = serde_json::to_string(value) {
144 buf.push_str(&s);
145 }
146 }
147 }
148 };
149}
150geometry_surrealql!(Point, "geometry<point>");
151geometry_surrealql!(LineString, "geometry<line>");
152geometry_surrealql!(Polygon, "geometry<polygon>");
153
154impl<T: SurrealQL> SurrealQL for Option<T> {
155 fn surreal_type() -> &'static str {
156 T::surreal_type()
157 }
158 fn render_literal(value: &Self, buf: &mut String) {
159 match value {
160 Some(v) => T::render_literal(v, buf),
161 None => buf.push_str("NONE"),
162 }
163 }
164}
165
166impl<T: crate::types::SurrealRecord> SurrealQL for crate::types::Thing<T> {
167 fn surreal_type() -> &'static str {
168 "record"
169 }
170 fn render_literal(value: &Self, buf: &mut String) {
171 buf.push_str(T::table_name());
172 buf.push(':');
173 value.key.render_id(buf);
174 }
175}
176
177#[derive(Debug, Clone)]
182pub struct Literal<V: SurrealQL>(pub V);
183
184impl<V: SurrealQL> DynExpr for Literal<V> {
185 fn render_dyn(&self, buf: &mut String) {
186 V::render_literal(&self.0, buf);
187 }
188}
189
190pub struct Column<T: SurrealRecord, V: SurrealQL> {
195 pub name: &'static str,
196 pub surreal_type: &'static str,
197 pub _marker: std::marker::PhantomData<(T, V)>,
198}
199
200impl<T: SurrealRecord, V: SurrealQL> std::fmt::Debug for Column<T, V> {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 f.debug_struct("Column").field("name", &self.name).finish()
203 }
204}
205
206impl<T: SurrealRecord, V: SurrealQL> Clone for Column<T, V> {
207 fn clone(&self) -> Self {
208 *self
209 }
210}
211impl<T: SurrealRecord, V: SurrealQL> Copy for Column<T, V> {}
212
213impl<T: SurrealRecord, V: SurrealQL> DynExpr for Column<T, V> {
214 fn render_dyn(&self, buf: &mut String) {
215 buf.push_str(self.name);
216 }
217}
218
219#[derive(Debug, Clone, Copy)]
227pub struct Ident(pub &'static str);
228
229pub fn ident(name: &'static str) -> Ident {
231 Ident(name)
232}
233
234impl Ident {
235 fn dyn_box(&self) -> Box<dyn DynExpr> {
236 Box::new(Raw(self.0.to_string()))
237 }
238
239 pub fn eq<V: SurrealQL>(&self, v: V) -> EqExpr {
240 EqExpr {
241 left: self.dyn_box(),
242 right: Box::new(Literal(v)),
243 }
244 }
245 pub fn ne<V: SurrealQL>(&self, v: V) -> NeExpr {
246 NeExpr {
247 left: self.dyn_box(),
248 right: Box::new(Literal(v)),
249 }
250 }
251 pub fn gt<V: SurrealQL>(&self, v: V) -> GtExpr {
252 GtExpr {
253 left: self.dyn_box(),
254 right: Box::new(Literal(v)),
255 }
256 }
257 pub fn lt<V: SurrealQL>(&self, v: V) -> LtExpr {
258 LtExpr {
259 left: self.dyn_box(),
260 right: Box::new(Literal(v)),
261 }
262 }
263 pub fn gte<V: SurrealQL>(&self, v: V) -> GteExpr {
264 GteExpr {
265 left: self.dyn_box(),
266 right: Box::new(Literal(v)),
267 }
268 }
269 pub fn lte<V: SurrealQL>(&self, v: V) -> LteExpr {
270 LteExpr {
271 left: self.dyn_box(),
272 right: Box::new(Literal(v)),
273 }
274 }
275 pub fn contains<V: SurrealQL>(&self, v: V) -> ContainsExpr {
276 ContainsExpr {
277 haystack: self.dyn_box(),
278 needle: Box::new(Literal(v)),
279 }
280 }
281 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
283 EqExpr {
284 left: self.dyn_box(),
285 right: Box::new(rhs),
286 }
287 }
288 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
289 NeExpr {
290 left: self.dyn_box(),
291 right: Box::new(rhs),
292 }
293 }
294 pub fn is_none(&self) -> Raw {
296 Raw(format!("{} IS NONE", self.0))
297 }
298}
299
300impl DynExpr for Ident {
301 fn render_dyn(&self, buf: &mut String) {
302 buf.push_str(self.0);
303 }
304}
305
306#[derive(Debug, Clone)]
313pub struct Raw(pub String);
314
315impl Raw {
316 pub fn new(s: impl Into<String>) -> Self {
317 Self(s.into())
318 }
319}
320
321impl DynExpr for Raw {
322 fn render_dyn(&self, buf: &mut String) {
323 buf.push_str(&self.0);
324 }
325}
326
327#[derive(Debug, Clone)]
329pub struct NoneLit;
330impl DynExpr for NoneLit {
331 fn render_dyn(&self, buf: &mut String) {
332 buf.push_str("NONE");
333 }
334}
335
336#[derive(Debug)]
343pub struct RecordLink {
344 table: &'static str,
345 key: Box<dyn DynExpr>,
346}
347
348impl RecordLink {
349 pub fn new<V: SurrealQL>(table: &'static str, key: V) -> Self {
351 Self {
352 table,
353 key: Box::new(Literal(key)),
354 }
355 }
356 pub fn from_expr(table: &'static str, key: impl DynExpr + 'static) -> Self {
358 Self {
359 table,
360 key: Box::new(key),
361 }
362 }
363}
364
365impl DynExpr for RecordLink {
366 fn render_dyn(&self, buf: &mut String) {
367 buf.push_str("type::record('");
368 buf.push_str(self.table);
369 buf.push_str("', ");
370 self.key.render_dyn(buf);
371 buf.push(')');
372 }
373}
374
375#[derive(Debug)]
381pub struct Func {
382 name: &'static str,
383 args: Vec<Box<dyn DynExpr>>,
384}
385
386impl Func {
387 pub fn new(name: &'static str, args: Vec<Box<dyn DynExpr>>) -> Self {
388 Self { name, args }
389 }
390 pub fn of(name: &'static str, ident: &'static str) -> Self {
393 Self {
394 name,
395 args: vec![Box::new(Raw(ident.to_string()))],
396 }
397 }
398}
399
400impl DynExpr for Func {
401 fn render_dyn(&self, buf: &mut String) {
402 buf.push_str(self.name);
403 buf.push('(');
404 for (i, a) in self.args.iter().enumerate() {
405 if i > 0 {
406 buf.push_str(", ");
407 }
408 a.render_dyn(buf);
409 }
410 buf.push(')');
411 }
412}
413
414macro_rules! binop {
419 ($name:ident, $op:literal) => {
420 #[derive(Debug)]
421 pub struct $name {
422 pub(crate) left: Box<dyn DynExpr>,
423 pub(crate) right: Box<dyn DynExpr>,
424 }
425 impl DynExpr for $name {
426 fn render_dyn(&self, buf: &mut String) {
427 self.left.render_dyn(buf);
428 buf.push(' ');
429 buf.push_str($op);
430 buf.push(' ');
431 self.right.render_dyn(buf);
432 }
433 }
434 };
435}
436
437binop!(EqExpr, "=");
438binop!(NeExpr, "!=");
439binop!(GtExpr, ">");
440binop!(LtExpr, "<");
441binop!(GteExpr, ">=");
442binop!(LteExpr, "<=");
443binop!(AndExpr, "AND");
444binop!(OrExpr, "OR");
445
446#[derive(Debug)]
447pub struct NotExpr {
448 pub(crate) inner: Box<dyn DynExpr>,
449}
450
451impl DynExpr for NotExpr {
452 fn render_dyn(&self, buf: &mut String) {
453 buf.push_str("NOT ");
454 self.inner.render_dyn(buf);
455 }
456}
457
458#[derive(Debug)]
459pub struct ContainsExpr {
460 pub(crate) haystack: Box<dyn DynExpr>,
461 pub(crate) needle: Box<dyn DynExpr>,
462}
463
464impl DynExpr for ContainsExpr {
465 fn render_dyn(&self, buf: &mut String) {
466 self.haystack.render_dyn(buf);
467 buf.push_str(" CONTAINS ");
468 self.needle.render_dyn(buf);
469 }
470}
471
472impl<T: SurrealRecord, V: SurrealQL> Column<T, V> {
477 pub fn eq(&self, value: V) -> EqExpr {
478 EqExpr {
479 left: self.dyn_box(),
480 right: Box::new(Literal(value)),
481 }
482 }
483 pub fn ne(&self, value: V) -> NeExpr {
484 NeExpr {
485 left: self.dyn_box(),
486 right: Box::new(Literal(value)),
487 }
488 }
489 pub fn gt(&self, value: V) -> GtExpr {
490 GtExpr {
491 left: self.dyn_box(),
492 right: Box::new(Literal(value)),
493 }
494 }
495 pub fn lt(&self, value: V) -> LtExpr {
496 LtExpr {
497 left: self.dyn_box(),
498 right: Box::new(Literal(value)),
499 }
500 }
501 pub fn gte(&self, value: V) -> GteExpr {
502 GteExpr {
503 left: self.dyn_box(),
504 right: Box::new(Literal(value)),
505 }
506 }
507 pub fn lte(&self, value: V) -> LteExpr {
508 LteExpr {
509 left: self.dyn_box(),
510 right: Box::new(Literal(value)),
511 }
512 }
513 pub fn contains(&self, value: V) -> ContainsExpr {
514 ContainsExpr {
515 haystack: self.dyn_box(),
516 needle: Box::new(Literal(value)),
517 }
518 }
519
520 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
523 EqExpr {
524 left: self.dyn_box(),
525 right: Box::new(rhs),
526 }
527 }
528 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
529 NeExpr {
530 left: self.dyn_box(),
531 right: Box::new(rhs),
532 }
533 }
534 pub fn is_none(&self) -> Raw {
536 Raw(format!("{} IS NONE", self.name))
537 }
538
539 fn dyn_box(&self) -> Box<dyn DynExpr> {
540 Box::new(Self {
541 name: self.name,
542 surreal_type: self.surreal_type,
543 _marker: self._marker,
544 })
545 }
546}
547
548macro_rules! combinators {
550 ($($t:ty),* $(,)?) => {$(
551 impl $t {
552 pub fn and(self, other: impl DynExpr + 'static) -> AndExpr {
553 AndExpr { left: Box::new(self), right: Box::new(other) }
554 }
555 pub fn or(self, other: impl DynExpr + 'static) -> OrExpr {
556 OrExpr { left: Box::new(self), right: Box::new(other) }
557 }
558 }
559 )*};
560}
561combinators!(
562 EqExpr,
563 NeExpr,
564 GtExpr,
565 LtExpr,
566 GteExpr,
567 LteExpr,
568 AndExpr,
569 OrExpr,
570 ContainsExpr,
571 NotExpr,
572 Raw
573);
574
575#[derive(Debug)]
577pub struct Grouped(pub Box<dyn DynExpr>);
578
579impl Grouped {
580 pub fn new(inner: impl DynExpr + 'static) -> Self {
581 Self(Box::new(inner))
582 }
583}
584
585impl DynExpr for Grouped {
586 fn render_dyn(&self, buf: &mut String) {
587 buf.push('(');
588 self.0.render_dyn(buf);
589 buf.push(')');
590 }
591}
592
593combinators!(Grouped, Func);
594
595#[derive(Debug)]
601pub struct Projection {
602 expr: Box<dyn DynExpr>,
603 alias: Option<&'static str>,
604}
605
606impl Projection {
607 pub fn new(expr: impl DynExpr + 'static) -> Self {
609 Self {
610 expr: Box::new(expr),
611 alias: None,
612 }
613 }
614 pub fn aliased(expr: impl DynExpr + 'static, alias: &'static str) -> Self {
616 Self {
617 expr: Box::new(expr),
618 alias: Some(alias),
619 }
620 }
621 pub fn render(&self, buf: &mut String) {
622 self.expr.render_dyn(buf);
623 if let Some(a) = self.alias {
624 buf.push_str(" AS ");
625 buf.push_str(a);
626 }
627 }
628}
629
630pub fn col(name: &'static str) -> Projection {
632 Projection::new(Raw(name.to_string()))
633}
634
635pub fn field(raw: &'static str, alias: &'static str) -> Projection {
637 Projection::aliased(Raw(raw.to_string()), alias)
638}
639
640#[derive(Debug, Clone)]
645pub struct ColumnMeta {
646 pub name: &'static str,
647 pub surreal_type: &'static str,
648}
649
650pub struct ColumnSet<T: SurrealRecord> {
652 pub cols: &'static [ColumnMeta],
653 pub _marker: std::marker::PhantomData<T>,
654}
655
656impl<T: SurrealRecord> std::fmt::Debug for ColumnSet<T> {
657 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
658 f.debug_struct("ColumnSet")
659 .field("cols", &self.cols)
660 .finish()
661 }
662}
663
664impl<T: SurrealRecord> DynExpr for ColumnSet<T> {
665 fn render_dyn(&self, buf: &mut String) {
666 buf.push('*');
667 }
668}
669
670#[derive(Debug, Clone, Copy)]
675pub enum Order {
676 Asc,
677 Desc,
678}
679
680impl Order {
681 pub fn render_suffix(&self) -> &'static str {
682 match self {
683 Order::Asc => "ASC",
684 Order::Desc => "DESC",
685 }
686 }
687}
688
689impl std::fmt::Display for Order {
690 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
691 f.write_str(self.render_suffix())
692 }
693}