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) { (**self).render_dyn(buf); }
19}
20
21pub trait Expr: DynExpr {
26 fn ty_hint(&self) -> &'static str;
27}
28
29impl<E: DynExpr> Expr for E {
32 fn ty_hint(&self) -> &'static str { "any" }
33}
34
35pub trait SurrealQL: fmt::Debug + Clone + Send + Sync + 'static {
40 fn surreal_type() -> &'static str;
41 fn render_literal(value: &Self, buf: &mut String);
42}
43
44impl SurrealQL for String {
45 fn surreal_type() -> &'static str { "string" }
46 fn render_literal(value: &Self, buf: &mut String) {
47 let escaped = value.replace('\\', "\\\\").replace('\'', "\\'");
48 buf.push('\'');
49 buf.push_str(&escaped);
50 buf.push('\'');
51 }
52}
53
54impl SurrealQL for bool {
55 fn surreal_type() -> &'static str { "bool" }
56 fn render_literal(value: &Self, buf: &mut String) {
57 buf.push_str(if *value { "true" } else { "false" });
58 }
59}
60
61macro_rules! surreal_display {
62 ($t:ty, $name:literal) => {
63 impl SurrealQL for $t {
64 fn surreal_type() -> &'static str { $name }
65 fn render_literal(value: &Self, buf: &mut String) {
66 use std::fmt::Write;
67 let _ = write!(buf, "{value}");
68 }
69 }
70 };
71}
72surreal_display!(i64, "int");
73surreal_display!(i32, "int");
74surreal_display!(i16, "int");
75surreal_display!(i8, "int");
76surreal_display!(f64, "float");
77surreal_display!(f32, "float");
78surreal_display!(u32, "int");
79surreal_display!(u64, "int");
80surreal_display!(u16, "int");
81surreal_display!(u8, "int");
82
83impl SurrealQL for chrono::DateTime<chrono::Utc> {
84 fn surreal_type() -> &'static str { "datetime" }
85 fn render_literal(value: &Self, buf: &mut String) {
86 buf.push('\'');
87 buf.push_str(&value.to_rfc3339());
88 buf.push('\'');
89 }
90}
91
92impl SurrealQL for uuid::Uuid {
93 fn surreal_type() -> &'static str { "uuid" }
94 fn render_literal(value: &Self, buf: &mut String) {
95 use std::fmt::Write;
96 buf.push('\'');
97 let _ = write!(buf, "{value}");
98 buf.push('\'');
99 }
100}
101
102impl SurrealQL for serde_json::Value {
103 fn surreal_type() -> &'static str { "object" }
104 fn render_literal(value: &Self, buf: &mut String) {
105 use std::fmt::Write;
109 let _ = write!(buf, "{value}");
110 }
111}
112
113impl<T: SurrealQL> SurrealQL for Option<T> {
114 fn surreal_type() -> &'static str { T::surreal_type() }
115 fn render_literal(value: &Self, buf: &mut String) {
116 match value {
117 Some(v) => T::render_literal(v, buf),
118 None => buf.push_str("NONE"),
119 }
120 }
121}
122
123impl<T: crate::types::SurrealRecord> SurrealQL for crate::types::Thing<T> {
124 fn surreal_type() -> &'static str { "record" }
125 fn render_literal(value: &Self, buf: &mut String) {
126 use std::fmt::Write;
127 let _ = write!(buf, "{}:{}", T::table_name(), value.key);
128 }
129}
130
131#[derive(Debug, Clone)]
136pub struct Literal<V: SurrealQL>(pub V);
137
138impl<V: SurrealQL> DynExpr for Literal<V> {
139 fn render_dyn(&self, buf: &mut String) { V::render_literal(&self.0, buf); }
140}
141
142pub struct Column<T: SurrealRecord, V: SurrealQL> {
147 pub name: &'static str,
148 pub surreal_type: &'static str,
149 pub _marker: std::marker::PhantomData<(T, V)>,
150}
151
152impl<T: SurrealRecord, V: SurrealQL> std::fmt::Debug for Column<T, V> {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 f.debug_struct("Column").field("name", &self.name).finish()
155 }
156}
157
158impl<T: SurrealRecord, V: SurrealQL> Clone for Column<T, V> {
159 fn clone(&self) -> Self { *self }
160}
161impl<T: SurrealRecord, V: SurrealQL> Copy for Column<T, V> {}
162
163impl<T: SurrealRecord, V: SurrealQL> DynExpr for Column<T, V> {
164 fn render_dyn(&self, buf: &mut String) { buf.push_str(self.name); }
165}
166
167#[derive(Debug, Clone, Copy)]
175pub struct Ident(pub &'static str);
176
177pub fn ident(name: &'static str) -> Ident { Ident(name) }
179
180impl Ident {
181 fn dyn_box(&self) -> Box<dyn DynExpr> { Box::new(Raw(self.0.to_string())) }
182
183 pub fn eq<V: SurrealQL>(&self, v: V) -> EqExpr {
184 EqExpr { left: self.dyn_box(), right: Box::new(Literal(v)) }
185 }
186 pub fn ne<V: SurrealQL>(&self, v: V) -> NeExpr {
187 NeExpr { left: self.dyn_box(), right: Box::new(Literal(v)) }
188 }
189 pub fn gt<V: SurrealQL>(&self, v: V) -> GtExpr {
190 GtExpr { left: self.dyn_box(), right: Box::new(Literal(v)) }
191 }
192 pub fn lt<V: SurrealQL>(&self, v: V) -> LtExpr {
193 LtExpr { left: self.dyn_box(), right: Box::new(Literal(v)) }
194 }
195 pub fn gte<V: SurrealQL>(&self, v: V) -> GteExpr {
196 GteExpr { left: self.dyn_box(), right: Box::new(Literal(v)) }
197 }
198 pub fn lte<V: SurrealQL>(&self, v: V) -> LteExpr {
199 LteExpr { left: self.dyn_box(), right: Box::new(Literal(v)) }
200 }
201 pub fn contains<V: SurrealQL>(&self, v: V) -> ContainsExpr {
202 ContainsExpr { haystack: self.dyn_box(), needle: Box::new(Literal(v)) }
203 }
204 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
206 EqExpr { left: self.dyn_box(), right: Box::new(rhs) }
207 }
208 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
209 NeExpr { left: self.dyn_box(), right: Box::new(rhs) }
210 }
211 pub fn is_none(&self) -> Raw { Raw(format!("{} IS NONE", self.0)) }
213}
214
215impl DynExpr for Ident {
216 fn render_dyn(&self, buf: &mut String) { buf.push_str(self.0); }
217}
218
219#[derive(Debug, Clone)]
226pub struct Raw(pub String);
227
228impl Raw {
229 pub fn new(s: impl Into<String>) -> Self { Self(s.into()) }
230}
231
232impl DynExpr for Raw {
233 fn render_dyn(&self, buf: &mut String) { buf.push_str(&self.0); }
234}
235
236#[derive(Debug, Clone)]
238pub struct NoneLit;
239impl DynExpr for NoneLit {
240 fn render_dyn(&self, buf: &mut String) { buf.push_str("NONE"); }
241}
242
243#[derive(Debug)]
250pub struct RecordLink {
251 table: &'static str,
252 key: Box<dyn DynExpr>,
253}
254
255impl RecordLink {
256 pub fn new<V: SurrealQL>(table: &'static str, key: V) -> Self {
258 Self { table, key: Box::new(Literal(key)) }
259 }
260 pub fn from_expr(table: &'static str, key: impl DynExpr + 'static) -> Self {
262 Self { table, key: Box::new(key) }
263 }
264}
265
266impl DynExpr for RecordLink {
267 fn render_dyn(&self, buf: &mut String) {
268 buf.push_str("type::record('");
269 buf.push_str(self.table);
270 buf.push_str("', ");
271 self.key.render_dyn(buf);
272 buf.push(')');
273 }
274}
275
276#[derive(Debug)]
282pub struct Func {
283 name: &'static str,
284 args: Vec<Box<dyn DynExpr>>,
285}
286
287impl Func {
288 pub fn new(name: &'static str, args: Vec<Box<dyn DynExpr>>) -> Self {
289 Self { name, args }
290 }
291 pub fn of(name: &'static str, ident: &'static str) -> Self {
294 Self { name, args: vec![Box::new(Raw(ident.to_string()))] }
295 }
296}
297
298impl DynExpr for Func {
299 fn render_dyn(&self, buf: &mut String) {
300 buf.push_str(self.name);
301 buf.push('(');
302 for (i, a) in self.args.iter().enumerate() {
303 if i > 0 { buf.push_str(", "); }
304 a.render_dyn(buf);
305 }
306 buf.push(')');
307 }
308}
309
310macro_rules! binop {
315 ($name:ident, $op:literal) => {
316 #[derive(Debug)]
317 pub struct $name {
318 pub(crate) left: Box<dyn DynExpr>,
319 pub(crate) right: Box<dyn DynExpr>,
320 }
321 impl DynExpr for $name {
322 fn render_dyn(&self, buf: &mut String) {
323 self.left.render_dyn(buf);
324 buf.push(' ');
325 buf.push_str($op);
326 buf.push(' ');
327 self.right.render_dyn(buf);
328 }
329 }
330 };
331}
332
333binop!(EqExpr, "=");
334binop!(NeExpr, "!=");
335binop!(GtExpr, ">");
336binop!(LtExpr, "<");
337binop!(GteExpr, ">=");
338binop!(LteExpr, "<=");
339binop!(AndExpr, "AND");
340binop!(OrExpr, "OR");
341
342#[derive(Debug)]
343pub struct NotExpr {
344 pub(crate) inner: Box<dyn DynExpr>,
345}
346
347impl DynExpr for NotExpr {
348 fn render_dyn(&self, buf: &mut String) {
349 buf.push_str("NOT ");
350 self.inner.render_dyn(buf);
351 }
352}
353
354#[derive(Debug)]
355pub struct ContainsExpr {
356 pub(crate) haystack: Box<dyn DynExpr>,
357 pub(crate) needle: Box<dyn DynExpr>,
358}
359
360impl DynExpr for ContainsExpr {
361 fn render_dyn(&self, buf: &mut String) {
362 self.haystack.render_dyn(buf);
363 buf.push_str(" CONTAINS ");
364 self.needle.render_dyn(buf);
365 }
366}
367
368impl<T: SurrealRecord, V: SurrealQL> Column<T, V> {
373 pub fn eq(&self, value: V) -> EqExpr {
374 EqExpr { left: self.dyn_box(), right: Box::new(Literal(value)) }
375 }
376 pub fn ne(&self, value: V) -> NeExpr {
377 NeExpr { left: self.dyn_box(), right: Box::new(Literal(value)) }
378 }
379 pub fn gt(&self, value: V) -> GtExpr {
380 GtExpr { left: self.dyn_box(), right: Box::new(Literal(value)) }
381 }
382 pub fn lt(&self, value: V) -> LtExpr {
383 LtExpr { left: self.dyn_box(), right: Box::new(Literal(value)) }
384 }
385 pub fn gte(&self, value: V) -> GteExpr {
386 GteExpr { left: self.dyn_box(), right: Box::new(Literal(value)) }
387 }
388 pub fn lte(&self, value: V) -> LteExpr {
389 LteExpr { left: self.dyn_box(), right: Box::new(Literal(value)) }
390 }
391 pub fn contains(&self, value: V) -> ContainsExpr {
392 ContainsExpr { haystack: self.dyn_box(), needle: Box::new(Literal(value)) }
393 }
394
395 pub fn eq_expr(&self, rhs: impl DynExpr + 'static) -> EqExpr {
398 EqExpr { left: self.dyn_box(), right: Box::new(rhs) }
399 }
400 pub fn ne_expr(&self, rhs: impl DynExpr + 'static) -> NeExpr {
401 NeExpr { left: self.dyn_box(), right: Box::new(rhs) }
402 }
403 pub fn is_none(&self) -> Raw {
405 Raw(format!("{} IS NONE", self.name))
406 }
407
408 fn dyn_box(&self) -> Box<dyn DynExpr> {
409 Box::new(Self { name: self.name, surreal_type: self.surreal_type, _marker: self._marker })
410 }
411}
412
413macro_rules! combinators {
415 ($($t:ty),* $(,)?) => {$(
416 impl $t {
417 pub fn and(self, other: impl DynExpr + 'static) -> AndExpr {
418 AndExpr { left: Box::new(self), right: Box::new(other) }
419 }
420 pub fn or(self, other: impl DynExpr + 'static) -> OrExpr {
421 OrExpr { left: Box::new(self), right: Box::new(other) }
422 }
423 }
424 )*};
425}
426combinators!(EqExpr, NeExpr, GtExpr, LtExpr, GteExpr, LteExpr, AndExpr, OrExpr, ContainsExpr, NotExpr, Raw);
427
428#[derive(Debug)]
430pub struct Grouped(pub Box<dyn DynExpr>);
431
432impl Grouped {
433 pub fn new(inner: impl DynExpr + 'static) -> Self { Self(Box::new(inner)) }
434}
435
436impl DynExpr for Grouped {
437 fn render_dyn(&self, buf: &mut String) {
438 buf.push('(');
439 self.0.render_dyn(buf);
440 buf.push(')');
441 }
442}
443
444combinators!(Grouped, Func);
445
446#[derive(Debug)]
452pub struct Projection {
453 expr: Box<dyn DynExpr>,
454 alias: Option<&'static str>,
455}
456
457impl Projection {
458 pub fn new(expr: impl DynExpr + 'static) -> Self {
460 Self { expr: Box::new(expr), alias: None }
461 }
462 pub fn aliased(expr: impl DynExpr + 'static, alias: &'static str) -> Self {
464 Self { expr: Box::new(expr), alias: Some(alias) }
465 }
466 pub fn render(&self, buf: &mut String) {
467 self.expr.render_dyn(buf);
468 if let Some(a) = self.alias {
469 buf.push_str(" AS ");
470 buf.push_str(a);
471 }
472 }
473}
474
475pub fn col(name: &'static str) -> Projection {
477 Projection::new(Raw(name.to_string()))
478}
479
480pub fn field(raw: &'static str, alias: &'static str) -> Projection {
482 Projection::aliased(Raw(raw.to_string()), alias)
483}
484
485#[derive(Debug, Clone)]
490pub struct ColumnMeta {
491 pub name: &'static str,
492 pub surreal_type: &'static str,
493}
494
495pub struct ColumnSet<T: SurrealRecord> {
497 pub cols: &'static [ColumnMeta],
498 pub _marker: std::marker::PhantomData<T>,
499}
500
501impl<T: SurrealRecord> std::fmt::Debug for ColumnSet<T> {
502 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
503 f.debug_struct("ColumnSet").field("cols", &self.cols).finish()
504 }
505}
506
507impl<T: SurrealRecord> DynExpr for ColumnSet<T> {
508 fn render_dyn(&self, buf: &mut String) { buf.push('*'); }
509}
510
511#[derive(Debug, Clone, Copy)]
516pub enum Order {
517 Asc,
518 Desc,
519}
520
521impl Order {
522 pub fn render_suffix(&self) -> &'static str {
523 match self { Order::Asc => "ASC", Order::Desc => "DESC" }
524 }
525}
526
527impl std::fmt::Display for Order {
528 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529 f.write_str(self.render_suffix())
530 }
531}