1use std::fmt;
10
11use crate::types::SurrealRecord;
12
13pub trait DynExpr: fmt::Debug + Send + Sync {
20 fn render_dyn(&self, buf: &mut String);
22}
23
24pub type DynExprBox = Box<dyn DynExpr>;
27
28impl DynExpr for Box<dyn DynExpr> {
29 fn render_dyn(&self, buf: &mut String) {
30 (**self).render_dyn(buf);
31 }
32}
33
34pub trait Expr: DynExpr {
41 fn ty_hint(&self) -> &'static str;
43}
44
45impl<E: DynExpr> Expr for E {
48 fn ty_hint(&self) -> &'static str {
49 "any"
50 }
51}
52
53pub trait SurrealQL: fmt::Debug + Clone + Send + Sync + 'static {
61 fn surreal_type() -> &'static str;
63 fn render_literal(value: &Self, buf: &mut String);
65}
66
67impl SurrealQL for String {
68 fn surreal_type() -> &'static str {
69 "string"
70 }
71 fn render_literal(value: &Self, buf: &mut String) {
72 let escaped = value.replace('\\', "\\\\").replace('\'', "\\'");
73 buf.push('\'');
74 buf.push_str(&escaped);
75 buf.push('\'');
76 }
77}
78
79impl SurrealQL for bool {
80 fn surreal_type() -> &'static str {
81 "bool"
82 }
83 fn render_literal(value: &Self, buf: &mut String) {
84 buf.push_str(if *value { "true" } else { "false" });
85 }
86}
87
88macro_rules! surreal_display {
89 ($t:ty, $name:literal) => {
90 impl SurrealQL for $t {
91 fn surreal_type() -> &'static str {
92 $name
93 }
94 fn render_literal(value: &Self, buf: &mut String) {
95 use std::fmt::Write;
96 let _ = write!(buf, "{value}");
97 }
98 }
99 };
100}
101surreal_display!(i64, "int");
102surreal_display!(i32, "int");
103surreal_display!(i16, "int");
104surreal_display!(i8, "int");
105surreal_display!(f64, "float");
106surreal_display!(f32, "float");
107surreal_display!(u32, "int");
108surreal_display!(u64, "int");
109surreal_display!(u16, "int");
110surreal_display!(u8, "int");
111
112impl SurrealQL for chrono::DateTime<chrono::Utc> {
113 fn surreal_type() -> &'static str {
114 "datetime"
115 }
116 fn render_literal(value: &Self, buf: &mut String) {
117 buf.push_str("d'");
121 buf.push_str(&value.to_rfc3339());
122 buf.push('\'');
123 }
124}
125
126impl SurrealQL for uuid::Uuid {
127 fn surreal_type() -> &'static str {
128 "uuid"
129 }
130 fn render_literal(value: &Self, buf: &mut String) {
131 use std::fmt::Write;
132 buf.push_str("u'");
135 let _ = write!(buf, "{value}");
136 buf.push('\'');
137 }
138}
139
140impl SurrealQL for serde_json::Value {
141 fn surreal_type() -> &'static str {
142 "object"
143 }
144 fn render_literal(value: &Self, buf: &mut String) {
145 use std::fmt::Write;
149 let _ = write!(buf, "{value}");
150 }
151}
152
153macro_rules! geometry_surrealql {
156 ($t:ident, $name:literal) => {
157 impl SurrealQL for crate::types::$t {
158 fn surreal_type() -> &'static str {
159 $name
160 }
161 fn render_literal(value: &Self, buf: &mut String) {
162 if let Ok(s) = serde_json::to_string(value) {
163 buf.push_str(&s);
164 }
165 }
166 }
167 };
168}
169geometry_surrealql!(Point, "geometry<point>");
170geometry_surrealql!(LineString, "geometry<line>");
171geometry_surrealql!(Polygon, "geometry<polygon>");
172
173impl<T: SurrealQL> SurrealQL for Option<T> {
174 fn surreal_type() -> &'static str {
175 T::surreal_type()
176 }
177 fn render_literal(value: &Self, buf: &mut String) {
178 match value {
179 Some(v) => T::render_literal(v, buf),
180 None => buf.push_str("NONE"),
181 }
182 }
183}
184
185impl<T: crate::types::SurrealRecord> SurrealQL for crate::types::Thing<T> {
186 fn surreal_type() -> &'static str {
187 "record"
188 }
189 fn render_literal(value: &Self, buf: &mut String) {
190 buf.push_str(T::table_name());
191 buf.push(':');
192 value.key.render_id(buf);
193 }
194}
195
196#[derive(Debug, Clone)]
201pub struct Literal<V: SurrealQL>(pub V);
202
203impl<V: SurrealQL> DynExpr for Literal<V> {
204 fn render_dyn(&self, buf: &mut String) {
205 V::render_literal(&self.0, buf);
206 }
207}
208
209pub struct Column<T: SurrealRecord, V: SurrealQL> {
217 pub name: &'static str,
219 pub surreal_type: &'static str,
221 #[doc(hidden)]
222 pub _marker: std::marker::PhantomData<(T, V)>,
223}
224
225impl<T: SurrealRecord, V: SurrealQL> std::fmt::Debug for Column<T, V> {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 f.debug_struct("Column").field("name", &self.name).finish()
228 }
229}
230
231impl<T: SurrealRecord, V: SurrealQL> Clone for Column<T, V> {
232 fn clone(&self) -> Self {
233 *self
234 }
235}
236impl<T: SurrealRecord, V: SurrealQL> Copy for Column<T, V> {}
237
238impl<T: SurrealRecord, V: SurrealQL> DynExpr for Column<T, V> {
239 fn render_dyn(&self, buf: &mut String) {
240 buf.push_str(self.name);
241 }
242}
243
244#[derive(Debug, Clone, Copy)]
252pub struct Ident(pub &'static str);
253
254pub fn ident(name: &'static str) -> Ident {
256 Ident(name)
257}
258
259impl Ident {
260 fn dyn_box(&self) -> Box<dyn DynExpr> {
261 Box::new(Raw(self.0.to_string()))
262 }
263
264 pub fn eq<V: SurrealQL>(&self, v: V) -> EqExpr {
265 EqExpr {
266 left: self.dyn_box(),
267 right: Box::new(Literal(v)),
268 }
269 }
270 pub fn ne<V: SurrealQL>(&self, v: V) -> NeExpr {
271 NeExpr {
272 left: self.dyn_box(),
273 right: Box::new(Literal(v)),
274 }
275 }
276 pub fn gt<V: SurrealQL>(&self, v: V) -> GtExpr {
277 GtExpr {
278 left: self.dyn_box(),
279 right: Box::new(Literal(v)),
280 }
281 }
282 pub fn lt<V: SurrealQL>(&self, v: V) -> LtExpr {
283 LtExpr {
284 left: self.dyn_box(),
285 right: Box::new(Literal(v)),
286 }
287 }
288 pub fn gte<V: SurrealQL>(&self, v: V) -> GteExpr {
289 GteExpr {
290 left: self.dyn_box(),
291 right: Box::new(Literal(v)),
292 }
293 }
294 pub fn lte<V: SurrealQL>(&self, v: V) -> LteExpr {
295 LteExpr {
296 left: self.dyn_box(),
297 right: Box::new(Literal(v)),
298 }
299 }
300 pub fn contains<V: SurrealQL>(&self, v: V) -> ContainsExpr {
301 ContainsExpr {
302 haystack: self.dyn_box(),
303 needle: Box::new(Literal(v)),
304 }
305 }
306 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
308 EqExpr {
309 left: self.dyn_box(),
310 right: Box::new(rhs),
311 }
312 }
313 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
314 NeExpr {
315 left: self.dyn_box(),
316 right: Box::new(rhs),
317 }
318 }
319 pub fn is_none(&self) -> Raw {
321 Raw(format!("{} IS NONE", self.0))
322 }
323}
324
325impl DynExpr for Ident {
326 fn render_dyn(&self, buf: &mut String) {
327 buf.push_str(self.0);
328 }
329}
330
331#[derive(Debug, Clone)]
338pub struct Raw(pub String);
339
340impl Raw {
341 pub fn new(s: impl Into<String>) -> Self {
342 Self(s.into())
343 }
344}
345
346impl DynExpr for Raw {
347 fn render_dyn(&self, buf: &mut String) {
348 buf.push_str(&self.0);
349 }
350}
351
352#[derive(Debug, Clone)]
354pub struct NoneLit;
355impl DynExpr for NoneLit {
356 fn render_dyn(&self, buf: &mut String) {
357 buf.push_str("NONE");
358 }
359}
360
361#[derive(Debug)]
368pub struct RecordLink {
369 table: &'static str,
370 key: Box<dyn DynExpr>,
371}
372
373impl RecordLink {
374 pub fn new<V: SurrealQL>(table: &'static str, key: V) -> Self {
376 Self {
377 table,
378 key: Box::new(Literal(key)),
379 }
380 }
381 pub fn from_expr(table: &'static str, key: impl DynExpr + 'static) -> Self {
383 Self {
384 table,
385 key: Box::new(key),
386 }
387 }
388}
389
390impl DynExpr for RecordLink {
391 fn render_dyn(&self, buf: &mut String) {
392 buf.push_str("type::record('");
393 buf.push_str(self.table);
394 buf.push_str("', ");
395 self.key.render_dyn(buf);
396 buf.push(')');
397 }
398}
399
400#[derive(Debug, Clone, Copy, PartialEq, Eq)]
406enum Dir {
407 Out,
409 In,
411 Both,
413}
414
415impl Dir {
416 fn arrow(self) -> &'static str {
417 match self {
418 Dir::Out => "->",
419 Dir::In => "<-",
420 Dir::Both => "<->",
421 }
422 }
423}
424
425#[derive(Debug)]
428struct Step {
429 dir: Dir,
430 edge: &'static str,
431 dest: Option<&'static str>,
432 filter: Option<Box<dyn DynExpr>>,
433}
434
435impl Step {
436 fn render(&self, buf: &mut String) {
437 buf.push_str(self.dir.arrow());
438 match &self.filter {
439 Some(f) => {
441 buf.push('(');
442 buf.push_str(self.edge);
443 buf.push_str(" WHERE ");
444 f.render_dyn(buf);
445 buf.push(')');
446 }
447 None => buf.push_str(self.edge),
448 }
449 if let Some(dest) = self.dest {
450 buf.push_str(self.dir.arrow());
451 buf.push_str(dest);
452 }
453 }
454}
455
456#[derive(Debug)]
458enum Tail {
459 Field(String),
460 All,
461}
462
463#[derive(Debug)]
477pub struct Path {
478 start: Option<Box<dyn DynExpr>>,
479 steps: Vec<Step>,
480 tail: Option<Tail>,
481}
482
483impl Path {
484 fn from_step(dir: Dir, edge: &'static str) -> Self {
485 Self {
486 start: None,
487 steps: vec![Step {
488 dir,
489 edge,
490 dest: None,
491 filter: None,
492 }],
493 tail: None,
494 }
495 }
496
497 pub fn out<E: crate::types::SurrealEdge>() -> Self {
499 Self::from_step(Dir::Out, E::edge_name())
500 }
501 pub fn inn<E: crate::types::SurrealEdge>() -> Self {
503 Self::from_step(Dir::In, E::edge_name())
504 }
505 pub fn both<E: crate::types::SurrealEdge>() -> Self {
507 Self::from_step(Dir::Both, E::edge_name())
508 }
509
510 pub fn out_edge(edge: &'static str) -> Self {
512 Self::from_step(Dir::Out, edge)
513 }
514 pub fn in_edge(edge: &'static str) -> Self {
516 Self::from_step(Dir::In, edge)
517 }
518 pub fn both_edge(edge: &'static str) -> Self {
520 Self::from_step(Dir::Both, edge)
521 }
522
523 pub fn from_record<V: SurrealQL>(mut self, start: V) -> Self {
526 self.start = Some(Box::new(Literal(start)));
527 self
528 }
529
530 pub fn from_expr(mut self, start: impl DynExpr + 'static) -> Self {
533 self.start = Some(Box::new(start));
534 self
535 }
536
537 fn last_mut(&mut self) -> &mut Step {
538 self.steps.last_mut().expect("path always has ≥1 step")
539 }
540
541 pub fn to<T: SurrealRecord>(mut self) -> Self {
543 self.last_mut().dest = Some(T::table_name());
544 self
545 }
546 pub fn to_table(mut self, table: &'static str) -> Self {
548 self.last_mut().dest = Some(table);
549 self
550 }
551
552 pub fn where_(mut self, expr: impl DynExpr + 'static) -> Self {
554 self.last_mut().filter = Some(Box::new(expr));
555 self
556 }
557
558 pub fn then_out<E: crate::types::SurrealEdge>(self) -> Self {
560 self.push_step(Dir::Out, E::edge_name())
561 }
562 pub fn then_in<E: crate::types::SurrealEdge>(self) -> Self {
564 self.push_step(Dir::In, E::edge_name())
565 }
566 pub fn then_both<E: crate::types::SurrealEdge>(self) -> Self {
568 self.push_step(Dir::Both, E::edge_name())
569 }
570 pub fn then_out_edge(self, edge: &'static str) -> Self {
572 self.push_step(Dir::Out, edge)
573 }
574 pub fn then_in_edge(self, edge: &'static str) -> Self {
576 self.push_step(Dir::In, edge)
577 }
578
579 fn push_step(mut self, dir: Dir, edge: &'static str) -> Self {
580 self.steps.push(Step {
581 dir,
582 edge,
583 dest: None,
584 filter: None,
585 });
586 self
587 }
588
589 pub fn field(mut self, name: impl Into<String>) -> Self {
591 self.tail = Some(Tail::Field(name.into()));
592 self
593 }
594 pub fn all(mut self) -> Self {
596 self.tail = Some(Tail::All);
597 self
598 }
599
600 pub fn contains<V: SurrealQL>(self, value: V) -> ContainsExpr {
602 ContainsExpr {
603 haystack: Box::new(self),
604 needle: Box::new(Literal(value)),
605 }
606 }
607 pub fn eq_expr(self, rhs: impl DynExpr + 'static) -> EqExpr {
609 EqExpr {
610 left: Box::new(self),
611 right: Box::new(rhs),
612 }
613 }
614}
615
616impl DynExpr for Path {
617 fn render_dyn(&self, buf: &mut String) {
618 if let Some(start) = &self.start {
619 start.render_dyn(buf);
620 }
621 for step in &self.steps {
622 step.render(buf);
623 }
624 match &self.tail {
625 Some(Tail::Field(f)) => {
626 buf.push('.');
627 buf.push_str(f);
628 }
629 Some(Tail::All) => buf.push_str(".*"),
630 None => {}
631 }
632 }
633}
634
635#[derive(Debug)]
641pub struct Func {
642 name: &'static str,
643 args: Vec<Box<dyn DynExpr>>,
644}
645
646impl Func {
647 pub fn new(name: &'static str, args: Vec<Box<dyn DynExpr>>) -> Self {
648 Self { name, args }
649 }
650 pub fn of(name: &'static str, ident: &'static str) -> Self {
653 Self {
654 name,
655 args: vec![Box::new(Raw(ident.to_string()))],
656 }
657 }
658}
659
660impl DynExpr for Func {
661 fn render_dyn(&self, buf: &mut String) {
662 buf.push_str(self.name);
663 buf.push('(');
664 for (i, a) in self.args.iter().enumerate() {
665 if i > 0 {
666 buf.push_str(", ");
667 }
668 a.render_dyn(buf);
669 }
670 buf.push(')');
671 }
672}
673
674macro_rules! binop {
679 ($name:ident, $op:literal) => {
680 #[derive(Debug)]
681 pub struct $name {
682 pub(crate) left: Box<dyn DynExpr>,
683 pub(crate) right: Box<dyn DynExpr>,
684 }
685 impl DynExpr for $name {
686 fn render_dyn(&self, buf: &mut String) {
687 self.left.render_dyn(buf);
688 buf.push(' ');
689 buf.push_str($op);
690 buf.push(' ');
691 self.right.render_dyn(buf);
692 }
693 }
694 };
695}
696
697binop!(EqExpr, "=");
698binop!(NeExpr, "!=");
699binop!(GtExpr, ">");
700binop!(LtExpr, "<");
701binop!(GteExpr, ">=");
702binop!(LteExpr, "<=");
703binop!(AndExpr, "AND");
704binop!(OrExpr, "OR");
705
706#[derive(Debug)]
707pub struct NotExpr {
708 pub(crate) inner: Box<dyn DynExpr>,
709}
710
711impl DynExpr for NotExpr {
712 fn render_dyn(&self, buf: &mut String) {
713 buf.push_str("NOT ");
714 self.inner.render_dyn(buf);
715 }
716}
717
718#[derive(Debug)]
719pub struct ContainsExpr {
720 pub(crate) haystack: Box<dyn DynExpr>,
721 pub(crate) needle: Box<dyn DynExpr>,
722}
723
724impl DynExpr for ContainsExpr {
725 fn render_dyn(&self, buf: &mut String) {
726 self.haystack.render_dyn(buf);
727 buf.push_str(" CONTAINS ");
728 self.needle.render_dyn(buf);
729 }
730}
731
732impl<T: SurrealRecord, V: SurrealQL> Column<T, V> {
737 pub fn eq(&self, value: V) -> EqExpr {
738 EqExpr {
739 left: self.dyn_box(),
740 right: Box::new(Literal(value)),
741 }
742 }
743 pub fn ne(&self, value: V) -> NeExpr {
744 NeExpr {
745 left: self.dyn_box(),
746 right: Box::new(Literal(value)),
747 }
748 }
749 pub fn gt(&self, value: V) -> GtExpr {
750 GtExpr {
751 left: self.dyn_box(),
752 right: Box::new(Literal(value)),
753 }
754 }
755 pub fn lt(&self, value: V) -> LtExpr {
756 LtExpr {
757 left: self.dyn_box(),
758 right: Box::new(Literal(value)),
759 }
760 }
761 pub fn gte(&self, value: V) -> GteExpr {
762 GteExpr {
763 left: self.dyn_box(),
764 right: Box::new(Literal(value)),
765 }
766 }
767 pub fn lte(&self, value: V) -> LteExpr {
768 LteExpr {
769 left: self.dyn_box(),
770 right: Box::new(Literal(value)),
771 }
772 }
773 pub fn contains(&self, value: V) -> ContainsExpr {
774 ContainsExpr {
775 haystack: self.dyn_box(),
776 needle: Box::new(Literal(value)),
777 }
778 }
779
780 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
783 EqExpr {
784 left: self.dyn_box(),
785 right: Box::new(rhs),
786 }
787 }
788 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
789 NeExpr {
790 left: self.dyn_box(),
791 right: Box::new(rhs),
792 }
793 }
794 pub fn is_none(&self) -> Raw {
796 Raw(format!("{} IS NONE", self.name))
797 }
798
799 fn dyn_box(&self) -> Box<dyn DynExpr> {
800 Box::new(Self {
801 name: self.name,
802 surreal_type: self.surreal_type,
803 _marker: self._marker,
804 })
805 }
806}
807
808macro_rules! combinators {
810 ($($t:ty),* $(,)?) => {$(
811 impl $t {
812 pub fn and(self, other: impl DynExpr + 'static) -> AndExpr {
813 AndExpr { left: Box::new(self), right: Box::new(other) }
814 }
815 pub fn or(self, other: impl DynExpr + 'static) -> OrExpr {
816 OrExpr { left: Box::new(self), right: Box::new(other) }
817 }
818 }
819 )*};
820}
821combinators!(
822 EqExpr,
823 NeExpr,
824 GtExpr,
825 LtExpr,
826 GteExpr,
827 LteExpr,
828 AndExpr,
829 OrExpr,
830 ContainsExpr,
831 NotExpr,
832 Raw
833);
834
835#[derive(Debug)]
837pub struct Grouped(pub Box<dyn DynExpr>);
838
839impl Grouped {
840 pub fn new(inner: impl DynExpr + 'static) -> Self {
841 Self(Box::new(inner))
842 }
843}
844
845impl DynExpr for Grouped {
846 fn render_dyn(&self, buf: &mut String) {
847 buf.push('(');
848 self.0.render_dyn(buf);
849 buf.push(')');
850 }
851}
852
853combinators!(Grouped, Func, Path);
854
855#[derive(Debug)]
861pub struct Projection {
862 expr: Box<dyn DynExpr>,
863 alias: Option<&'static str>,
864}
865
866impl Projection {
867 pub fn new(expr: impl DynExpr + 'static) -> Self {
869 Self {
870 expr: Box::new(expr),
871 alias: None,
872 }
873 }
874 pub fn aliased(expr: impl DynExpr + 'static, alias: &'static str) -> Self {
876 Self {
877 expr: Box::new(expr),
878 alias: Some(alias),
879 }
880 }
881 pub fn render(&self, buf: &mut String) {
882 self.expr.render_dyn(buf);
883 if let Some(a) = self.alias {
884 buf.push_str(" AS ");
885 buf.push_str(a);
886 }
887 }
888}
889
890pub fn col(name: &'static str) -> Projection {
892 Projection::new(Raw(name.to_string()))
893}
894
895pub fn field(raw: &'static str, alias: &'static str) -> Projection {
897 Projection::aliased(Raw(raw.to_string()), alias)
898}
899
900#[derive(Debug, Clone)]
905pub struct ColumnMeta {
906 pub name: &'static str,
907 pub surreal_type: &'static str,
908}
909
910pub struct ColumnSet<T: SurrealRecord> {
912 pub cols: &'static [ColumnMeta],
913 pub _marker: std::marker::PhantomData<T>,
914}
915
916impl<T: SurrealRecord> std::fmt::Debug for ColumnSet<T> {
917 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
918 f.debug_struct("ColumnSet")
919 .field("cols", &self.cols)
920 .finish()
921 }
922}
923
924impl<T: SurrealRecord> DynExpr for ColumnSet<T> {
925 fn render_dyn(&self, buf: &mut String) {
926 buf.push('*');
927 }
928}
929
930#[derive(Debug, Clone, Copy)]
936pub enum Order {
937 Asc,
939 Desc,
941}
942
943impl Order {
944 pub fn render_suffix(&self) -> &'static str {
945 match self {
946 Order::Asc => "ASC",
947 Order::Desc => "DESC",
948 }
949 }
950}
951
952impl std::fmt::Display for Order {
953 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
954 f.write_str(self.render_suffix())
955 }
956}