1use std::collections::BTreeMap;
10use std::fmt;
11
12use crate::types::SurrealRecord;
13
14pub trait DynExpr: fmt::Debug + Send + Sync {
21 fn render_dyn(&self, buf: &mut String);
23
24 fn render_dyn_params(
28 &self,
29 buf: &mut String,
30 _params: &mut BTreeMap<String, serde_json::Value>,
31 ) {
32 self.render_dyn(buf);
33 }
34}
35
36pub type DynExprBox = Box<dyn DynExpr>;
39
40impl DynExpr for Box<dyn DynExpr> {
41 fn render_dyn(&self, buf: &mut String) {
42 (**self).render_dyn(buf);
43 }
44 fn render_dyn_params(
45 &self,
46 buf: &mut String,
47 params: &mut BTreeMap<String, serde_json::Value>,
48 ) {
49 (**self).render_dyn_params(buf, params);
50 }
51}
52
53pub trait Expr: DynExpr {
60 fn ty_hint(&self) -> &'static str;
62}
63
64impl<E: DynExpr> Expr for E {
67 fn ty_hint(&self) -> &'static str {
68 "any"
69 }
70}
71
72pub trait SurrealQL: fmt::Debug + Clone + Send + Sync + 'static {
80 fn surreal_type() -> &'static str;
82 fn render_literal(value: &Self, buf: &mut String);
84 fn to_param_value(value: &Self) -> serde_json::Value;
87}
88
89impl SurrealQL for String {
90 fn surreal_type() -> &'static str {
91 "string"
92 }
93 fn render_literal(value: &Self, buf: &mut String) {
94 let escaped = value.replace('\\', "\\\\").replace('\'', "\\'");
95 buf.push('\'');
96 buf.push_str(&escaped);
97 buf.push('\'');
98 }
99 fn to_param_value(value: &Self) -> serde_json::Value {
100 serde_json::Value::String(value.clone())
101 }
102}
103
104impl SurrealQL for bool {
105 fn surreal_type() -> &'static str {
106 "bool"
107 }
108 fn render_literal(value: &Self, buf: &mut String) {
109 buf.push_str(if *value { "true" } else { "false" });
110 }
111 fn to_param_value(value: &Self) -> serde_json::Value {
112 serde_json::Value::Bool(*value)
113 }
114}
115
116macro_rules! surreal_display {
117 ($t:ty, $name:literal) => {
118 impl SurrealQL for $t {
119 fn surreal_type() -> &'static str {
120 $name
121 }
122 fn render_literal(value: &Self, buf: &mut String) {
123 use std::fmt::Write;
124 let _ = write!(buf, "{value}");
125 }
126 fn to_param_value(value: &Self) -> serde_json::Value {
127 serde_json::to_value(value).unwrap_or(serde_json::Value::Null)
128 }
129 }
130 };
131}
132surreal_display!(i64, "int");
133surreal_display!(i32, "int");
134surreal_display!(i16, "int");
135surreal_display!(i8, "int");
136surreal_display!(f64, "float");
137surreal_display!(f32, "float");
138surreal_display!(u32, "int");
139surreal_display!(u64, "int");
140surreal_display!(u16, "int");
141surreal_display!(u8, "int");
142
143impl SurrealQL for chrono::DateTime<chrono::Utc> {
144 fn surreal_type() -> &'static str {
145 "datetime"
146 }
147 fn render_literal(value: &Self, buf: &mut String) {
148 buf.push_str("d'");
152 buf.push_str(&value.to_rfc3339());
153 buf.push('\'');
154 }
155 fn to_param_value(value: &Self) -> serde_json::Value {
156 serde_json::Value::String(value.to_rfc3339())
157 }
158}
159
160impl SurrealQL for uuid::Uuid {
161 fn surreal_type() -> &'static str {
162 "uuid"
163 }
164 fn render_literal(value: &Self, buf: &mut String) {
165 use std::fmt::Write;
166 buf.push_str("u'");
169 let _ = write!(buf, "{value}");
170 buf.push('\'');
171 }
172 fn to_param_value(value: &Self) -> serde_json::Value {
173 serde_json::Value::String(value.to_string())
174 }
175}
176
177impl SurrealQL for serde_json::Value {
178 fn surreal_type() -> &'static str {
179 "object"
180 }
181 fn render_literal(value: &Self, buf: &mut String) {
182 use std::fmt::Write;
186 let _ = write!(buf, "{value}");
187 }
188 fn to_param_value(value: &Self) -> serde_json::Value {
189 value.clone()
190 }
191}
192
193macro_rules! geometry_surrealql {
196 ($t:ident, $name:literal) => {
197 impl SurrealQL for crate::types::$t {
198 fn surreal_type() -> &'static str {
199 $name
200 }
201 fn render_literal(value: &Self, buf: &mut String) {
202 if let Ok(s) = serde_json::to_string(value) {
203 buf.push_str(&s);
204 }
205 }
206 fn to_param_value(value: &Self) -> serde_json::Value {
207 serde_json::to_value(value).unwrap_or(serde_json::Value::Null)
208 }
209 }
210 };
211}
212geometry_surrealql!(Point, "geometry<point>");
213geometry_surrealql!(LineString, "geometry<line>");
214geometry_surrealql!(Polygon, "geometry<polygon>");
215
216impl<T: SurrealQL> SurrealQL for Option<T> {
217 fn surreal_type() -> &'static str {
218 T::surreal_type()
219 }
220 fn render_literal(value: &Self, buf: &mut String) {
221 match value {
222 Some(v) => T::render_literal(v, buf),
223 None => buf.push_str("NONE"),
224 }
225 }
226 fn to_param_value(value: &Self) -> serde_json::Value {
227 match value {
228 Some(v) => T::to_param_value(v),
229 None => serde_json::Value::Null,
230 }
231 }
232}
233
234impl<V: SurrealQL> SurrealQL for Vec<V> {
235 fn surreal_type() -> &'static str {
236 "array"
239 }
240 fn render_literal(value: &Self, buf: &mut String) {
241 buf.push('[');
242 for (i, v) in value.iter().enumerate() {
243 if i > 0 {
244 buf.push_str(", ");
245 }
246 V::render_literal(v, buf);
247 }
248 buf.push(']');
249 }
250 fn to_param_value(value: &Self) -> serde_json::Value {
251 serde_json::Value::Array(value.iter().map(|v| V::to_param_value(v)).collect())
252 }
253}
254
255impl SurrealQL for std::time::Duration {
256 fn surreal_type() -> &'static str {
257 "duration"
258 }
259 fn render_literal(value: &Self, buf: &mut String) {
260 use std::fmt::Write;
261 let secs = value.as_secs();
265 let nanos = value.subsec_nanos();
266 if secs == 0 && nanos == 0 {
267 buf.push_str("0ns");
268 return;
269 }
270 if secs > 0 {
271 let _ = write!(buf, "{secs}s");
272 }
273 if nanos > 0 {
274 let _ = write!(buf, "{nanos}ns");
275 }
276 }
277 fn to_param_value(value: &Self) -> serde_json::Value {
278 let secs = value.as_secs();
279 let nanos = value.subsec_nanos();
280 if secs == 0 && nanos == 0 {
281 serde_json::Value::String("0ns".into())
282 } else if nanos == 0 {
283 serde_json::Value::String(format!("{secs}s"))
284 } else {
285 serde_json::Value::String(format!("{secs}s{nanos}ns"))
286 }
287 }
288}
289
290impl<T: crate::types::SurrealRecord> SurrealQL for crate::types::Thing<T> {
291 fn surreal_type() -> &'static str {
292 "record"
293 }
294 fn render_literal(value: &Self, buf: &mut String) {
295 buf.push_str(T::table_name());
296 buf.push(':');
297 value.key.render_id(buf);
298 }
299 fn to_param_value(value: &Self) -> serde_json::Value {
300 let mut s = String::new();
301 value.key.render_id(&mut s);
302 serde_json::Value::String(format!("{}:{}", T::table_name(), s))
303 }
304}
305
306#[derive(Debug, Clone)]
311pub struct Literal<V: SurrealQL>(pub V);
312
313impl<V: SurrealQL> DynExpr for Literal<V> {
314 fn render_dyn(&self, buf: &mut String) {
315 V::render_literal(&self.0, buf);
316 }
317 fn render_dyn_params(
318 &self,
319 buf: &mut String,
320 params: &mut BTreeMap<String, serde_json::Value>,
321 ) {
322 let name = format!("p{}", params.len());
323 buf.push('$');
324 buf.push_str(&name);
325 params.insert(name, V::to_param_value(&self.0));
326 }
327}
328
329#[derive(Debug, Clone)]
348pub struct Param<V: SurrealQL> {
349 name: String,
350 value: V,
351}
352
353impl<V: SurrealQL> Param<V> {
354 pub fn new(name: impl Into<String>, value: V) -> Self {
356 Self {
357 name: name.into(),
358 value,
359 }
360 }
361}
362
363impl<V: SurrealQL> DynExpr for Param<V> {
364 fn render_dyn(&self, buf: &mut String) {
365 V::render_literal(&self.value, buf);
367 }
368 fn render_dyn_params(
369 &self,
370 buf: &mut String,
371 params: &mut BTreeMap<String, serde_json::Value>,
372 ) {
373 buf.push('$');
374 buf.push_str(&self.name);
375 params
376 .entry(self.name.clone())
377 .or_insert_with(|| V::to_param_value(&self.value));
378 }
379}
380
381pub struct Column<T: SurrealRecord, V: SurrealQL> {
389 pub name: &'static str,
391 pub surreal_type: &'static str,
393 #[doc(hidden)]
394 pub _marker: std::marker::PhantomData<(T, V)>,
395}
396
397impl<T: SurrealRecord, V: SurrealQL> std::fmt::Debug for Column<T, V> {
398 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
399 f.debug_struct("Column").field("name", &self.name).finish()
400 }
401}
402
403impl<T: SurrealRecord, V: SurrealQL> Clone for Column<T, V> {
404 fn clone(&self) -> Self {
405 *self
406 }
407}
408impl<T: SurrealRecord, V: SurrealQL> Copy for Column<T, V> {}
409
410impl<T: SurrealRecord, V: SurrealQL> DynExpr for Column<T, V> {
411 fn render_dyn(&self, buf: &mut String) {
412 buf.push_str(self.name);
413 }
414}
415
416#[derive(Debug, Clone, Copy)]
424pub struct Ident(pub &'static str);
425
426pub fn ident(name: &'static str) -> Ident {
428 Ident(name)
429}
430
431impl Ident {
432 fn dyn_box(&self) -> Box<dyn DynExpr> {
433 Box::new(Raw(self.0.to_string()))
434 }
435
436 pub fn eq<V: SurrealQL>(&self, v: V) -> EqExpr {
437 EqExpr {
438 left: self.dyn_box(),
439 right: Box::new(Literal(v)),
440 }
441 }
442 pub fn ne<V: SurrealQL>(&self, v: V) -> NeExpr {
443 NeExpr {
444 left: self.dyn_box(),
445 right: Box::new(Literal(v)),
446 }
447 }
448 pub fn gt<V: SurrealQL>(&self, v: V) -> GtExpr {
449 GtExpr {
450 left: self.dyn_box(),
451 right: Box::new(Literal(v)),
452 }
453 }
454 pub fn lt<V: SurrealQL>(&self, v: V) -> LtExpr {
455 LtExpr {
456 left: self.dyn_box(),
457 right: Box::new(Literal(v)),
458 }
459 }
460 pub fn gte<V: SurrealQL>(&self, v: V) -> GteExpr {
461 GteExpr {
462 left: self.dyn_box(),
463 right: Box::new(Literal(v)),
464 }
465 }
466 pub fn lte<V: SurrealQL>(&self, v: V) -> LteExpr {
467 LteExpr {
468 left: self.dyn_box(),
469 right: Box::new(Literal(v)),
470 }
471 }
472 pub fn contains<V: SurrealQL>(&self, v: V) -> ContainsExpr {
473 ContainsExpr {
474 haystack: self.dyn_box(),
475 needle: Box::new(Literal(v)),
476 }
477 }
478 pub fn contains_expr(&self, expr: impl DynExpr + 'static) -> ContainsExpr {
481 ContainsExpr {
482 haystack: self.dyn_box(),
483 needle: Box::new(expr),
484 }
485 }
486 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
488 EqExpr {
489 left: self.dyn_box(),
490 right: Box::new(rhs),
491 }
492 }
493 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
494 NeExpr {
495 left: self.dyn_box(),
496 right: Box::new(rhs),
497 }
498 }
499 pub fn in_expr(&self, rhs: impl DynExpr + 'static) -> InExpr {
501 InExpr {
502 left: self.dyn_box(),
503 right: Box::new(rhs),
504 }
505 }
506 pub fn not_in_expr(&self, rhs: impl DynExpr + 'static) -> NotInExpr {
508 NotInExpr {
509 left: self.dyn_box(),
510 right: Box::new(rhs),
511 }
512 }
513 pub fn is_none(&self) -> Raw {
515 Raw(format!("{} IS NONE", self.0))
516 }
517}
518
519impl DynExpr for Ident {
520 fn render_dyn(&self, buf: &mut String) {
521 buf.push_str(self.0);
522 }
523}
524
525#[derive(Debug, Clone)]
532pub struct Raw(pub String);
533
534impl Raw {
535 pub fn new(s: impl Into<String>) -> Self {
536 Self(s.into())
537 }
538}
539
540impl DynExpr for Raw {
541 fn render_dyn(&self, buf: &mut String) {
542 buf.push_str(&self.0);
543 }
544}
545
546#[derive(Debug, Clone)]
548pub struct NoneLit;
549impl DynExpr for NoneLit {
550 fn render_dyn(&self, buf: &mut String) {
551 buf.push_str("NONE");
552 }
553}
554
555#[derive(Debug)]
562pub struct RecordLink {
563 table: &'static str,
564 key: Box<dyn DynExpr>,
565}
566
567impl RecordLink {
568 pub fn new<V: SurrealQL>(table: &'static str, key: V) -> Self {
570 Self {
571 table,
572 key: Box::new(Literal(key)),
573 }
574 }
575 pub fn from_expr(table: &'static str, key: impl DynExpr + 'static) -> Self {
577 Self {
578 table,
579 key: Box::new(key),
580 }
581 }
582}
583
584impl DynExpr for RecordLink {
585 fn render_dyn(&self, buf: &mut String) {
586 buf.push_str("type::record('");
587 buf.push_str(self.table);
588 buf.push_str("', ");
589 self.key.render_dyn(buf);
590 buf.push(')');
591 }
592 fn render_dyn_params(
593 &self,
594 buf: &mut String,
595 params: &mut BTreeMap<String, serde_json::Value>,
596 ) {
597 buf.push_str("type::record('");
598 buf.push_str(self.table);
599 buf.push_str("', ");
600 self.key.render_dyn_params(buf, params);
601 buf.push(')');
602 }
603}
604
605#[derive(Debug, Clone, Copy, PartialEq, Eq)]
611enum Dir {
612 Out,
614 In,
616 Both,
618}
619
620impl Dir {
621 fn arrow(self) -> &'static str {
622 match self {
623 Dir::Out => "->",
624 Dir::In => "<-",
625 Dir::Both => "<->",
626 }
627 }
628}
629
630#[derive(Debug)]
633struct Step {
634 dir: Dir,
635 edge: &'static str,
636 dest: Option<&'static str>,
637 filter: Option<Box<dyn DynExpr>>,
638}
639
640impl Step {
641 fn render(&self, buf: &mut String) {
642 buf.push_str(self.dir.arrow());
643 match &self.filter {
644 Some(f) => {
646 buf.push('(');
647 buf.push_str(self.edge);
648 buf.push_str(" WHERE ");
649 f.render_dyn(buf);
650 buf.push(')');
651 }
652 None => buf.push_str(self.edge),
653 }
654 if let Some(dest) = self.dest {
655 buf.push_str(self.dir.arrow());
656 buf.push_str(dest);
657 }
658 }
659 fn render_params(&self, buf: &mut String, params: &mut BTreeMap<String, serde_json::Value>) {
660 buf.push_str(self.dir.arrow());
661 match &self.filter {
662 Some(f) => {
663 buf.push('(');
664 buf.push_str(self.edge);
665 buf.push_str(" WHERE ");
666 f.render_dyn_params(buf, params);
667 buf.push(')');
668 }
669 None => buf.push_str(self.edge),
670 }
671 if let Some(dest) = self.dest {
672 buf.push_str(self.dir.arrow());
673 buf.push_str(dest);
674 }
675 }
676}
677
678#[derive(Debug)]
680enum Tail {
681 Field(String),
682 All,
683}
684
685#[derive(Debug)]
699pub struct Path {
700 start: Option<Box<dyn DynExpr>>,
701 recurse: Option<String>,
702 steps: Vec<Step>,
703 tail: Option<Tail>,
704}
705
706impl Path {
707 fn from_step(dir: Dir, edge: &'static str) -> Self {
708 Self {
709 start: None,
710 recurse: None,
711 steps: vec![Step {
712 dir,
713 edge,
714 dest: None,
715 filter: None,
716 }],
717 tail: None,
718 }
719 }
720
721 pub fn out<E: crate::types::SurrealEdge>() -> Self {
723 Self::from_step(Dir::Out, E::edge_name())
724 }
725 pub fn inn<E: crate::types::SurrealEdge>() -> Self {
727 Self::from_step(Dir::In, E::edge_name())
728 }
729 pub fn both<E: crate::types::SurrealEdge>() -> Self {
731 Self::from_step(Dir::Both, E::edge_name())
732 }
733
734 pub fn out_edge(edge: &'static str) -> Self {
736 Self::from_step(Dir::Out, edge)
737 }
738 pub fn in_edge(edge: &'static str) -> Self {
740 Self::from_step(Dir::In, edge)
741 }
742 pub fn both_edge(edge: &'static str) -> Self {
744 Self::from_step(Dir::Both, edge)
745 }
746
747 pub fn from_record<V: SurrealQL>(mut self, start: V) -> Self {
750 self.start = Some(Box::new(Literal(start)));
751 self
752 }
753
754 pub fn from_expr(mut self, start: impl DynExpr + 'static) -> Self {
757 self.start = Some(Box::new(start));
758 self
759 }
760
761 fn last_mut(&mut self) -> &mut Step {
762 self.steps.last_mut().expect("path always has ≥1 step")
763 }
764
765 pub fn to<T: SurrealRecord>(mut self) -> Self {
767 self.last_mut().dest = Some(T::table_name());
768 self
769 }
770 pub fn to_table(mut self, table: &'static str) -> Self {
772 self.last_mut().dest = Some(table);
773 self
774 }
775
776 pub fn where_(mut self, expr: impl DynExpr + 'static) -> Self {
778 self.last_mut().filter = Some(Box::new(expr));
779 self
780 }
781
782 pub fn then_out<E: crate::types::SurrealEdge>(self) -> Self {
784 self.push_step(Dir::Out, E::edge_name())
785 }
786 pub fn then_in<E: crate::types::SurrealEdge>(self) -> Self {
788 self.push_step(Dir::In, E::edge_name())
789 }
790 pub fn then_both<E: crate::types::SurrealEdge>(self) -> Self {
792 self.push_step(Dir::Both, E::edge_name())
793 }
794 pub fn then_out_edge(self, edge: &'static str) -> Self {
796 self.push_step(Dir::Out, edge)
797 }
798 pub fn then_in_edge(self, edge: &'static str) -> Self {
800 self.push_step(Dir::In, edge)
801 }
802
803 fn push_step(mut self, dir: Dir, edge: &'static str) -> Self {
804 self.steps.push(Step {
805 dir,
806 edge,
807 dest: None,
808 filter: None,
809 });
810 self
811 }
812
813 pub fn recurse_all(mut self) -> Self {
817 self.recurse = Some("..".to_string());
818 self
819 }
820 pub fn recurse_up_to(mut self, max: u32) -> Self {
822 self.recurse = Some(format!("..{max}"));
823 self
824 }
825 pub fn recurse_range(mut self, min: u32, max: u32) -> Self {
827 self.recurse = Some(format!("{min}..{max}"));
828 self
829 }
830 pub fn recurse_exact(mut self, n: u32) -> Self {
832 self.recurse = Some(format!("{n}"));
833 self
834 }
835
836 pub fn field(mut self, name: impl Into<String>) -> Self {
838 self.tail = Some(Tail::Field(name.into()));
839 self
840 }
841 pub fn all(mut self) -> Self {
843 self.tail = Some(Tail::All);
844 self
845 }
846
847 pub fn contains<V: SurrealQL>(self, value: V) -> ContainsExpr {
849 ContainsExpr {
850 haystack: Box::new(self),
851 needle: Box::new(Literal(value)),
852 }
853 }
854 pub fn eq_expr(self, rhs: impl DynExpr + 'static) -> EqExpr {
856 EqExpr {
857 left: Box::new(self),
858 right: Box::new(rhs),
859 }
860 }
861}
862
863impl DynExpr for Path {
864 fn render_dyn(&self, buf: &mut String) {
865 match (&self.start, &self.recurse) {
866 (Some(start), Some(range)) => {
868 start.render_dyn(buf);
869 buf.push_str(".{");
870 buf.push_str(range);
871 buf.push('}');
872 }
873 (None, Some(range)) => {
875 buf.push_str("@.{");
876 buf.push_str(range);
877 buf.push('}');
878 }
879 (Some(start), None) => start.render_dyn(buf),
881 (None, None) => {}
882 }
883 for step in &self.steps {
884 step.render(buf);
885 }
886 match &self.tail {
887 Some(Tail::Field(f)) => {
888 buf.push('.');
889 buf.push_str(f);
890 }
891 Some(Tail::All) => buf.push_str(".*"),
892 None => {}
893 }
894 }
895 fn render_dyn_params(
896 &self,
897 buf: &mut String,
898 params: &mut BTreeMap<String, serde_json::Value>,
899 ) {
900 match (&self.start, &self.recurse) {
901 (Some(start), Some(range)) => {
902 start.render_dyn_params(buf, params);
903 buf.push_str(".{");
904 buf.push_str(range);
905 buf.push('}');
906 }
907 (None, Some(range)) => {
908 buf.push_str("@.{");
909 buf.push_str(range);
910 buf.push('}');
911 }
912 (Some(start), None) => start.render_dyn_params(buf, params),
913 (None, None) => {}
914 }
915 for step in &self.steps {
916 step.render_params(buf, params);
917 }
918 match &self.tail {
919 Some(Tail::Field(f)) => {
920 buf.push('.');
921 buf.push_str(f);
922 }
923 Some(Tail::All) => buf.push_str(".*"),
924 None => {}
925 }
926 }
927}
928
929#[derive(Debug)]
935pub struct Func {
936 name: &'static str,
937 args: Vec<Box<dyn DynExpr>>,
938}
939
940impl Func {
941 pub fn new(name: &'static str, args: Vec<Box<dyn DynExpr>>) -> Self {
942 Self { name, args }
943 }
944 pub fn of(name: &'static str, ident: &'static str) -> Self {
947 Self {
948 name,
949 args: vec![Box::new(Raw(ident.to_string()))],
950 }
951 }
952}
953
954impl DynExpr for Func {
955 fn render_dyn(&self, buf: &mut String) {
956 buf.push_str(self.name);
957 buf.push('(');
958 for (i, a) in self.args.iter().enumerate() {
959 if i > 0 {
960 buf.push_str(", ");
961 }
962 a.render_dyn(buf);
963 }
964 buf.push(')');
965 }
966 fn render_dyn_params(
967 &self,
968 buf: &mut String,
969 params: &mut BTreeMap<String, serde_json::Value>,
970 ) {
971 buf.push_str(self.name);
972 buf.push('(');
973 for (i, a) in self.args.iter().enumerate() {
974 if i > 0 {
975 buf.push_str(", ");
976 }
977 a.render_dyn_params(buf, params);
978 }
979 buf.push(')');
980 }
981}
982
983macro_rules! binop {
988 ($name:ident, $op:literal) => {
989 #[derive(Debug)]
990 pub struct $name {
991 pub(crate) left: Box<dyn DynExpr>,
992 pub(crate) right: Box<dyn DynExpr>,
993 }
994 impl DynExpr for $name {
995 fn render_dyn(&self, buf: &mut String) {
996 self.left.render_dyn(buf);
997 buf.push(' ');
998 buf.push_str($op);
999 buf.push(' ');
1000 self.right.render_dyn(buf);
1001 }
1002 fn render_dyn_params(
1003 &self,
1004 buf: &mut String,
1005 params: &mut BTreeMap<String, serde_json::Value>,
1006 ) {
1007 self.left.render_dyn_params(buf, params);
1008 buf.push(' ');
1009 buf.push_str($op);
1010 buf.push(' ');
1011 self.right.render_dyn_params(buf, params);
1012 }
1013 }
1014 };
1015}
1016
1017binop!(EqExpr, "=");
1018binop!(NeExpr, "!=");
1019binop!(GtExpr, ">");
1020binop!(LtExpr, "<");
1021binop!(GteExpr, ">=");
1022binop!(LteExpr, "<=");
1023binop!(AndExpr, "AND");
1024binop!(OrExpr, "OR");
1025binop!(InExpr, "IN");
1026binop!(NotInExpr, "NOT IN");
1027
1028#[derive(Debug)]
1029pub struct NotExpr {
1030 pub(crate) inner: Box<dyn DynExpr>,
1031}
1032
1033impl DynExpr for NotExpr {
1034 fn render_dyn(&self, buf: &mut String) {
1035 buf.push_str("NOT ");
1036 self.inner.render_dyn(buf);
1037 }
1038 fn render_dyn_params(
1039 &self,
1040 buf: &mut String,
1041 params: &mut BTreeMap<String, serde_json::Value>,
1042 ) {
1043 buf.push_str("NOT ");
1044 self.inner.render_dyn_params(buf, params);
1045 }
1046}
1047
1048#[derive(Debug)]
1049pub struct ContainsExpr {
1050 pub(crate) haystack: Box<dyn DynExpr>,
1051 pub(crate) needle: Box<dyn DynExpr>,
1052}
1053
1054impl DynExpr for ContainsExpr {
1055 fn render_dyn(&self, buf: &mut String) {
1056 self.haystack.render_dyn(buf);
1057 buf.push_str(" CONTAINS ");
1058 self.needle.render_dyn(buf);
1059 }
1060 fn render_dyn_params(
1061 &self,
1062 buf: &mut String,
1063 params: &mut BTreeMap<String, serde_json::Value>,
1064 ) {
1065 self.haystack.render_dyn_params(buf, params);
1066 buf.push_str(" CONTAINS ");
1067 self.needle.render_dyn_params(buf, params);
1068 }
1069}
1070
1071#[derive(Debug)]
1081pub struct MatchesExpr {
1082 pub(crate) left: Box<dyn DynExpr>,
1083 pub(crate) right: Box<dyn DynExpr>,
1084 pub(crate) reference: Option<u8>,
1085}
1086
1087impl MatchesExpr {
1088 pub fn reference(mut self, n: u8) -> Self {
1090 self.reference = Some(n);
1091 self
1092 }
1093 fn op(&self) -> String {
1094 match self.reference {
1095 Some(r) => format!("@{r}@"),
1096 None => "@@".to_string(),
1097 }
1098 }
1099}
1100
1101impl DynExpr for MatchesExpr {
1102 fn render_dyn(&self, buf: &mut String) {
1103 self.left.render_dyn(buf);
1104 buf.push(' ');
1105 buf.push_str(&self.op());
1106 buf.push(' ');
1107 self.right.render_dyn(buf);
1108 }
1109 fn render_dyn_params(
1110 &self,
1111 buf: &mut String,
1112 params: &mut BTreeMap<String, serde_json::Value>,
1113 ) {
1114 self.left.render_dyn_params(buf, params);
1115 buf.push(' ');
1116 buf.push_str(&self.op());
1117 buf.push(' ');
1118 self.right.render_dyn_params(buf, params);
1119 }
1120}
1121
1122#[derive(Debug)]
1127pub struct KnnExpr {
1128 pub(crate) left: Box<dyn DynExpr>,
1129 pub(crate) right: Box<dyn DynExpr>,
1130 pub(crate) k: u32,
1131 pub(crate) opt: Option<String>,
1132}
1133
1134impl KnnExpr {
1135 fn op(&self) -> String {
1136 match &self.opt {
1137 Some(o) => format!("<|{},{}|>", self.k, o),
1138 None => format!("<|{}|>", self.k),
1139 }
1140 }
1141}
1142
1143impl DynExpr for KnnExpr {
1144 fn render_dyn(&self, buf: &mut String) {
1145 self.left.render_dyn(buf);
1146 buf.push(' ');
1147 buf.push_str(&self.op());
1148 buf.push(' ');
1149 self.right.render_dyn(buf);
1150 }
1151 fn render_dyn_params(
1152 &self,
1153 buf: &mut String,
1154 params: &mut BTreeMap<String, serde_json::Value>,
1155 ) {
1156 self.left.render_dyn_params(buf, params);
1157 buf.push(' ');
1158 buf.push_str(&self.op());
1159 buf.push(' ');
1160 self.right.render_dyn_params(buf, params);
1161 }
1162}
1163
1164impl<T: SurrealRecord, V: SurrealQL> Column<T, V> {
1169 pub fn eq(&self, value: V) -> EqExpr {
1170 EqExpr {
1171 left: self.dyn_box(),
1172 right: Box::new(Literal(value)),
1173 }
1174 }
1175 pub fn ne(&self, value: V) -> NeExpr {
1176 NeExpr {
1177 left: self.dyn_box(),
1178 right: Box::new(Literal(value)),
1179 }
1180 }
1181 pub fn gt(&self, value: V) -> GtExpr {
1182 GtExpr {
1183 left: self.dyn_box(),
1184 right: Box::new(Literal(value)),
1185 }
1186 }
1187 pub fn lt(&self, value: V) -> LtExpr {
1188 LtExpr {
1189 left: self.dyn_box(),
1190 right: Box::new(Literal(value)),
1191 }
1192 }
1193 pub fn gte(&self, value: V) -> GteExpr {
1194 GteExpr {
1195 left: self.dyn_box(),
1196 right: Box::new(Literal(value)),
1197 }
1198 }
1199 pub fn lte(&self, value: V) -> LteExpr {
1200 LteExpr {
1201 left: self.dyn_box(),
1202 right: Box::new(Literal(value)),
1203 }
1204 }
1205 pub fn contains(&self, value: V) -> ContainsExpr {
1206 ContainsExpr {
1207 haystack: self.dyn_box(),
1208 needle: Box::new(Literal(value)),
1209 }
1210 }
1211 pub fn contains_expr(&self, expr: impl DynExpr + 'static) -> ContainsExpr {
1213 ContainsExpr {
1214 haystack: self.dyn_box(),
1215 needle: Box::new(expr),
1216 }
1217 }
1218
1219 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
1222 EqExpr {
1223 left: self.dyn_box(),
1224 right: Box::new(rhs),
1225 }
1226 }
1227 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
1228 NeExpr {
1229 left: self.dyn_box(),
1230 right: Box::new(rhs),
1231 }
1232 }
1233 pub fn in_expr(&self, rhs: impl DynExpr + 'static) -> InExpr {
1235 InExpr {
1236 left: self.dyn_box(),
1237 right: Box::new(rhs),
1238 }
1239 }
1240 pub fn not_in_expr(&self, rhs: impl DynExpr + 'static) -> NotInExpr {
1242 NotInExpr {
1243 left: self.dyn_box(),
1244 right: Box::new(rhs),
1245 }
1246 }
1247 pub fn is_none(&self) -> Raw {
1249 Raw(format!("{} IS NONE", self.name))
1250 }
1251
1252 pub fn matches(&self, query: impl Into<String>) -> MatchesExpr {
1255 MatchesExpr {
1256 left: self.dyn_box(),
1257 right: Box::new(Literal(query.into())),
1258 reference: None,
1259 }
1260 }
1261
1262 fn dyn_box(&self) -> Box<dyn DynExpr> {
1263 Box::new(Self {
1264 name: self.name,
1265 surreal_type: self.surreal_type,
1266 _marker: self._marker,
1267 })
1268 }
1269}
1270
1271macro_rules! combinators {
1273 ($($t:ty),* $(,)?) => {$(
1274 impl $t {
1275 pub fn and(self, other: impl DynExpr + 'static) -> AndExpr {
1276 AndExpr { left: Box::new(self), right: Box::new(other) }
1277 }
1278 pub fn or(self, other: impl DynExpr + 'static) -> OrExpr {
1279 OrExpr { left: Box::new(self), right: Box::new(other) }
1280 }
1281 }
1282 )*};
1283}
1284combinators!(
1285 EqExpr,
1286 NeExpr,
1287 GtExpr,
1288 LtExpr,
1289 GteExpr,
1290 LteExpr,
1291 AndExpr,
1292 OrExpr,
1293 ContainsExpr,
1294 InExpr,
1295 NotInExpr,
1296 NotExpr,
1297 MatchesExpr,
1298 KnnExpr,
1299 Raw
1300);
1301
1302#[derive(Debug)]
1304pub struct Grouped(pub Box<dyn DynExpr>);
1305
1306impl Grouped {
1307 pub fn new(inner: impl DynExpr + 'static) -> Self {
1308 Self(Box::new(inner))
1309 }
1310}
1311
1312impl DynExpr for Grouped {
1313 fn render_dyn(&self, buf: &mut String) {
1314 buf.push('(');
1315 self.0.render_dyn(buf);
1316 buf.push(')');
1317 }
1318 fn render_dyn_params(
1319 &self,
1320 buf: &mut String,
1321 params: &mut BTreeMap<String, serde_json::Value>,
1322 ) {
1323 buf.push('(');
1324 self.0.render_dyn_params(buf, params);
1325 buf.push(')');
1326 }
1327}
1328
1329combinators!(Grouped, Func, Path);
1330
1331#[derive(Debug)]
1345pub struct Closure {
1346 args: Vec<(String, String)>,
1347 returns: Option<String>,
1348 body: Box<dyn DynExpr>,
1349}
1350
1351impl Closure {
1352 pub fn new(body: impl DynExpr + 'static) -> Self {
1354 Self {
1355 args: Vec::new(),
1356 returns: None,
1357 body: Box::new(body),
1358 }
1359 }
1360 pub fn arg(mut self, name: impl Into<String>, surreal_type: impl Into<String>) -> Self {
1362 let mut name = name.into();
1363 if !name.starts_with('$') {
1364 name.insert(0, '$');
1365 }
1366 self.args.push((name, surreal_type.into()));
1367 self
1368 }
1369 pub fn returns(mut self, surreal_type: impl Into<String>) -> Self {
1371 self.returns = Some(surreal_type.into());
1372 self
1373 }
1374
1375 fn render_head(&self, buf: &mut String) {
1376 buf.push('|');
1377 for (i, (name, ty)) in self.args.iter().enumerate() {
1378 if i > 0 {
1379 buf.push_str(", ");
1380 }
1381 buf.push_str(name);
1382 buf.push_str(": ");
1383 buf.push_str(ty);
1384 }
1385 buf.push('|');
1386 if let Some(ret) = &self.returns {
1387 buf.push_str(" -> ");
1388 buf.push_str(ret);
1389 }
1390 buf.push(' ');
1391 }
1392}
1393
1394impl DynExpr for Closure {
1395 fn render_dyn(&self, buf: &mut String) {
1396 self.render_head(buf);
1397 self.body.render_dyn(buf);
1398 }
1399 fn render_dyn_params(
1400 &self,
1401 buf: &mut String,
1402 params: &mut BTreeMap<String, serde_json::Value>,
1403 ) {
1404 self.render_head(buf);
1405 self.body.render_dyn_params(buf, params);
1406 }
1407}
1408
1409#[derive(Debug)]
1424pub struct IfExpr {
1425 branches: Vec<(Box<dyn DynExpr>, Box<dyn DynExpr>)>,
1426 else_branch: Option<Box<dyn DynExpr>>,
1427}
1428
1429impl IfExpr {
1430 pub fn new(cond: impl DynExpr + 'static, then: impl DynExpr + 'static) -> Self {
1432 Self {
1433 branches: vec![(Box::new(cond), Box::new(then))],
1434 else_branch: None,
1435 }
1436 }
1437 pub fn else_if(mut self, cond: impl DynExpr + 'static, then: impl DynExpr + 'static) -> Self {
1439 self.branches.push((Box::new(cond), Box::new(then)));
1440 self
1441 }
1442 pub fn else_(mut self, expr: impl DynExpr + 'static) -> Self {
1444 self.else_branch = Some(Box::new(expr));
1445 self
1446 }
1447}
1448
1449impl DynExpr for IfExpr {
1450 fn render_dyn(&self, buf: &mut String) {
1451 for (i, (cond, then)) in self.branches.iter().enumerate() {
1452 buf.push_str(if i == 0 { "IF " } else { " ELSE IF " });
1453 cond.render_dyn(buf);
1454 buf.push_str(" THEN ");
1455 then.render_dyn(buf);
1456 }
1457 if let Some(e) = &self.else_branch {
1458 buf.push_str(" ELSE ");
1459 e.render_dyn(buf);
1460 }
1461 buf.push_str(" END");
1462 }
1463 fn render_dyn_params(
1464 &self,
1465 buf: &mut String,
1466 params: &mut BTreeMap<String, serde_json::Value>,
1467 ) {
1468 for (i, (cond, then)) in self.branches.iter().enumerate() {
1469 buf.push_str(if i == 0 { "IF " } else { " ELSE IF " });
1470 cond.render_dyn_params(buf, params);
1471 buf.push_str(" THEN ");
1472 then.render_dyn_params(buf, params);
1473 }
1474 if let Some(e) = &self.else_branch {
1475 buf.push_str(" ELSE ");
1476 e.render_dyn_params(buf, params);
1477 }
1478 buf.push_str(" END");
1479 }
1480}
1481
1482#[derive(Debug)]
1488pub struct Projection {
1489 expr: Box<dyn DynExpr>,
1490 alias: Option<&'static str>,
1491}
1492
1493impl Projection {
1494 pub fn new(expr: impl DynExpr + 'static) -> Self {
1496 Self {
1497 expr: Box::new(expr),
1498 alias: None,
1499 }
1500 }
1501 pub fn aliased(expr: impl DynExpr + 'static, alias: &'static str) -> Self {
1503 Self {
1504 expr: Box::new(expr),
1505 alias: Some(alias),
1506 }
1507 }
1508 pub fn render(&self, buf: &mut String) {
1509 self.expr.render_dyn(buf);
1510 if let Some(a) = self.alias {
1511 buf.push_str(" AS ");
1512 buf.push_str(a);
1513 }
1514 }
1515 pub fn render_params(
1516 &self,
1517 buf: &mut String,
1518 params: &mut BTreeMap<String, serde_json::Value>,
1519 ) {
1520 self.expr.render_dyn_params(buf, params);
1521 if let Some(a) = self.alias {
1522 buf.push_str(" AS ");
1523 buf.push_str(a);
1524 }
1525 }
1526}
1527
1528pub fn col(name: &'static str) -> Projection {
1530 Projection::new(Raw(name.to_string()))
1531}
1532
1533pub fn field(raw: &'static str, alias: &'static str) -> Projection {
1535 Projection::aliased(Raw(raw.to_string()), alias)
1536}
1537
1538#[derive(Debug, Clone)]
1543pub struct ColumnMeta {
1544 pub name: &'static str,
1545 pub surreal_type: &'static str,
1546}
1547
1548pub struct ColumnSet<T: SurrealRecord> {
1550 pub cols: &'static [ColumnMeta],
1551 pub _marker: std::marker::PhantomData<T>,
1552}
1553
1554impl<T: SurrealRecord> std::fmt::Debug for ColumnSet<T> {
1555 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1556 f.debug_struct("ColumnSet")
1557 .field("cols", &self.cols)
1558 .finish()
1559 }
1560}
1561
1562impl<T: SurrealRecord> DynExpr for ColumnSet<T> {
1563 fn render_dyn(&self, buf: &mut String) {
1564 buf.push('*');
1565 }
1566}
1567
1568#[derive(Debug, Clone, Copy)]
1574pub enum Order {
1575 Asc,
1577 Desc,
1579}
1580
1581impl Order {
1582 pub fn render_suffix(&self) -> &'static str {
1583 match self {
1584 Order::Asc => "ASC",
1585 Order::Desc => "DESC",
1586 }
1587 }
1588}
1589
1590impl std::fmt::Display for Order {
1591 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1592 f.write_str(self.render_suffix())
1593 }
1594}