1use crate::{
17 expr::{Column, DynExpr, Order, Path, Projection, RecordLink, SurrealQL},
18 types::{SurrealEdge, SurrealRecord, Thing},
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum Returning {
24 None,
26 Nothing,
28 Before,
30 After,
32 Diff,
34}
35
36impl Returning {
37 fn render(self, buf: &mut String) {
38 match self {
39 Returning::None => {}
40 Returning::Nothing => buf.push_str(" RETURN NONE"),
41 Returning::Before => buf.push_str(" RETURN BEFORE"),
42 Returning::After => buf.push_str(" RETURN AFTER"),
43 Returning::Diff => buf.push_str(" RETURN DIFF"),
44 }
45 }
46}
47
48enum Target {
51 Table(&'static str),
52 Record(RecordLink),
53}
54
55impl Target {
56 fn render(&self, buf: &mut String) {
57 match self {
58 Target::Table(t) => buf.push_str(t),
59 Target::Record(r) => r.render_dyn(buf),
60 }
61 }
62}
63
64pub struct Table<T: SurrealRecord> {
71 _marker: std::marker::PhantomData<T>,
72}
73
74impl<T: SurrealRecord> Table<T> {
75 pub fn new() -> Self {
77 Self {
78 _marker: std::marker::PhantomData,
79 }
80 }
81
82 pub fn select(self, _cols: crate::expr::ColumnSet<T>) -> Select<T> {
84 Select::bare()
85 }
86
87 pub fn project(self, fields: Vec<Projection>) -> Select<T> {
89 let mut s = Select::bare();
90 s.projections = fields;
91 s
92 }
93
94 pub fn project_path(self, path: Path, alias: &'static str) -> Select<T> {
97 let mut s = Select::bare();
98 s.projections = vec![Projection::aliased(path, alias)];
99 s
100 }
101
102 pub fn count(self, _field: &str) -> Select<T> {
104 let mut s = Select::bare();
105 s.count = true;
106 s.group_all = true;
107 s
108 }
109
110 pub fn insert(self) -> Insert<T> {
112 Insert {
113 data: Vec::new(),
114 return_fields: vec![],
115 }
116 }
117 pub fn create(self) -> Create<T> {
119 Create::for_table()
120 }
121 pub fn update(self) -> Update<T> {
123 Update::for_table()
124 }
125 pub fn upsert(self) -> Update<T> {
129 Update::for_upsert()
130 }
131 pub fn delete(self) -> Delete<T> {
133 Delete::for_table()
134 }
135}
136
137impl<T: SurrealRecord> Default for Table<T> {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143pub struct Select<T: SurrealRecord> {
150 _marker: std::marker::PhantomData<T>,
151 projections: Vec<Projection>,
152 filter: Option<Box<dyn DynExpr>>,
153 order: Vec<(String, Order)>,
154 limit: Option<u32>,
155 start: u32,
156 fetch: Vec<String>,
157 group_by: Vec<String>,
158 group_all: bool,
159 count: bool,
160 count_alias: Option<&'static str>,
161}
162
163impl<T: SurrealRecord> Select<T> {
164 fn bare() -> Self {
165 Select {
166 _marker: std::marker::PhantomData,
167 projections: Vec::new(),
168 filter: None,
169 order: Vec::new(),
170 limit: None,
171 start: 0,
172 fetch: Vec::new(),
173 group_by: Vec::new(),
174 group_all: false,
175 count: false,
176 count_alias: None,
177 }
178 }
179
180 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
181 self.filter = Some(Box::new(expr));
182 self
183 }
184 pub fn with_path(mut self, path: Path, alias: &'static str) -> Self {
189 if self.projections.is_empty() {
190 self.projections
191 .push(Projection::new(crate::expr::Raw("*".to_string())));
192 }
193 self.projections.push(Projection::aliased(path, alias));
194 self
195 }
196 pub fn limit(mut self, n: u32) -> Self {
197 self.limit = Some(n);
198 self
199 }
200 pub fn start(mut self, n: u32) -> Self {
201 self.start = n;
202 self
203 }
204 pub fn fetch(mut self, field: &str) -> Self {
205 self.fetch.push(field.to_string());
206 self
207 }
208 pub fn group_by<C: DynExpr>(mut self, col: C) -> Self {
209 let mut buf = String::new();
210 col.render_dyn(&mut buf);
211 self.group_by.push(buf);
212 self
213 }
214 pub fn group_all(mut self) -> Self {
216 self.group_all = true;
217 self
218 }
219 pub fn count_as(mut self, alias: &'static str) -> Self {
221 self.count = true;
222 self.count_alias = Some(alias);
223 self
224 }
225
226 pub fn order_by<C: DynExpr>(mut self, col: C, dir: Order) -> Self {
227 let mut buf = String::new();
228 col.render_dyn(&mut buf);
229 self.order.push((buf, dir));
230 self
231 }
232
233 pub fn order_asc<C: DynExpr>(self, col: C) -> Self {
234 self.order_by(col, Order::Asc)
235 }
236 pub fn order_desc<C: DynExpr>(self, col: C) -> Self {
237 self.order_by(col, Order::Desc)
238 }
239
240 fn render_select_list(&self, q: &mut String) {
241 if self.count {
242 q.push_str("count()");
243 if let Some(a) = self.count_alias {
244 q.push_str(" AS ");
245 q.push_str(a);
246 }
247 } else if self.projections.is_empty() {
248 q.push('*');
249 } else {
250 for (i, p) in self.projections.iter().enumerate() {
251 if i > 0 {
252 q.push_str(", ");
253 }
254 p.render(q);
255 }
256 }
257 }
258
259 pub fn to_surrealql(&self) -> String {
260 let mut q = String::from("SELECT ");
261 self.render_select_list(&mut q);
262 q.push_str(" FROM ");
263 q.push_str(T::table_name());
264 if let Some(ref f) = self.filter {
265 q.push_str(" WHERE ");
266 f.render_dyn(&mut q);
267 }
268 for (i, (col, dir)) in self.order.iter().enumerate() {
269 if i == 0 {
270 q.push_str(" ORDER BY ");
271 } else {
272 q.push_str(", ");
273 }
274 q.push_str(&format!("{col} {dir}"));
275 }
276 for (i, g) in self.group_by.iter().enumerate() {
277 if i == 0 {
278 q.push_str(" GROUP BY ");
279 } else {
280 q.push_str(", ");
281 }
282 q.push_str(g);
283 }
284 if self.group_all {
285 q.push_str(" GROUP ALL");
286 }
287 if self.start > 0 {
288 q.push_str(&format!(" START {}", self.start));
289 }
290 if let Some(n) = self.limit {
291 q.push_str(&format!(" LIMIT {n}"));
292 }
293 for f in &self.fetch {
294 q.push_str(&format!(" FETCH {f}"));
295 }
296 q
297 }
298}
299
300impl<T: SurrealRecord> std::fmt::Display for Select<T> {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 write!(f, "{}", self.to_surrealql())
303 }
304}
305
306pub struct Insert<T: SurrealRecord> {
313 data: Vec<T>,
314 return_fields: Vec<&'static str>,
315}
316
317impl<T: SurrealRecord> Insert<T> {
318 pub fn content(mut self, record: T) -> Self {
319 self.data.push(record);
320 self
321 }
322 pub fn return_field(mut self, field: &'static str) -> Self {
323 self.return_fields.push(field);
324 self
325 }
326 pub fn data(&self) -> &[T] {
327 &self.data
328 }
329
330 pub fn to_surrealql(&self) -> String
334 where
335 T: serde::Serialize,
336 {
337 let body = match self.data.as_slice() {
338 [] => "[]".to_string(),
339 [one] => serde_json::to_string(one).unwrap_or_else(|_| "{}".to_string()),
340 many => serde_json::to_string(many).unwrap_or_else(|_| "[]".to_string()),
341 };
342 let returning = if self.return_fields.is_empty() {
343 ""
344 } else {
345 " RETURN AFTER"
346 };
347 format!("INSERT INTO {} {}{}", T::table_name(), body, returning)
348 }
349}
350
351enum SetVal {
356 Assign(String, String),
358 Merge(String),
360 Content(String),
362}
363
364pub struct Update<T: SurrealRecord> {
367 _marker: std::marker::PhantomData<T>,
368 verb: &'static str,
369 target: Target,
370 filter: Option<Box<dyn DynExpr>>,
371 sets: Vec<SetVal>,
372 returning: Returning,
373}
374
375impl<T: SurrealRecord> Update<T> {
376 pub(crate) fn for_table() -> Self {
377 Self::with_verb("UPDATE")
378 }
379
380 pub(crate) fn for_upsert() -> Self {
383 Self::with_verb("UPSERT")
384 }
385
386 fn with_verb(verb: &'static str) -> Self {
387 Self {
388 _marker: std::marker::PhantomData,
389 verb,
390 target: Target::Table(T::table_name()),
391 filter: None,
392 sets: Vec::new(),
393 returning: Returning::None,
394 }
395 }
396
397 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
399 self.target = Target::Record(RecordLink::new(T::table_name(), id));
400 self
401 }
402
403 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
404 self.filter = Some(Box::new(expr));
405 self
406 }
407
408 pub fn set<C: SurrealQL>(mut self, col: Column<T, C>, value: C) -> Self {
410 let mut buf = String::new();
411 C::render_literal(&value, &mut buf);
412 self.sets.push(SetVal::Assign(col.name.to_string(), buf));
413 self
414 }
415 pub fn set_lit<C: SurrealQL>(mut self, col: &str, value: C) -> Self {
417 let mut buf = String::new();
418 C::render_literal(&value, &mut buf);
419 self.sets.push(SetVal::Assign(col.to_string(), buf));
420 self
421 }
422 pub fn set_expr(mut self, col: &str, expr: impl DynExpr) -> Self {
424 let mut buf = String::new();
425 expr.render_dyn(&mut buf);
426 self.sets.push(SetVal::Assign(col.to_string(), buf));
427 self
428 }
429 pub fn set_raw(mut self, col: &str, raw: impl Into<String>) -> Self {
431 self.sets.push(SetVal::Assign(col.to_string(), raw.into()));
432 self
433 }
434 pub fn merge(mut self, expr: impl DynExpr) -> Self {
436 let mut buf = String::new();
437 expr.render_dyn(&mut buf);
438 self.sets.push(SetVal::Merge(buf));
439 self
440 }
441 pub fn content(mut self, expr: impl DynExpr) -> Self {
443 let mut buf = String::new();
444 expr.render_dyn(&mut buf);
445 self.sets.push(SetVal::Content(buf));
446 self
447 }
448 pub fn returning(mut self, r: Returning) -> Self {
449 self.returning = r;
450 self
451 }
452
453 pub fn then_select(self, select: Select<T>) -> String {
456 format!("{};\n{}", self.to_surrealql(), select.to_surrealql())
457 }
458
459 pub fn to_surrealql(&self) -> String {
460 let mut q = String::from(self.verb);
461 q.push(' ');
462 self.target.render(&mut q);
463 let mut set_pairs = Vec::new();
465 let mut merge_clause = None;
466 let mut content_clause = None;
467 for s in &self.sets {
468 match s {
469 SetVal::Assign(k, v) => set_pairs.push(format!("{k} = {v}")),
470 SetVal::Merge(v) => merge_clause = Some(v.clone()),
471 SetVal::Content(v) => content_clause = Some(v.clone()),
472 }
473 }
474 if let Some(c) = content_clause {
475 q.push_str(" CONTENT ");
476 q.push_str(&c);
477 } else if let Some(m) = merge_clause {
478 q.push_str(" MERGE ");
479 q.push_str(&m);
480 } else if !set_pairs.is_empty() {
481 q.push_str(" SET ");
482 q.push_str(&set_pairs.join(", "));
483 }
484 if let Some(ref f) = self.filter {
485 q.push_str(" WHERE ");
486 f.render_dyn(&mut q);
487 }
488 self.returning.render(&mut q);
489 q
490 }
491}
492
493impl<T: SurrealRecord> std::fmt::Display for Update<T> {
494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
495 write!(f, "{}", self.to_surrealql())
496 }
497}
498
499enum CreateBody {
504 Content(String),
506 Set(Vec<(String, String)>),
508}
509
510pub struct Create<T: SurrealRecord> {
512 _marker: std::marker::PhantomData<T>,
513 target: Target,
514 body: CreateBody,
515 returning: Returning,
516}
517
518impl<T: SurrealRecord> Create<T> {
519 pub(crate) fn for_table() -> Self {
520 Self {
521 _marker: std::marker::PhantomData,
522 target: Target::Table(T::table_name()),
523 body: CreateBody::Set(Vec::new()),
524 returning: Returning::None,
525 }
526 }
527
528 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
530 self.target = Target::Record(RecordLink::new(T::table_name(), id));
531 self
532 }
533
534 pub fn content(mut self, expr: impl DynExpr) -> Self {
536 let mut buf = String::new();
537 expr.render_dyn(&mut buf);
538 self.body = CreateBody::Content(buf);
539 self
540 }
541
542 pub fn set_lit<C: SurrealQL>(mut self, col: &str, value: C) -> Self {
544 let mut buf = String::new();
545 C::render_literal(&value, &mut buf);
546 self.push_set(col, buf);
547 self
548 }
549 pub fn set_expr(mut self, col: &str, expr: impl DynExpr) -> Self {
551 let mut buf = String::new();
552 expr.render_dyn(&mut buf);
553 self.push_set(col, buf);
554 self
555 }
556 pub fn set_raw(mut self, col: &str, raw: impl Into<String>) -> Self {
558 self.push_set(col, raw.into());
559 self
560 }
561
562 fn push_set(&mut self, col: &str, rendered: String) {
563 match &mut self.body {
564 CreateBody::Set(v) => v.push((col.to_string(), rendered)),
565 CreateBody::Content(_) => {
566 self.body = CreateBody::Set(vec![(col.to_string(), rendered)]);
567 }
568 }
569 }
570
571 pub fn returning(mut self, r: Returning) -> Self {
572 self.returning = r;
573 self
574 }
575
576 pub fn then_select(self, select: Select<T>) -> String {
583 format!("{};\n{}", self.to_surrealql(), select.to_surrealql())
584 }
585
586 pub fn to_surrealql(&self) -> String {
587 let mut q = String::from("CREATE ");
588 self.target.render(&mut q);
589 match &self.body {
590 CreateBody::Content(c) => {
591 q.push_str(" CONTENT ");
592 q.push_str(c);
593 }
594 CreateBody::Set(pairs) if !pairs.is_empty() => {
595 q.push_str(" SET ");
596 q.push_str(
597 &pairs
598 .iter()
599 .map(|(k, v)| format!("{k} = {v}"))
600 .collect::<Vec<_>>()
601 .join(", "),
602 );
603 }
604 CreateBody::Set(_) => {}
605 }
606 self.returning.render(&mut q);
607 q
608 }
609}
610
611impl<T: SurrealRecord> std::fmt::Display for Create<T> {
612 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
613 write!(f, "{}", self.to_surrealql())
614 }
615}
616
617pub struct Delete<T: SurrealRecord> {
623 _marker: std::marker::PhantomData<T>,
624 target: Target,
625 filter: Option<Box<dyn DynExpr>>,
626 returning: Returning,
627}
628
629impl<T: SurrealRecord> Delete<T> {
630 pub(crate) fn for_table() -> Self {
631 Self {
632 _marker: std::marker::PhantomData,
633 target: Target::Table(T::table_name()),
634 filter: None,
635 returning: Returning::None,
636 }
637 }
638 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
640 self.target = Target::Record(RecordLink::new(T::table_name(), id));
641 self
642 }
643 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
644 self.filter = Some(Box::new(expr));
645 self
646 }
647 pub fn returning(mut self, r: Returning) -> Self {
648 self.returning = r;
649 self
650 }
651
652 pub fn then_select(self, select: Select<T>) -> String {
655 format!("{};\n{}", self.to_surrealql(), select.to_surrealql())
656 }
657
658 pub fn to_surrealql(&self) -> String {
659 let mut q = String::from("DELETE ");
660 self.target.render(&mut q);
661 if let Some(ref f) = self.filter {
662 q.push_str(" WHERE ");
663 f.render_dyn(&mut q);
664 }
665 self.returning.render(&mut q);
666 q
667 }
668}
669
670impl<T: SurrealRecord> std::fmt::Display for Delete<T> {
671 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
672 write!(f, "{}", self.to_surrealql())
673 }
674}
675
676#[derive(Default)]
683pub struct Batch {
684 statements: Vec<String>,
685}
686
687impl Batch {
688 pub fn new() -> Self {
689 Self {
690 statements: Vec::new(),
691 }
692 }
693 pub fn push(mut self, stmt: impl ToString) -> Self {
694 self.statements.push(stmt.to_string());
695 self
696 }
697 pub fn to_surrealql(&self) -> String {
698 self.statements.join(";\n")
699 }
700 pub fn len(&self) -> usize {
702 self.statements.len()
703 }
704 pub fn is_empty(&self) -> bool {
705 self.statements.is_empty()
706 }
707}
708
709impl std::fmt::Display for Batch {
710 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
711 write!(f, "{}", self.to_surrealql())
712 }
713}
714
715fn record_id(thing: &Thing<impl SurrealRecord>, buf: &mut String) {
721 buf.push_str(thing.table());
722 buf.push(':');
723 thing.key.render_id(buf);
724}
725
726fn record_id_string(thing: &Thing<impl SurrealRecord>) -> String {
728 let mut s = String::new();
729 record_id(thing, &mut s);
730 s
731}
732
733pub struct Relate<E: SurrealEdge> {
736 _marker: std::marker::PhantomData<E>,
737}
738
739impl<E: SurrealEdge> Relate<E> {
740 pub fn new() -> Self {
741 Self {
742 _marker: std::marker::PhantomData,
743 }
744 }
745
746 pub fn to_surrealql(
747 from: &Thing<impl SurrealRecord>,
748 to: &Thing<impl SurrealRecord>,
749 ) -> String {
750 let mut q = String::from("RELATE ");
751 record_id(from, &mut q);
752 q.push_str(" -> ");
753 q.push_str(E::edge_name());
754 q.push_str(" -> ");
755 record_id(to, &mut q);
756 q
757 }
758}
759
760impl<E: SurrealEdge> Default for Relate<E> {
761 fn default() -> Self {
762 Self::new()
763 }
764}
765
766pub struct RelateEdge<E: SurrealEdge> {
776 _marker: std::marker::PhantomData<E>,
777 from_label: String,
778 to_label: String,
779 content_json: Option<serde_json::Value>,
780}
781
782impl<E: SurrealEdge> RelateEdge<E> {
783 pub fn from(from: &Thing<impl SurrealRecord>) -> Self {
784 Self {
785 _marker: std::marker::PhantomData,
786 from_label: record_id_string(from),
787 to_label: String::new(),
788 content_json: None,
789 }
790 }
791
792 pub fn to(mut self, to: &Thing<impl SurrealRecord>) -> Self {
793 self.to_label = record_id_string(to);
794 self
795 }
796
797 pub fn content(mut self, edge: &impl serde::Serialize) -> Self {
799 self.content_json = serde_json::to_value(edge).ok();
800 self
801 }
802
803 pub fn build(&self) -> String {
804 let mut q = format!(
805 "RELATE {} -> {} -> {}",
806 self.from_label,
807 E::edge_name(),
808 self.to_label
809 );
810 if let Some(ref c) = self.content_json {
811 q.push_str(&format!(
812 " CONTENT {}",
813 serde_json::to_string(c).unwrap_or_default()
814 ));
815 }
816 q
817 }
818}