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
1071impl<T: SurrealRecord, V: SurrealQL> Column<T, V> {
1076 pub fn eq(&self, value: V) -> EqExpr {
1077 EqExpr {
1078 left: self.dyn_box(),
1079 right: Box::new(Literal(value)),
1080 }
1081 }
1082 pub fn ne(&self, value: V) -> NeExpr {
1083 NeExpr {
1084 left: self.dyn_box(),
1085 right: Box::new(Literal(value)),
1086 }
1087 }
1088 pub fn gt(&self, value: V) -> GtExpr {
1089 GtExpr {
1090 left: self.dyn_box(),
1091 right: Box::new(Literal(value)),
1092 }
1093 }
1094 pub fn lt(&self, value: V) -> LtExpr {
1095 LtExpr {
1096 left: self.dyn_box(),
1097 right: Box::new(Literal(value)),
1098 }
1099 }
1100 pub fn gte(&self, value: V) -> GteExpr {
1101 GteExpr {
1102 left: self.dyn_box(),
1103 right: Box::new(Literal(value)),
1104 }
1105 }
1106 pub fn lte(&self, value: V) -> LteExpr {
1107 LteExpr {
1108 left: self.dyn_box(),
1109 right: Box::new(Literal(value)),
1110 }
1111 }
1112 pub fn contains(&self, value: V) -> ContainsExpr {
1113 ContainsExpr {
1114 haystack: self.dyn_box(),
1115 needle: Box::new(Literal(value)),
1116 }
1117 }
1118 pub fn contains_expr(&self, expr: impl DynExpr + 'static) -> ContainsExpr {
1120 ContainsExpr {
1121 haystack: self.dyn_box(),
1122 needle: Box::new(expr),
1123 }
1124 }
1125
1126 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
1129 EqExpr {
1130 left: self.dyn_box(),
1131 right: Box::new(rhs),
1132 }
1133 }
1134 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
1135 NeExpr {
1136 left: self.dyn_box(),
1137 right: Box::new(rhs),
1138 }
1139 }
1140 pub fn in_expr(&self, rhs: impl DynExpr + 'static) -> InExpr {
1142 InExpr {
1143 left: self.dyn_box(),
1144 right: Box::new(rhs),
1145 }
1146 }
1147 pub fn not_in_expr(&self, rhs: impl DynExpr + 'static) -> NotInExpr {
1149 NotInExpr {
1150 left: self.dyn_box(),
1151 right: Box::new(rhs),
1152 }
1153 }
1154 pub fn is_none(&self) -> Raw {
1156 Raw(format!("{} IS NONE", self.name))
1157 }
1158
1159 fn dyn_box(&self) -> Box<dyn DynExpr> {
1160 Box::new(Self {
1161 name: self.name,
1162 surreal_type: self.surreal_type,
1163 _marker: self._marker,
1164 })
1165 }
1166}
1167
1168macro_rules! combinators {
1170 ($($t:ty),* $(,)?) => {$(
1171 impl $t {
1172 pub fn and(self, other: impl DynExpr + 'static) -> AndExpr {
1173 AndExpr { left: Box::new(self), right: Box::new(other) }
1174 }
1175 pub fn or(self, other: impl DynExpr + 'static) -> OrExpr {
1176 OrExpr { left: Box::new(self), right: Box::new(other) }
1177 }
1178 }
1179 )*};
1180}
1181combinators!(
1182 EqExpr,
1183 NeExpr,
1184 GtExpr,
1185 LtExpr,
1186 GteExpr,
1187 LteExpr,
1188 AndExpr,
1189 OrExpr,
1190 ContainsExpr,
1191 InExpr,
1192 NotInExpr,
1193 NotExpr,
1194 Raw
1195);
1196
1197#[derive(Debug)]
1199pub struct Grouped(pub Box<dyn DynExpr>);
1200
1201impl Grouped {
1202 pub fn new(inner: impl DynExpr + 'static) -> Self {
1203 Self(Box::new(inner))
1204 }
1205}
1206
1207impl DynExpr for Grouped {
1208 fn render_dyn(&self, buf: &mut String) {
1209 buf.push('(');
1210 self.0.render_dyn(buf);
1211 buf.push(')');
1212 }
1213 fn render_dyn_params(
1214 &self,
1215 buf: &mut String,
1216 params: &mut BTreeMap<String, serde_json::Value>,
1217 ) {
1218 buf.push('(');
1219 self.0.render_dyn_params(buf, params);
1220 buf.push(')');
1221 }
1222}
1223
1224combinators!(Grouped, Func, Path);
1225
1226#[derive(Debug)]
1241pub struct IfExpr {
1242 branches: Vec<(Box<dyn DynExpr>, Box<dyn DynExpr>)>,
1243 else_branch: Option<Box<dyn DynExpr>>,
1244}
1245
1246impl IfExpr {
1247 pub fn new(cond: impl DynExpr + 'static, then: impl DynExpr + 'static) -> Self {
1249 Self {
1250 branches: vec![(Box::new(cond), Box::new(then))],
1251 else_branch: None,
1252 }
1253 }
1254 pub fn else_if(mut self, cond: impl DynExpr + 'static, then: impl DynExpr + 'static) -> Self {
1256 self.branches.push((Box::new(cond), Box::new(then)));
1257 self
1258 }
1259 pub fn else_(mut self, expr: impl DynExpr + 'static) -> Self {
1261 self.else_branch = Some(Box::new(expr));
1262 self
1263 }
1264}
1265
1266impl DynExpr for IfExpr {
1267 fn render_dyn(&self, buf: &mut String) {
1268 for (i, (cond, then)) in self.branches.iter().enumerate() {
1269 buf.push_str(if i == 0 { "IF " } else { " ELSE IF " });
1270 cond.render_dyn(buf);
1271 buf.push_str(" THEN ");
1272 then.render_dyn(buf);
1273 }
1274 if let Some(e) = &self.else_branch {
1275 buf.push_str(" ELSE ");
1276 e.render_dyn(buf);
1277 }
1278 buf.push_str(" END");
1279 }
1280 fn render_dyn_params(
1281 &self,
1282 buf: &mut String,
1283 params: &mut BTreeMap<String, serde_json::Value>,
1284 ) {
1285 for (i, (cond, then)) in self.branches.iter().enumerate() {
1286 buf.push_str(if i == 0 { "IF " } else { " ELSE IF " });
1287 cond.render_dyn_params(buf, params);
1288 buf.push_str(" THEN ");
1289 then.render_dyn_params(buf, params);
1290 }
1291 if let Some(e) = &self.else_branch {
1292 buf.push_str(" ELSE ");
1293 e.render_dyn_params(buf, params);
1294 }
1295 buf.push_str(" END");
1296 }
1297}
1298
1299#[derive(Debug)]
1305pub struct Projection {
1306 expr: Box<dyn DynExpr>,
1307 alias: Option<&'static str>,
1308}
1309
1310impl Projection {
1311 pub fn new(expr: impl DynExpr + 'static) -> Self {
1313 Self {
1314 expr: Box::new(expr),
1315 alias: None,
1316 }
1317 }
1318 pub fn aliased(expr: impl DynExpr + 'static, alias: &'static str) -> Self {
1320 Self {
1321 expr: Box::new(expr),
1322 alias: Some(alias),
1323 }
1324 }
1325 pub fn render(&self, buf: &mut String) {
1326 self.expr.render_dyn(buf);
1327 if let Some(a) = self.alias {
1328 buf.push_str(" AS ");
1329 buf.push_str(a);
1330 }
1331 }
1332 pub fn render_params(
1333 &self,
1334 buf: &mut String,
1335 params: &mut BTreeMap<String, serde_json::Value>,
1336 ) {
1337 self.expr.render_dyn_params(buf, params);
1338 if let Some(a) = self.alias {
1339 buf.push_str(" AS ");
1340 buf.push_str(a);
1341 }
1342 }
1343}
1344
1345pub fn col(name: &'static str) -> Projection {
1347 Projection::new(Raw(name.to_string()))
1348}
1349
1350pub fn field(raw: &'static str, alias: &'static str) -> Projection {
1352 Projection::aliased(Raw(raw.to_string()), alias)
1353}
1354
1355#[derive(Debug, Clone)]
1360pub struct ColumnMeta {
1361 pub name: &'static str,
1362 pub surreal_type: &'static str,
1363}
1364
1365pub struct ColumnSet<T: SurrealRecord> {
1367 pub cols: &'static [ColumnMeta],
1368 pub _marker: std::marker::PhantomData<T>,
1369}
1370
1371impl<T: SurrealRecord> std::fmt::Debug for ColumnSet<T> {
1372 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1373 f.debug_struct("ColumnSet")
1374 .field("cols", &self.cols)
1375 .finish()
1376 }
1377}
1378
1379impl<T: SurrealRecord> DynExpr for ColumnSet<T> {
1380 fn render_dyn(&self, buf: &mut String) {
1381 buf.push('*');
1382 }
1383}
1384
1385#[derive(Debug, Clone, Copy)]
1391pub enum Order {
1392 Asc,
1394 Desc,
1396}
1397
1398impl Order {
1399 pub fn render_suffix(&self) -> &'static str {
1400 match self {
1401 Order::Asc => "ASC",
1402 Order::Desc => "DESC",
1403 }
1404 }
1405}
1406
1407impl std::fmt::Display for Order {
1408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1409 f.write_str(self.render_suffix())
1410 }
1411}