1use crate::{
2 expr::{Column, DynExpr, Order, Projection, RecordLink, SurrealQL},
3 types::{SurrealEdge, SurrealRecord, Thing},
4};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum Returning {
9 None,
11 Nothing,
13 Before,
15 After,
17 Diff,
19}
20
21impl Returning {
22 fn render(self, buf: &mut String) {
23 match self {
24 Returning::None => {}
25 Returning::Nothing => buf.push_str(" RETURN NONE"),
26 Returning::Before => buf.push_str(" RETURN BEFORE"),
27 Returning::After => buf.push_str(" RETURN AFTER"),
28 Returning::Diff => buf.push_str(" RETURN DIFF"),
29 }
30 }
31}
32
33enum Target {
36 Table(&'static str),
37 Record(RecordLink),
38}
39
40impl Target {
41 fn render(&self, buf: &mut String) {
42 match self {
43 Target::Table(t) => buf.push_str(t),
44 Target::Record(r) => r.render_dyn(buf),
45 }
46 }
47}
48
49pub struct Table<T: SurrealRecord> {
54 _marker: std::marker::PhantomData<T>,
55}
56
57impl<T: SurrealRecord> Table<T> {
58 pub fn new() -> Self {
59 Self {
60 _marker: std::marker::PhantomData,
61 }
62 }
63
64 pub fn select(self, _cols: crate::expr::ColumnSet<T>) -> Select<T> {
65 Select::bare()
66 }
67
68 pub fn project(self, fields: Vec<Projection>) -> Select<T> {
70 let mut s = Select::bare();
71 s.projections = fields;
72 s
73 }
74
75 pub fn count(self, _field: &str) -> Select<T> {
77 let mut s = Select::bare();
78 s.count = true;
79 s.group_all = true;
80 s
81 }
82
83 pub fn insert(self) -> Insert<T> {
84 Insert {
85 data: Vec::new(),
86 return_fields: vec![],
87 }
88 }
89 pub fn create(self) -> Create<T> {
90 Create::for_table()
91 }
92 pub fn update(self) -> Update<T> {
93 Update::for_table()
94 }
95 pub fn delete(self) -> Delete<T> {
96 Delete::for_table()
97 }
98}
99
100impl<T: SurrealRecord> Default for Table<T> {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106pub struct Select<T: SurrealRecord> {
111 _marker: std::marker::PhantomData<T>,
112 projections: Vec<Projection>,
113 filter: Option<Box<dyn DynExpr>>,
114 order: Vec<(String, Order)>,
115 limit: Option<u32>,
116 start: u32,
117 fetch: Vec<String>,
118 group_by: Vec<String>,
119 group_all: bool,
120 count: bool,
121 count_alias: Option<&'static str>,
122}
123
124impl<T: SurrealRecord> Select<T> {
125 fn bare() -> Self {
126 Select {
127 _marker: std::marker::PhantomData,
128 projections: Vec::new(),
129 filter: None,
130 order: Vec::new(),
131 limit: None,
132 start: 0,
133 fetch: Vec::new(),
134 group_by: Vec::new(),
135 group_all: false,
136 count: false,
137 count_alias: None,
138 }
139 }
140
141 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
142 self.filter = Some(Box::new(expr));
143 self
144 }
145 pub fn limit(mut self, n: u32) -> Self {
146 self.limit = Some(n);
147 self
148 }
149 pub fn start(mut self, n: u32) -> Self {
150 self.start = n;
151 self
152 }
153 pub fn fetch(mut self, field: &str) -> Self {
154 self.fetch.push(field.to_string());
155 self
156 }
157 pub fn group_by<C: DynExpr>(mut self, col: C) -> Self {
158 let mut buf = String::new();
159 col.render_dyn(&mut buf);
160 self.group_by.push(buf);
161 self
162 }
163 pub fn group_all(mut self) -> Self {
165 self.group_all = true;
166 self
167 }
168 pub fn count_as(mut self, alias: &'static str) -> Self {
170 self.count = true;
171 self.count_alias = Some(alias);
172 self
173 }
174
175 pub fn order_by<C: DynExpr>(mut self, col: C, dir: Order) -> Self {
176 let mut buf = String::new();
177 col.render_dyn(&mut buf);
178 self.order.push((buf, dir));
179 self
180 }
181
182 pub fn order_asc<C: DynExpr>(self, col: C) -> Self {
183 self.order_by(col, Order::Asc)
184 }
185 pub fn order_desc<C: DynExpr>(self, col: C) -> Self {
186 self.order_by(col, Order::Desc)
187 }
188
189 fn render_select_list(&self, q: &mut String) {
190 if self.count {
191 q.push_str("count()");
192 if let Some(a) = self.count_alias {
193 q.push_str(" AS ");
194 q.push_str(a);
195 }
196 } else if self.projections.is_empty() {
197 q.push('*');
198 } else {
199 for (i, p) in self.projections.iter().enumerate() {
200 if i > 0 {
201 q.push_str(", ");
202 }
203 p.render(q);
204 }
205 }
206 }
207
208 pub fn to_surrealql(&self) -> String {
209 let mut q = String::from("SELECT ");
210 self.render_select_list(&mut q);
211 q.push_str(" FROM ");
212 q.push_str(T::table_name());
213 if let Some(ref f) = self.filter {
214 q.push_str(" WHERE ");
215 f.render_dyn(&mut q);
216 }
217 for (i, (col, dir)) in self.order.iter().enumerate() {
218 if i == 0 {
219 q.push_str(" ORDER BY ");
220 } else {
221 q.push_str(", ");
222 }
223 q.push_str(&format!("{col} {dir}"));
224 }
225 for (i, g) in self.group_by.iter().enumerate() {
226 if i == 0 {
227 q.push_str(" GROUP BY ");
228 } else {
229 q.push_str(", ");
230 }
231 q.push_str(g);
232 }
233 if self.group_all {
234 q.push_str(" GROUP ALL");
235 }
236 if self.start > 0 {
237 q.push_str(&format!(" START {}", self.start));
238 }
239 if let Some(n) = self.limit {
240 q.push_str(&format!(" LIMIT {n}"));
241 }
242 for f in &self.fetch {
243 q.push_str(&format!(" FETCH {f}"));
244 }
245 q
246 }
247}
248
249impl<T: SurrealRecord> std::fmt::Display for Select<T> {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 write!(f, "{}", self.to_surrealql())
252 }
253}
254
255pub struct Insert<T: SurrealRecord> {
260 data: Vec<T>,
261 return_fields: Vec<&'static str>,
262}
263
264impl<T: SurrealRecord> Insert<T> {
265 pub fn content(mut self, record: T) -> Self {
266 self.data.push(record);
267 self
268 }
269 pub fn return_field(mut self, field: &'static str) -> Self {
270 self.return_fields.push(field);
271 self
272 }
273 pub fn data(&self) -> &[T] {
274 &self.data
275 }
276
277 pub fn to_surrealql(&self) -> String
281 where
282 T: serde::Serialize,
283 {
284 let body = match self.data.as_slice() {
285 [] => "[]".to_string(),
286 [one] => serde_json::to_string(one).unwrap_or_else(|_| "{}".to_string()),
287 many => serde_json::to_string(many).unwrap_or_else(|_| "[]".to_string()),
288 };
289 let returning = if self.return_fields.is_empty() {
290 ""
291 } else {
292 " RETURN AFTER"
293 };
294 format!("INSERT INTO {} {}{}", T::table_name(), body, returning)
295 }
296}
297
298enum SetVal {
303 Assign(String, String),
305 Merge(String),
307 Content(String),
309}
310
311pub struct Update<T: SurrealRecord> {
312 _marker: std::marker::PhantomData<T>,
313 target: Target,
314 filter: Option<Box<dyn DynExpr>>,
315 sets: Vec<SetVal>,
316 returning: Returning,
317}
318
319impl<T: SurrealRecord> Update<T> {
320 pub(crate) fn for_table() -> Self {
321 Self {
322 _marker: std::marker::PhantomData,
323 target: Target::Table(T::table_name()),
324 filter: None,
325 sets: Vec::new(),
326 returning: Returning::None,
327 }
328 }
329
330 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
332 self.target = Target::Record(RecordLink::new(T::table_name(), id));
333 self
334 }
335
336 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
337 self.filter = Some(Box::new(expr));
338 self
339 }
340
341 pub fn set<C: SurrealQL>(mut self, col: Column<T, C>, value: C) -> Self {
343 let mut buf = String::new();
344 C::render_literal(&value, &mut buf);
345 self.sets.push(SetVal::Assign(col.name.to_string(), buf));
346 self
347 }
348 pub fn set_lit<C: SurrealQL>(mut self, col: &str, value: C) -> Self {
350 let mut buf = String::new();
351 C::render_literal(&value, &mut buf);
352 self.sets.push(SetVal::Assign(col.to_string(), buf));
353 self
354 }
355 pub fn set_expr(mut self, col: &str, expr: impl DynExpr) -> Self {
357 let mut buf = String::new();
358 expr.render_dyn(&mut buf);
359 self.sets.push(SetVal::Assign(col.to_string(), buf));
360 self
361 }
362 pub fn set_raw(mut self, col: &str, raw: impl Into<String>) -> Self {
364 self.sets.push(SetVal::Assign(col.to_string(), raw.into()));
365 self
366 }
367 pub fn merge(mut self, expr: impl DynExpr) -> Self {
369 let mut buf = String::new();
370 expr.render_dyn(&mut buf);
371 self.sets.push(SetVal::Merge(buf));
372 self
373 }
374 pub fn content(mut self, expr: impl DynExpr) -> Self {
376 let mut buf = String::new();
377 expr.render_dyn(&mut buf);
378 self.sets.push(SetVal::Content(buf));
379 self
380 }
381 pub fn returning(mut self, r: Returning) -> Self {
382 self.returning = r;
383 self
384 }
385
386 pub fn to_surrealql(&self) -> String {
387 let mut q = String::from("UPDATE ");
388 self.target.render(&mut q);
389 let mut set_pairs = Vec::new();
391 let mut merge_clause = None;
392 let mut content_clause = None;
393 for s in &self.sets {
394 match s {
395 SetVal::Assign(k, v) => set_pairs.push(format!("{k} = {v}")),
396 SetVal::Merge(v) => merge_clause = Some(v.clone()),
397 SetVal::Content(v) => content_clause = Some(v.clone()),
398 }
399 }
400 if let Some(c) = content_clause {
401 q.push_str(" CONTENT ");
402 q.push_str(&c);
403 } else if let Some(m) = merge_clause {
404 q.push_str(" MERGE ");
405 q.push_str(&m);
406 } else if !set_pairs.is_empty() {
407 q.push_str(" SET ");
408 q.push_str(&set_pairs.join(", "));
409 }
410 if let Some(ref f) = self.filter {
411 q.push_str(" WHERE ");
412 f.render_dyn(&mut q);
413 }
414 self.returning.render(&mut q);
415 q
416 }
417}
418
419impl<T: SurrealRecord> std::fmt::Display for Update<T> {
420 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
421 write!(f, "{}", self.to_surrealql())
422 }
423}
424
425enum CreateBody {
430 Content(String),
432 Set(Vec<(String, String)>),
434}
435
436pub struct Create<T: SurrealRecord> {
438 _marker: std::marker::PhantomData<T>,
439 target: Target,
440 body: CreateBody,
441 returning: Returning,
442}
443
444impl<T: SurrealRecord> Create<T> {
445 pub(crate) fn for_table() -> Self {
446 Self {
447 _marker: std::marker::PhantomData,
448 target: Target::Table(T::table_name()),
449 body: CreateBody::Set(Vec::new()),
450 returning: Returning::None,
451 }
452 }
453
454 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
456 self.target = Target::Record(RecordLink::new(T::table_name(), id));
457 self
458 }
459
460 pub fn content(mut self, expr: impl DynExpr) -> Self {
462 let mut buf = String::new();
463 expr.render_dyn(&mut buf);
464 self.body = CreateBody::Content(buf);
465 self
466 }
467
468 pub fn set_lit<C: SurrealQL>(mut self, col: &str, value: C) -> Self {
470 let mut buf = String::new();
471 C::render_literal(&value, &mut buf);
472 self.push_set(col, buf);
473 self
474 }
475 pub fn set_expr(mut self, col: &str, expr: impl DynExpr) -> Self {
477 let mut buf = String::new();
478 expr.render_dyn(&mut buf);
479 self.push_set(col, buf);
480 self
481 }
482 pub fn set_raw(mut self, col: &str, raw: impl Into<String>) -> Self {
484 self.push_set(col, raw.into());
485 self
486 }
487
488 fn push_set(&mut self, col: &str, rendered: String) {
489 match &mut self.body {
490 CreateBody::Set(v) => v.push((col.to_string(), rendered)),
491 CreateBody::Content(_) => {
492 self.body = CreateBody::Set(vec![(col.to_string(), rendered)]);
493 }
494 }
495 }
496
497 pub fn returning(mut self, r: Returning) -> Self {
498 self.returning = r;
499 self
500 }
501
502 pub fn to_surrealql(&self) -> String {
503 let mut q = String::from("CREATE ");
504 self.target.render(&mut q);
505 match &self.body {
506 CreateBody::Content(c) => {
507 q.push_str(" CONTENT ");
508 q.push_str(c);
509 }
510 CreateBody::Set(pairs) if !pairs.is_empty() => {
511 q.push_str(" SET ");
512 q.push_str(
513 &pairs
514 .iter()
515 .map(|(k, v)| format!("{k} = {v}"))
516 .collect::<Vec<_>>()
517 .join(", "),
518 );
519 }
520 CreateBody::Set(_) => {}
521 }
522 self.returning.render(&mut q);
523 q
524 }
525}
526
527impl<T: SurrealRecord> std::fmt::Display for Create<T> {
528 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529 write!(f, "{}", self.to_surrealql())
530 }
531}
532
533pub struct Delete<T: SurrealRecord> {
538 _marker: std::marker::PhantomData<T>,
539 target: Target,
540 filter: Option<Box<dyn DynExpr>>,
541 returning: Returning,
542}
543
544impl<T: SurrealRecord> Delete<T> {
545 pub(crate) fn for_table() -> Self {
546 Self {
547 _marker: std::marker::PhantomData,
548 target: Target::Table(T::table_name()),
549 filter: None,
550 returning: Returning::None,
551 }
552 }
553 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
555 self.target = Target::Record(RecordLink::new(T::table_name(), id));
556 self
557 }
558 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
559 self.filter = Some(Box::new(expr));
560 self
561 }
562 pub fn returning(mut self, r: Returning) -> Self {
563 self.returning = r;
564 self
565 }
566 pub fn to_surrealql(&self) -> String {
567 let mut q = String::from("DELETE ");
568 self.target.render(&mut q);
569 if let Some(ref f) = self.filter {
570 q.push_str(" WHERE ");
571 f.render_dyn(&mut q);
572 }
573 self.returning.render(&mut q);
574 q
575 }
576}
577
578impl<T: SurrealRecord> std::fmt::Display for Delete<T> {
579 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580 write!(f, "{}", self.to_surrealql())
581 }
582}
583
584#[derive(Default)]
591pub struct Batch {
592 statements: Vec<String>,
593}
594
595impl Batch {
596 pub fn new() -> Self {
597 Self {
598 statements: Vec::new(),
599 }
600 }
601 pub fn push(mut self, stmt: impl ToString) -> Self {
602 self.statements.push(stmt.to_string());
603 self
604 }
605 pub fn to_surrealql(&self) -> String {
606 self.statements.join(";\n")
607 }
608 pub fn len(&self) -> usize {
610 self.statements.len()
611 }
612 pub fn is_empty(&self) -> bool {
613 self.statements.is_empty()
614 }
615}
616
617impl std::fmt::Display for Batch {
618 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
619 write!(f, "{}", self.to_surrealql())
620 }
621}
622
623fn record_id(thing: &Thing<impl SurrealRecord>, buf: &mut String) {
629 buf.push_str(thing.table());
630 buf.push(':');
631 thing.key.render_id(buf);
632}
633
634fn record_id_string(thing: &Thing<impl SurrealRecord>) -> String {
636 let mut s = String::new();
637 record_id(thing, &mut s);
638 s
639}
640
641pub struct Relate<E: SurrealEdge> {
642 _marker: std::marker::PhantomData<E>,
643}
644
645impl<E: SurrealEdge> Relate<E> {
646 pub fn new() -> Self {
647 Self {
648 _marker: std::marker::PhantomData,
649 }
650 }
651
652 pub fn to_surrealql(
653 from: &Thing<impl SurrealRecord>,
654 to: &Thing<impl SurrealRecord>,
655 ) -> String {
656 let mut q = String::from("RELATE ");
657 record_id(from, &mut q);
658 q.push_str(" -> ");
659 q.push_str(E::edge_name());
660 q.push_str(" -> ");
661 record_id(to, &mut q);
662 q
663 }
664}
665
666impl<E: SurrealEdge> Default for Relate<E> {
667 fn default() -> Self {
668 Self::new()
669 }
670}
671
672pub struct RelateEdge<E: SurrealEdge> {
682 _marker: std::marker::PhantomData<E>,
683 from_label: String,
684 to_label: String,
685 content_json: Option<serde_json::Value>,
686}
687
688impl<E: SurrealEdge> RelateEdge<E> {
689 pub fn from(from: &Thing<impl SurrealRecord>) -> Self {
690 Self {
691 _marker: std::marker::PhantomData,
692 from_label: record_id_string(from),
693 to_label: String::new(),
694 content_json: None,
695 }
696 }
697
698 pub fn to(mut self, to: &Thing<impl SurrealRecord>) -> Self {
699 self.to_label = record_id_string(to);
700 self
701 }
702
703 pub fn content(mut self, edge: &impl serde::Serialize) -> Self {
705 self.content_json = serde_json::to_value(edge).ok();
706 self
707 }
708
709 pub fn build(&self) -> String {
710 let mut q = format!(
711 "RELATE {} -> {} -> {}",
712 self.from_label,
713 E::edge_name(),
714 self.to_label
715 );
716 if let Some(ref c) = self.content_json {
717 q.push_str(&format!(
718 " CONTENT {}",
719 serde_json::to_string(c).unwrap_or_default()
720 ));
721 }
722 q
723 }
724}