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
134impl<T: SurrealQL> SurrealQL for Option<T> {
135 fn surreal_type() -> &'static str {
136 T::surreal_type()
137 }
138 fn render_literal(value: &Self, buf: &mut String) {
139 match value {
140 Some(v) => T::render_literal(v, buf),
141 None => buf.push_str("NONE"),
142 }
143 }
144}
145
146impl<T: crate::types::SurrealRecord> SurrealQL for crate::types::Thing<T> {
147 fn surreal_type() -> &'static str {
148 "record"
149 }
150 fn render_literal(value: &Self, buf: &mut String) {
151 use std::fmt::Write;
152 let _ = write!(buf, "{}:{}", T::table_name(), value.key);
153 }
154}
155
156#[derive(Debug, Clone)]
161pub struct Literal<V: SurrealQL>(pub V);
162
163impl<V: SurrealQL> DynExpr for Literal<V> {
164 fn render_dyn(&self, buf: &mut String) {
165 V::render_literal(&self.0, buf);
166 }
167}
168
169pub struct Column<T: SurrealRecord, V: SurrealQL> {
174 pub name: &'static str,
175 pub surreal_type: &'static str,
176 pub _marker: std::marker::PhantomData<(T, V)>,
177}
178
179impl<T: SurrealRecord, V: SurrealQL> std::fmt::Debug for Column<T, V> {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 f.debug_struct("Column").field("name", &self.name).finish()
182 }
183}
184
185impl<T: SurrealRecord, V: SurrealQL> Clone for Column<T, V> {
186 fn clone(&self) -> Self {
187 *self
188 }
189}
190impl<T: SurrealRecord, V: SurrealQL> Copy for Column<T, V> {}
191
192impl<T: SurrealRecord, V: SurrealQL> DynExpr for Column<T, V> {
193 fn render_dyn(&self, buf: &mut String) {
194 buf.push_str(self.name);
195 }
196}
197
198#[derive(Debug, Clone, Copy)]
206pub struct Ident(pub &'static str);
207
208pub fn ident(name: &'static str) -> Ident {
210 Ident(name)
211}
212
213impl Ident {
214 fn dyn_box(&self) -> Box<dyn DynExpr> {
215 Box::new(Raw(self.0.to_string()))
216 }
217
218 pub fn eq<V: SurrealQL>(&self, v: V) -> EqExpr {
219 EqExpr {
220 left: self.dyn_box(),
221 right: Box::new(Literal(v)),
222 }
223 }
224 pub fn ne<V: SurrealQL>(&self, v: V) -> NeExpr {
225 NeExpr {
226 left: self.dyn_box(),
227 right: Box::new(Literal(v)),
228 }
229 }
230 pub fn gt<V: SurrealQL>(&self, v: V) -> GtExpr {
231 GtExpr {
232 left: self.dyn_box(),
233 right: Box::new(Literal(v)),
234 }
235 }
236 pub fn lt<V: SurrealQL>(&self, v: V) -> LtExpr {
237 LtExpr {
238 left: self.dyn_box(),
239 right: Box::new(Literal(v)),
240 }
241 }
242 pub fn gte<V: SurrealQL>(&self, v: V) -> GteExpr {
243 GteExpr {
244 left: self.dyn_box(),
245 right: Box::new(Literal(v)),
246 }
247 }
248 pub fn lte<V: SurrealQL>(&self, v: V) -> LteExpr {
249 LteExpr {
250 left: self.dyn_box(),
251 right: Box::new(Literal(v)),
252 }
253 }
254 pub fn contains<V: SurrealQL>(&self, v: V) -> ContainsExpr {
255 ContainsExpr {
256 haystack: self.dyn_box(),
257 needle: Box::new(Literal(v)),
258 }
259 }
260 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
262 EqExpr {
263 left: self.dyn_box(),
264 right: Box::new(rhs),
265 }
266 }
267 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
268 NeExpr {
269 left: self.dyn_box(),
270 right: Box::new(rhs),
271 }
272 }
273 pub fn is_none(&self) -> Raw {
275 Raw(format!("{} IS NONE", self.0))
276 }
277}
278
279impl DynExpr for Ident {
280 fn render_dyn(&self, buf: &mut String) {
281 buf.push_str(self.0);
282 }
283}
284
285#[derive(Debug, Clone)]
292pub struct Raw(pub String);
293
294impl Raw {
295 pub fn new(s: impl Into<String>) -> Self {
296 Self(s.into())
297 }
298}
299
300impl DynExpr for Raw {
301 fn render_dyn(&self, buf: &mut String) {
302 buf.push_str(&self.0);
303 }
304}
305
306#[derive(Debug, Clone)]
308pub struct NoneLit;
309impl DynExpr for NoneLit {
310 fn render_dyn(&self, buf: &mut String) {
311 buf.push_str("NONE");
312 }
313}
314
315#[derive(Debug)]
322pub struct RecordLink {
323 table: &'static str,
324 key: Box<dyn DynExpr>,
325}
326
327impl RecordLink {
328 pub fn new<V: SurrealQL>(table: &'static str, key: V) -> Self {
330 Self {
331 table,
332 key: Box::new(Literal(key)),
333 }
334 }
335 pub fn from_expr(table: &'static str, key: impl DynExpr + 'static) -> Self {
337 Self {
338 table,
339 key: Box::new(key),
340 }
341 }
342}
343
344impl DynExpr for RecordLink {
345 fn render_dyn(&self, buf: &mut String) {
346 buf.push_str("type::record('");
347 buf.push_str(self.table);
348 buf.push_str("', ");
349 self.key.render_dyn(buf);
350 buf.push(')');
351 }
352}
353
354#[derive(Debug)]
360pub struct Func {
361 name: &'static str,
362 args: Vec<Box<dyn DynExpr>>,
363}
364
365impl Func {
366 pub fn new(name: &'static str, args: Vec<Box<dyn DynExpr>>) -> Self {
367 Self { name, args }
368 }
369 pub fn of(name: &'static str, ident: &'static str) -> Self {
372 Self {
373 name,
374 args: vec![Box::new(Raw(ident.to_string()))],
375 }
376 }
377}
378
379impl DynExpr for Func {
380 fn render_dyn(&self, buf: &mut String) {
381 buf.push_str(self.name);
382 buf.push('(');
383 for (i, a) in self.args.iter().enumerate() {
384 if i > 0 {
385 buf.push_str(", ");
386 }
387 a.render_dyn(buf);
388 }
389 buf.push(')');
390 }
391}
392
393macro_rules! binop {
398 ($name:ident, $op:literal) => {
399 #[derive(Debug)]
400 pub struct $name {
401 pub(crate) left: Box<dyn DynExpr>,
402 pub(crate) right: Box<dyn DynExpr>,
403 }
404 impl DynExpr for $name {
405 fn render_dyn(&self, buf: &mut String) {
406 self.left.render_dyn(buf);
407 buf.push(' ');
408 buf.push_str($op);
409 buf.push(' ');
410 self.right.render_dyn(buf);
411 }
412 }
413 };
414}
415
416binop!(EqExpr, "=");
417binop!(NeExpr, "!=");
418binop!(GtExpr, ">");
419binop!(LtExpr, "<");
420binop!(GteExpr, ">=");
421binop!(LteExpr, "<=");
422binop!(AndExpr, "AND");
423binop!(OrExpr, "OR");
424
425#[derive(Debug)]
426pub struct NotExpr {
427 pub(crate) inner: Box<dyn DynExpr>,
428}
429
430impl DynExpr for NotExpr {
431 fn render_dyn(&self, buf: &mut String) {
432 buf.push_str("NOT ");
433 self.inner.render_dyn(buf);
434 }
435}
436
437#[derive(Debug)]
438pub struct ContainsExpr {
439 pub(crate) haystack: Box<dyn DynExpr>,
440 pub(crate) needle: Box<dyn DynExpr>,
441}
442
443impl DynExpr for ContainsExpr {
444 fn render_dyn(&self, buf: &mut String) {
445 self.haystack.render_dyn(buf);
446 buf.push_str(" CONTAINS ");
447 self.needle.render_dyn(buf);
448 }
449}
450
451impl<T: SurrealRecord, V: SurrealQL> Column<T, V> {
456 pub fn eq(&self, value: V) -> EqExpr {
457 EqExpr {
458 left: self.dyn_box(),
459 right: Box::new(Literal(value)),
460 }
461 }
462 pub fn ne(&self, value: V) -> NeExpr {
463 NeExpr {
464 left: self.dyn_box(),
465 right: Box::new(Literal(value)),
466 }
467 }
468 pub fn gt(&self, value: V) -> GtExpr {
469 GtExpr {
470 left: self.dyn_box(),
471 right: Box::new(Literal(value)),
472 }
473 }
474 pub fn lt(&self, value: V) -> LtExpr {
475 LtExpr {
476 left: self.dyn_box(),
477 right: Box::new(Literal(value)),
478 }
479 }
480 pub fn gte(&self, value: V) -> GteExpr {
481 GteExpr {
482 left: self.dyn_box(),
483 right: Box::new(Literal(value)),
484 }
485 }
486 pub fn lte(&self, value: V) -> LteExpr {
487 LteExpr {
488 left: self.dyn_box(),
489 right: Box::new(Literal(value)),
490 }
491 }
492 pub fn contains(&self, value: V) -> ContainsExpr {
493 ContainsExpr {
494 haystack: self.dyn_box(),
495 needle: Box::new(Literal(value)),
496 }
497 }
498
499 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
502 EqExpr {
503 left: self.dyn_box(),
504 right: Box::new(rhs),
505 }
506 }
507 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
508 NeExpr {
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.name))
516 }
517
518 fn dyn_box(&self) -> Box<dyn DynExpr> {
519 Box::new(Self {
520 name: self.name,
521 surreal_type: self.surreal_type,
522 _marker: self._marker,
523 })
524 }
525}
526
527macro_rules! combinators {
529 ($($t:ty),* $(,)?) => {$(
530 impl $t {
531 pub fn and(self, other: impl DynExpr + 'static) -> AndExpr {
532 AndExpr { left: Box::new(self), right: Box::new(other) }
533 }
534 pub fn or(self, other: impl DynExpr + 'static) -> OrExpr {
535 OrExpr { left: Box::new(self), right: Box::new(other) }
536 }
537 }
538 )*};
539}
540combinators!(
541 EqExpr,
542 NeExpr,
543 GtExpr,
544 LtExpr,
545 GteExpr,
546 LteExpr,
547 AndExpr,
548 OrExpr,
549 ContainsExpr,
550 NotExpr,
551 Raw
552);
553
554#[derive(Debug)]
556pub struct Grouped(pub Box<dyn DynExpr>);
557
558impl Grouped {
559 pub fn new(inner: impl DynExpr + 'static) -> Self {
560 Self(Box::new(inner))
561 }
562}
563
564impl DynExpr for Grouped {
565 fn render_dyn(&self, buf: &mut String) {
566 buf.push('(');
567 self.0.render_dyn(buf);
568 buf.push(')');
569 }
570}
571
572combinators!(Grouped, Func);
573
574#[derive(Debug)]
580pub struct Projection {
581 expr: Box<dyn DynExpr>,
582 alias: Option<&'static str>,
583}
584
585impl Projection {
586 pub fn new(expr: impl DynExpr + 'static) -> Self {
588 Self {
589 expr: Box::new(expr),
590 alias: None,
591 }
592 }
593 pub fn aliased(expr: impl DynExpr + 'static, alias: &'static str) -> Self {
595 Self {
596 expr: Box::new(expr),
597 alias: Some(alias),
598 }
599 }
600 pub fn render(&self, buf: &mut String) {
601 self.expr.render_dyn(buf);
602 if let Some(a) = self.alias {
603 buf.push_str(" AS ");
604 buf.push_str(a);
605 }
606 }
607}
608
609pub fn col(name: &'static str) -> Projection {
611 Projection::new(Raw(name.to_string()))
612}
613
614pub fn field(raw: &'static str, alias: &'static str) -> Projection {
616 Projection::aliased(Raw(raw.to_string()), alias)
617}
618
619#[derive(Debug, Clone)]
624pub struct ColumnMeta {
625 pub name: &'static str,
626 pub surreal_type: &'static str,
627}
628
629pub struct ColumnSet<T: SurrealRecord> {
631 pub cols: &'static [ColumnMeta],
632 pub _marker: std::marker::PhantomData<T>,
633}
634
635impl<T: SurrealRecord> std::fmt::Debug for ColumnSet<T> {
636 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
637 f.debug_struct("ColumnSet")
638 .field("cols", &self.cols)
639 .finish()
640 }
641}
642
643impl<T: SurrealRecord> DynExpr for ColumnSet<T> {
644 fn render_dyn(&self, buf: &mut String) {
645 buf.push('*');
646 }
647}
648
649#[derive(Debug, Clone, Copy)]
654pub enum Order {
655 Asc,
656 Desc,
657}
658
659impl Order {
660 pub fn render_suffix(&self) -> &'static str {
661 match self {
662 Order::Asc => "ASC",
663 Order::Desc => "DESC",
664 }
665 }
666}
667
668impl std::fmt::Display for Order {
669 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
670 f.write_str(self.render_suffix())
671 }
672}