1use reblessive::Stk;
2
3use crate::{
4 sql::{
5 graph::GraphSubjects,
6 part::{DestructurePart, Recurse, RecurseInstruction},
7 Dir, Edges, Field, Fields, Graph, Ident, Idiom, Param, Part, Table, Value,
8 },
9 syn::{
10 error::bail,
11 token::{t, Glued, Span, TokenKind},
12 },
13};
14
15use super::{
16 mac::{expected, parse_option, unexpected},
17 ParseResult, Parser,
18};
19
20impl Parser<'_> {
21 pub(super) fn peek_continues_idiom(&mut self) -> bool {
22 let peek = self.peek().kind;
23 if matches!(peek, t!("->") | t!("[") | t!(".") | t!("...") | t!("?")) {
24 return true;
25 }
26 peek == t!("<") && matches!(self.peek1().kind, t!("-") | t!("->"))
27 }
28
29 pub(super) async fn parse_fields(&mut self, ctx: &mut Stk) -> ParseResult<Fields> {
34 if self.eat(t!("VALUE")) {
35 let expr = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
36 let alias = if self.eat(t!("AS")) {
37 Some(self.parse_plain_idiom(ctx).await?)
38 } else {
39 None
40 };
41 Ok(Fields(
42 vec![Field::Single {
43 expr,
44 alias,
45 }],
46 true,
47 ))
48 } else {
49 let mut fields = Vec::new();
50 loop {
51 let field = if self.eat(t!("*")) {
52 Field::All
53 } else {
54 let expr = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
55 let alias = if self.eat(t!("AS")) {
56 Some(self.parse_plain_idiom(ctx).await?)
57 } else {
58 None
59 };
60 Field::Single {
61 expr,
62 alias,
63 }
64 };
65 fields.push(field);
66 if !self.eat(t!(",")) {
67 break;
68 }
69 }
70 Ok(Fields(fields, false))
71 }
72 }
73
74 pub(super) async fn parse_idiom_list(&mut self, ctx: &mut Stk) -> ParseResult<Vec<Idiom>> {
76 let mut res = vec![self.parse_plain_idiom(ctx).await?];
77 while self.eat(t!(",")) {
78 res.push(self.parse_plain_idiom(ctx).await?);
79 }
80 Ok(res)
81 }
82
83 pub(super) async fn parse_remaining_idiom(
88 &mut self,
89 stk: &mut Stk,
90 start: Vec<Part>,
91 ) -> ParseResult<Idiom> {
92 let mut res = start;
93 loop {
94 match self.peek_kind() {
95 t!("?") => {
96 self.pop_peek();
97 res.push(Part::Optional);
98 }
99 t!("...") => {
100 self.pop_peek();
101 res.push(Part::Flatten);
102 }
103 t!(".") => {
104 self.pop_peek();
105 res.push(self.parse_dot_part(stk).await?)
106 }
107 t!("[") => {
108 let span = self.pop_peek().span;
109 let part = self.parse_bracket_part(stk, span).await?;
110 res.push(part)
111 }
112 t!("->") => {
113 self.pop_peek();
114 let graph = stk.run(|stk| self.parse_graph(stk, Dir::Out)).await?;
115 res.push(Part::Graph(graph))
116 }
117 t!("<") => {
118 let peek = self.peek_whitespace1();
119 if peek.kind == t!("-") {
120 self.pop_peek();
121 self.pop_peek();
122 let graph = stk.run(|stk| self.parse_graph(stk, Dir::In)).await?;
123 res.push(Part::Graph(graph))
124 } else if peek.kind == t!("->") {
125 self.pop_peek();
126 self.pop_peek();
127 let graph = stk.run(|stk| self.parse_graph(stk, Dir::Both)).await?;
128 res.push(Part::Graph(graph))
129 } else {
130 break;
131 }
132 }
133 t!("..") => {
134 bail!("Unexpected token `{}` expected and idiom",t!(".."),
135 @self.last_span() => "Did you maybe intent to use the flatten operator `...`");
136 }
137 _ => break,
138 }
139 }
140 Ok(Idiom(res))
141 }
142
143 pub(super) async fn parse_remaining_value_idiom(
150 &mut self,
151 ctx: &mut Stk,
152 start: Vec<Part>,
153 ) -> ParseResult<Value> {
154 let mut res = start;
155 loop {
156 match self.peek_kind() {
157 t!("?") => {
158 self.pop_peek();
159 res.push(Part::Optional);
160 }
161 t!("...") => {
162 self.pop_peek();
163 res.push(Part::Flatten);
164 }
165 t!(".") => {
166 self.pop_peek();
167 res.push(self.parse_dot_part(ctx).await?)
168 }
169 t!("[") => {
170 let span = self.pop_peek().span;
171 let part = self.parse_bracket_part(ctx, span).await?;
172 res.push(part)
173 }
174 t!("->") => {
175 self.pop_peek();
176 if let Some(x) = self.parse_graph_idiom(ctx, &mut res, Dir::Out).await? {
177 return Ok(x);
178 }
179 }
180 t!("<") => {
181 let peek = self.peek_whitespace1();
182 if peek.kind == t!("-") {
183 self.pop_peek();
184 self.pop_peek();
185
186 if let Some(x) = self.parse_graph_idiom(ctx, &mut res, Dir::In).await? {
187 return Ok(x);
188 }
189 } else if peek.kind == t!("->") {
190 self.pop_peek();
191 self.pop_peek();
192
193 if let Some(x) = self.parse_graph_idiom(ctx, &mut res, Dir::Both).await? {
194 return Ok(x);
195 }
196 } else {
197 break;
198 }
199 }
200 t!("..") => {
201 bail!("Unexpected token `{}` expected and idiom",t!(".."),
202 @self.last_span() => "Did you maybe intent to use the flatten operator `...`");
203 }
204 _ => break,
205 }
206 }
207 Ok(Value::Idiom(Idiom(res)))
208 }
209
210 async fn parse_graph_idiom(
213 &mut self,
214 ctx: &mut Stk,
215 res: &mut Vec<Part>,
216 dir: Dir,
217 ) -> ParseResult<Option<Value>> {
218 let graph = ctx.run(|ctx| self.parse_graph(ctx, dir)).await?;
219 if res.len() == 1
222 && graph.alias.is_none()
223 && graph.cond.is_none()
224 && graph.group.is_none()
225 && graph.limit.is_none()
226 && graph.order.is_none()
227 && graph.split.is_none()
228 && graph.start.is_none()
229 && graph.expr.is_none()
230 {
231 match std::mem::replace(&mut res[0], Part::All) {
232 Part::Value(Value::Thing(t)) | Part::Start(Value::Thing(t)) => {
233 let edge = Edges {
234 dir: graph.dir,
235 from: t,
236 what: graph.what,
237 };
238 let value = Value::Edges(Box::new(edge));
239
240 if !self.peek_continues_idiom() {
241 return Ok(Some(value));
242 }
243 res[0] = Part::Start(value);
244 return Ok(None);
245 }
246 x => {
247 res[0] = x;
248 }
249 }
250 }
251 res.push(Part::Graph(graph));
252 Ok(None)
253 }
254
255 pub async fn parse_plain_idiom(&mut self, ctx: &mut Stk) -> ParseResult<Idiom> {
258 let start = match self.peek_kind() {
259 t!("->") => {
260 self.pop_peek();
261 let graph = ctx.run(|ctx| self.parse_graph(ctx, Dir::Out)).await?;
262 Part::Graph(graph)
263 }
264 t!("<") => {
265 let t = self.pop_peek();
266 let graph = if self.eat_whitespace(t!("-")) {
267 ctx.run(|ctx| self.parse_graph(ctx, Dir::In)).await?
268 } else if self.eat_whitespace(t!("->")) {
269 ctx.run(|ctx| self.parse_graph(ctx, Dir::Both)).await?
270 } else {
271 unexpected!(self, t, "either `<-` `<->` or `->`")
272 };
273 Part::Graph(graph)
274 }
275 _ => Part::Field(self.next_token_value()?),
276 };
277 let start = vec![start];
278 self.parse_remaining_idiom(ctx, start).await
279 }
280
281 pub(super) async fn parse_dot_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
283 let res = match self.peek_kind() {
284 t!("*") => {
285 self.pop_peek();
286 Part::All
287 }
288 t!("@") => {
289 self.pop_peek();
290 Part::RepeatRecurse
291 }
292 t!("{") => {
293 self.pop_peek();
294 ctx.run(|ctx| self.parse_curly_part(ctx)).await?
295 }
296 _ => {
297 let ident: Ident = self.next_token_value()?;
298 if self.eat(t!("(")) {
299 self.parse_function_part(ctx, ident).await?
300 } else {
301 Part::Field(ident)
302 }
303 }
304 };
305 Ok(res)
306 }
307 pub(super) async fn parse_function_part(
308 &mut self,
309 ctx: &mut Stk,
310 name: Ident,
311 ) -> ParseResult<Part> {
312 let args = self.parse_function_args(ctx).await?;
313 Ok(Part::Method(name.0, args))
314 }
315 pub(super) async fn parse_curly_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
317 match self.peek_kind() {
318 t!("*") | t!("..") | TokenKind::Digits => self.parse_recurse_part(ctx).await,
319 _ => self.parse_destructure_part(ctx).await,
320 }
321 }
322 pub(super) async fn parse_destructure_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
324 let start = self.last_span();
325 let mut destructured: Vec<DestructurePart> = Vec::new();
326 loop {
327 if self.eat(t!("}")) {
328 break;
330 }
331
332 let field: Ident = self.next_token_value()?;
333 let part = match self.peek_kind() {
334 t!(":") => {
335 self.pop_peek();
336 let idiom = match self.parse_value_inherit(ctx).await? {
337 Value::Idiom(x) => x,
338 v => Idiom(vec![Part::Start(v)]),
339 };
340 DestructurePart::Aliased(field, idiom)
341 }
342 t!(".") => {
343 self.pop_peek();
344 let found = self.peek_kind();
345 match self.parse_dot_part(ctx).await? {
346 Part::All => DestructurePart::All(field),
347 Part::Destructure(v) => DestructurePart::Destructure(field, v),
348 _ => {
349 bail!("Unexpected token `{}` expected a `*` or a destructuring", found, @self.last_span());
350 }
351 }
352 }
353 _ => DestructurePart::Field(field),
354 };
355
356 destructured.push(part);
357
358 if !self.eat(t!(",")) {
359 self.expect_closing_delimiter(t!("}"), start)?;
361 break;
362 }
363 }
364
365 Ok(Part::Destructure(destructured))
366 }
367 pub(super) fn parse_recurse_inner(&mut self) -> ParseResult<Recurse> {
369 let min = if matches!(self.peek().kind, TokenKind::Digits) {
370 Some(self.next_token_value::<u32>()?)
371 } else {
372 None
373 };
374
375 match (self.eat_whitespace(t!("..")), min) {
376 (true, _) => (),
377 (false, Some(v)) => {
378 return Ok(Recurse::Fixed(v));
379 }
380 _ => {
381 let found = self.next().kind;
382 bail!("Unexpected token `{}` expected an integer or ..", found, @self.last_span());
383 }
384 }
385
386 let max = if matches!(self.peek_whitespace().kind, TokenKind::Digits) {
388 Some(self.next_token_value::<u32>()?)
389 } else {
390 None
391 };
392
393 Ok(Recurse::Range(min, max))
394 }
395 pub(super) async fn parse_recurse_instruction(
397 &mut self,
398 ctx: &mut Stk,
399 ) -> ParseResult<Option<RecurseInstruction>> {
400 let instruction = parse_option!(
401 self,
402 "instruction",
403 "path" => {
404 let mut inclusive = false;
405 loop {
406 parse_option!(
407 self,
408 "option",
409 "inclusive" => inclusive = true,
410 _ => break
411 );
412 };
413
414 Some(RecurseInstruction::Path { inclusive })
415 },
416 "collect" => {
417 let mut inclusive = false;
418 loop {
419 parse_option!(
420 self,
421 "option",
422 "inclusive" => inclusive = true,
423 _ => break
424 );
425 };
426
427 Some(RecurseInstruction::Collect { inclusive })
428 },
429 "shortest" => {
430 expected!(self, t!("="));
431 let token = self.peek();
432 let expects = match token.kind {
433 TokenKind::Parameter => {
434 Value::from(self.next_token_value::<Param>()?)
435 },
436 x if Parser::kind_is_identifier(x) => {
437 Value::from(self.parse_thing(ctx).await?)
438 }
439 _ => {
440 unexpected!(self, token, "a param or thing");
441 }
442 };
443 let mut inclusive = false;
444 loop {
445 parse_option!(
446 self,
447 "option",
448 "inclusive" => inclusive = true,
449 _ => break
450 );
451 };
452
453 Some(RecurseInstruction::Shortest { expects, inclusive })
454 },
455 _ => None
456 );
457
458 Ok(instruction)
459 }
460 pub(super) async fn parse_recurse_part(&mut self, ctx: &mut Stk) -> ParseResult<Part> {
462 let start = self.last_span();
463 let recurse = self.parse_recurse_inner()?;
464 let instruction = self.parse_recurse_instruction(ctx).await?;
465 self.expect_closing_delimiter(t!("}"), start)?;
466
467 let nest = if self.eat(t!("(")) {
468 let start = self.last_span();
469 let idiom = self.parse_remaining_idiom(ctx, vec![]).await?;
470 self.expect_closing_delimiter(t!(")"), start)?;
471 Some(idiom)
472 } else {
473 None
474 };
475
476 Ok(Part::Recurse(recurse, nest, instruction))
477 }
478 pub(super) async fn parse_bracket_part(
480 &mut self,
481 ctx: &mut Stk,
482 start: Span,
483 ) -> ParseResult<Part> {
484 let peek = self.peek();
485 let res = match peek.kind {
486 t!("*") => {
487 self.pop_peek();
488 Part::All
489 }
490 t!("$") => {
491 self.pop_peek();
492 Part::Last
493 }
494 t!("?") | t!("WHERE") => {
495 self.pop_peek();
496 let value = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
497 Part::Where(value)
498 }
499 _ => {
500 let value = ctx.run(|ctx| self.parse_value_inherit(ctx)).await?;
501 if let Value::Number(x) = value {
502 Part::Index(x)
503 } else {
504 Part::Value(value)
505 }
506 }
507 };
508 self.expect_closing_delimiter(t!("]"), start)?;
509 Ok(res)
510 }
511
512 pub(super) async fn parse_basic_idiom(&mut self, ctx: &mut Stk) -> ParseResult<Idiom> {
517 let start = self.next_token_value::<Ident>()?;
518 let mut parts = vec![Part::Field(start)];
519 loop {
520 let token = self.peek();
521 let part = match token.kind {
522 t!(".") => {
523 self.pop_peek();
524 self.parse_dot_part(ctx).await?
525 }
526 t!("[") => {
527 self.pop_peek();
528 let peek = self.peek();
529 let res = match peek.kind {
530 t!("*") => {
531 self.pop_peek();
532 Part::All
533 }
534 t!("$") => {
535 self.pop_peek();
536 Part::Last
537 }
538 TokenKind::Digits | t!("+") | TokenKind::Glued(Glued::Number) => {
539 let number = self.next_token_value()?;
540 Part::Index(number)
541 }
542 t!("-") => {
543 let peek_digit = self.peek_whitespace1();
544 if let TokenKind::Digits = peek_digit.kind {
545 let span = self.recent_span().covers(peek_digit.span);
546 bail!("Unexpected token `-` expected $, *, or a number", @span => "an index can't be negative");
547 }
548 unexpected!(self, peek, "$, * or a number");
549 }
550 _ => unexpected!(self, peek, "$, * or a number"),
551 };
552 self.expect_closing_delimiter(t!("]"), token.span)?;
553 res
554 }
555 _ => break,
556 };
557 parts.push(part);
558 }
559 Ok(Idiom(parts))
560 }
561
562 pub(super) async fn parse_local_idiom(&mut self, ctx: &mut Stk) -> ParseResult<Idiom> {
568 let start = self.next_token_value()?;
569 let mut parts = vec![Part::Field(start)];
570 loop {
571 let token = self.peek();
572 let part = match token.kind {
573 t!(".") => {
574 self.pop_peek();
575 self.parse_dot_part(ctx).await?
576 }
577 t!("[") => {
578 self.pop_peek();
579 let token = self.peek();
580 let res = match token.kind {
581 t!("*") => {
582 self.pop_peek();
583 Part::All
584 }
585 TokenKind::Digits | t!("+") | TokenKind::Glued(Glued::Number) => {
586 let number = self.next_token_value()?;
587 Part::Index(number)
588 }
589 t!("-") => {
590 let peek_digit = self.peek_whitespace1();
591 if let TokenKind::Digits = peek_digit.kind {
592 let span = self.recent_span().covers(peek_digit.span);
593 bail!("Unexpected token `-` expected $, *, or a number", @span => "an index can't be negative");
594 }
595 unexpected!(self, token, "$, * or a number");
596 }
597 _ => unexpected!(self, token, "$, * or a number"),
598 };
599 self.expect_closing_delimiter(t!("]"), token.span)?;
600 res
601 }
602 _ => break,
603 };
604
605 parts.push(part);
606 }
607
608 if self.eat(t!("...")) {
609 let token = self.peek();
610 if let t!(".") | t!("[") = token.kind {
611 bail!("Unexpected token `...` expected a local idiom to end.",
612 @token.span => "Flattening can only be done at the end of a local idiom")
613 }
614 parts.push(Part::Flatten);
615 }
616
617 Ok(Idiom(parts))
618 }
619
620 pub(super) async fn parse_what_list(&mut self, ctx: &mut Stk) -> ParseResult<Vec<Value>> {
625 let mut res = vec![self.parse_what_value(ctx).await?];
626 while self.eat(t!(",")) {
627 res.push(self.parse_what_value(ctx).await?)
628 }
629 Ok(res)
630 }
631
632 pub(super) async fn parse_what_value(&mut self, ctx: &mut Stk) -> ParseResult<Value> {
637 let start = self.parse_what_primary(ctx).await?;
638 if start.can_start_idiom() && self.peek_continues_idiom() {
639 let start = match start {
640 Value::Table(Table(x)) => vec![Part::Field(Ident(x))],
641 Value::Idiom(Idiom(x)) => x,
642 x => vec![Part::Start(x)],
643 };
644
645 let idiom = self.parse_remaining_value_idiom(ctx, start).await?;
646 Ok(idiom)
647 } else {
648 Ok(start)
649 }
650 }
651
652 pub(super) async fn parse_graph(&mut self, ctx: &mut Stk, dir: Dir) -> ParseResult<Graph> {
658 let token = self.peek();
659 match token.kind {
660 t!("?") => {
661 self.pop_peek();
662 Ok(Graph {
663 dir,
664 ..Default::default()
665 })
666 }
667 t!("(") => {
668 let span = self.pop_peek().span;
669 let expr = if self.eat(t!("SELECT")) {
670 let before = self.peek().span;
671 let expr = self.parse_fields(ctx).await?;
672 let fields_span = before.covers(self.last_span());
673 expected!(self, t!("FROM"));
674 Some((expr, fields_span))
675 } else {
676 None
677 };
678
679 let token = self.peek();
680 let what = match token.kind {
681 t!("?") => {
682 self.pop_peek();
683 GraphSubjects::default()
684 }
685 x if Self::kind_is_identifier(x) => {
686 let subject = self.parse_graph_subject(ctx).await?;
687 let mut subjects = GraphSubjects(vec![subject]);
688 while self.eat(t!(",")) {
689 subjects.0.push(self.parse_graph_subject(ctx).await?);
690 }
691 subjects
692 }
693 _ => unexpected!(self, token, "`?`, an identifier or a range"),
694 };
695
696 let cond = self.try_parse_condition(ctx).await?;
697 let (split, group, order) = if let Some((ref expr, fields_span)) = expr {
698 let split = self.try_parse_split(ctx, expr, fields_span).await?;
699 let group = self.try_parse_group(ctx, expr, fields_span).await?;
700 let order = self.try_parse_orders(ctx, expr, fields_span).await?;
701 (split, group, order)
702 } else {
703 (None, None, None)
704 };
705
706 let (limit, start) = if let t!("START") = self.peek_kind() {
707 let start = self.try_parse_start(ctx).await?;
708 let limit = self.try_parse_limit(ctx).await?;
709 (limit, start)
710 } else {
711 let limit = self.try_parse_limit(ctx).await?;
712 let start = self.try_parse_start(ctx).await?;
713 (limit, start)
714 };
715
716 let alias = if self.eat(t!("AS")) {
717 Some(self.parse_plain_idiom(ctx).await?)
718 } else {
719 None
720 };
721
722 self.expect_closing_delimiter(t!(")"), span)?;
723
724 Ok(Graph {
725 dir,
726 what,
727 cond,
728 alias,
729 expr: expr.map(|(x, _)| x),
730 split,
731 group,
732 order,
733 limit,
734 start,
735 ..Default::default()
736 })
737 }
738 x if Self::kind_is_identifier(x) => {
739 let subject = self.parse_graph_subject(ctx).await?;
742 Ok(Graph {
743 dir,
744 what: GraphSubjects(vec![subject]),
745 ..Default::default()
746 })
747 }
748 _ => unexpected!(self, token, "`?`, `(` or an identifier"),
749 }
750 }
751}
752
753#[cfg(test)]
754mod tests {
755 use crate::sql::{Expression, Id, Number, Object, Operator, Param, Strand, Thing};
756 use crate::syn::Parse;
757
758 use super::*;
759
760 #[test]
761 fn graph_in() {
762 let sql = "<-likes";
763 let out = Value::parse(sql);
764 assert_eq!("<-likes", format!("{}", out));
765 }
766
767 #[test]
768 fn graph_out() {
769 let sql = "->likes";
770 let out = Value::parse(sql);
771 assert_eq!("->likes", format!("{}", out));
772 }
773
774 #[test]
775 fn graph_both() {
776 let sql = "<->likes";
777 let out = Value::parse(sql);
778 assert_eq!("<->likes", format!("{}", out));
779 }
780
781 #[test]
782 fn graph_multiple() {
783 let sql = "->(likes, follows)";
784 let out = Value::parse(sql);
785 assert_eq!("->(likes, follows)", format!("{}", out));
786 }
787
788 #[test]
789 fn graph_aliases() {
790 let sql = "->(likes, follows AS connections)";
791 let out = Value::parse(sql);
792 assert_eq!("->(likes, follows AS connections)", format!("{}", out));
793 }
794
795 #[test]
796 fn graph_conditions() {
797 let sql = "->(likes, follows WHERE influencer = true)";
798 let out = Value::parse(sql);
799 assert_eq!("->(likes, follows WHERE influencer = true)", format!("{}", out));
800 }
801
802 #[test]
803 fn graph_conditions_aliases() {
804 let sql = "->(likes, follows WHERE influencer = true AS connections)";
805 let out = Value::parse(sql);
806 assert_eq!("->(likes, follows WHERE influencer = true AS connections)", format!("{}", out));
807 }
808
809 #[test]
810 fn idiom_normal() {
811 let sql = "test";
812 let out = Value::parse(sql);
813 assert_eq!("test", format!("{}", out));
814 assert_eq!(out, Value::from(Idiom(vec![Part::from("test")])));
815 }
816
817 #[test]
818 fn idiom_quoted_backtick() {
819 let sql = "`test`";
820 let out = Value::parse(sql);
821 assert_eq!("test", format!("{}", out));
822 assert_eq!(out, Value::from(Idiom(vec![Part::from("test")])));
823 }
824
825 #[test]
826 fn idiom_quoted_brackets() {
827 let sql = "⟨test⟩";
828 let out = Value::parse(sql);
829 assert_eq!("test", format!("{}", out));
830 assert_eq!(out, Value::from(Idiom(vec![Part::from("test")])));
831 }
832
833 #[test]
834 fn idiom_nested() {
835 let sql = "test.temp";
836 let out = Value::parse(sql);
837 assert_eq!("test.temp", format!("{}", out));
838 assert_eq!(out, Value::from(Idiom(vec![Part::from("test"), Part::from("temp")])));
839 }
840
841 #[test]
842 fn idiom_nested_quoted() {
843 let sql = "test.`some key`";
844 let out = Value::parse(sql);
845 assert_eq!("test.`some key`", format!("{}", out));
846 assert_eq!(out, Value::from(Idiom(vec![Part::from("test"), Part::from("some key")])));
847 }
848
849 #[test]
850 fn idiom_nested_array_all() {
851 let sql = "test.temp[*]";
852 let out = Value::parse(sql);
853 assert_eq!("test.temp[*]", format!("{}", out));
854 assert_eq!(
855 out,
856 Value::from(Idiom(vec![Part::from("test"), Part::from("temp"), Part::All]))
857 );
858 }
859
860 #[test]
861 fn idiom_nested_array_last() {
862 let sql = "test.temp[$]";
863 let out = Value::parse(sql);
864 assert_eq!("test.temp[$]", format!("{}", out));
865 assert_eq!(
866 out,
867 Value::from(Idiom(vec![Part::from("test"), Part::from("temp"), Part::Last]))
868 );
869 }
870
871 #[test]
872 fn idiom_nested_array_value() {
873 let sql = "test.temp[*].text";
874 let out = Value::parse(sql);
875 assert_eq!("test.temp[*].text", format!("{}", out));
876 assert_eq!(
877 out,
878 Value::from(Idiom(vec![
879 Part::from("test"),
880 Part::from("temp"),
881 Part::All,
882 Part::from("text")
883 ]))
884 );
885 }
886
887 #[test]
888 fn idiom_nested_array_question() {
889 let sql = "test.temp[? test = true].text";
890 let out = Value::parse(sql);
891 assert_eq!("test.temp[WHERE test = true].text", format!("{}", out));
892 assert_eq!(
893 out,
894 Value::from(Idiom(vec![
895 Part::from("test"),
896 Part::from("temp"),
897 Part::Where(Value::Expression(Box::new(Expression::Binary {
898 l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
899 o: Operator::Equal,
900 r: Value::Bool(true)
901 }))),
902 Part::from("text")
903 ]))
904 );
905 }
906
907 #[test]
908 fn idiom_nested_array_condition() {
909 let sql = "test.temp[WHERE test = true].text";
910 let out = Value::parse(sql);
911 assert_eq!("test.temp[WHERE test = true].text", format!("{}", out));
912 assert_eq!(
913 out,
914 Value::from(Idiom(vec![
915 Part::from("test"),
916 Part::from("temp"),
917 Part::Where(Value::Expression(Box::new(Expression::Binary {
918 l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
919 o: Operator::Equal,
920 r: Value::Bool(true)
921 }))),
922 Part::from("text")
923 ]))
924 );
925 }
926
927 #[test]
928 fn idiom_start_param_local_field() {
929 let sql = "$test.temporary[0].embedded…";
930 let out = Value::parse(sql);
931 assert_eq!("$test.temporary[0].embedded…", format!("{}", out));
932 assert_eq!(
933 out,
934 Value::from(Idiom(vec![
935 Part::Start(Param::from("test").into()),
936 Part::from("temporary"),
937 Part::Index(Number::Int(0)),
938 Part::from("embedded"),
939 Part::Flatten,
940 ]))
941 );
942 }
943
944 #[test]
945 fn idiom_start_thing_remote_traversal() {
946 let sql = "person:test.friend->like->person";
947 let out = Value::parse(sql);
948 assert_eq!("person:test.friend->like->person", format!("{}", out));
949 assert_eq!(
950 out,
951 Value::from(Idiom(vec![
952 Part::Start(Thing::from(("person", "test")).into()),
953 Part::from("friend"),
954 Part::Graph(Graph {
955 dir: Dir::Out,
956 what: Table::from("like").into(),
957 ..Default::default()
958 }),
959 Part::Graph(Graph {
960 dir: Dir::Out,
961 what: Table::from("person").into(),
962 ..Default::default()
963 }),
964 ]))
965 );
966 }
967
968 #[test]
969 fn part_all() {
970 let sql = "{}[*]";
971 let out = Value::parse(sql);
972 assert_eq!("{ }[*]", format!("{}", out));
973 assert_eq!(
974 out,
975 Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::All]))
976 );
977 }
978
979 #[test]
980 fn part_last() {
981 let sql = "{}[$]";
982 let out = Value::parse(sql);
983 assert_eq!("{ }[$]", format!("{}", out));
984 assert_eq!(
985 out,
986 Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::Last]))
987 );
988 }
989
990 #[test]
991 fn part_param() {
992 let sql = "{}[$param]";
993 let out = Value::parse(sql);
994 assert_eq!("{ }[$param]", format!("{}", out));
995 assert_eq!(
996 out,
997 Value::from(Idiom(vec![
998 Part::Start(Value::from(Object::default())),
999 Part::Value(Value::Param(Param::from("param")))
1000 ]))
1001 );
1002 }
1003
1004 #[test]
1005 fn part_flatten() {
1006 let sql = "{}...";
1007 let out = Value::parse(sql);
1008 assert_eq!("{ }…", format!("{}", out));
1009 assert_eq!(
1010 out,
1011 Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::Flatten]))
1012 );
1013 }
1014
1015 #[test]
1016 fn part_flatten_ellipsis() {
1017 let sql = "{}…";
1018 let out = Value::parse(sql);
1019 assert_eq!("{ }…", format!("{}", out));
1020 assert_eq!(
1021 out,
1022 Value::from(Idiom(vec![Part::Start(Value::from(Object::default())), Part::Flatten]))
1023 );
1024 }
1025
1026 #[test]
1027 fn part_number() {
1028 let sql = "{}[0]";
1029 let out = Value::parse(sql);
1030 assert_eq!("{ }[0]", format!("{}", out));
1031 assert_eq!(
1032 out,
1033 Value::from(Idiom(vec![
1034 Part::Start(Value::from(Object::default())),
1035 Part::Index(Number::from(0))
1036 ]))
1037 );
1038 }
1039
1040 #[test]
1041 fn part_expression_question() {
1042 let sql = "{}[?test = true]";
1043 let out = Value::parse(sql);
1044 assert_eq!("{ }[WHERE test = true]", format!("{}", out));
1045 assert_eq!(
1046 out,
1047 Value::from(Idiom(vec![
1048 Part::Start(Value::from(Object::default())),
1049 Part::Where(Value::Expression(Box::new(Expression::Binary {
1050 l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
1051 o: Operator::Equal,
1052 r: Value::Bool(true)
1053 }))),
1054 ]))
1055 );
1056 }
1057
1058 #[test]
1059 fn part_expression_condition() {
1060 let sql = "{}[WHERE test = true]";
1061 let out = Value::parse(sql);
1062 assert_eq!("{ }[WHERE test = true]", format!("{}", out));
1063 assert_eq!(
1064 out,
1065 Value::from(Idiom(vec![
1066 Part::Start(Value::from(Object::default())),
1067 Part::Where(Value::Expression(Box::new(Expression::Binary {
1068 l: Value::Idiom(Idiom(vec![Part::Field(Ident("test".to_string()))])),
1069 o: Operator::Equal,
1070 r: Value::Bool(true)
1071 }))),
1072 ]))
1073 );
1074 }
1075
1076 #[test]
1077 fn idiom_thing_number() {
1078 let sql = "test:1.foo";
1079 let out = Value::parse(sql);
1080 assert_eq!(
1081 out,
1082 Value::from(Idiom(vec![
1083 Part::Start(Value::Thing(Thing {
1084 tb: "test".to_owned(),
1085 id: Id::from(1),
1086 })),
1087 Part::from("foo"),
1088 ]))
1089 );
1090 }
1091
1092 #[test]
1093 fn idiom_thing_index() {
1094 let sql = "test:1['foo']";
1095 let out = Value::parse(sql);
1096 assert_eq!(
1097 out,
1098 Value::from(Idiom(vec![
1099 Part::Start(Value::Thing(Thing {
1100 tb: "test".to_owned(),
1101 id: Id::from(1),
1102 })),
1103 Part::Value(Value::Strand(Strand("foo".to_owned()))),
1104 ]))
1105 );
1106 }
1107
1108 #[test]
1109 fn idiom_thing_all() {
1110 let sql = "test:1.*";
1111 let out = Value::parse(sql);
1112 assert_eq!(
1113 out,
1114 Value::from(Idiom(vec![
1115 Part::Start(Value::Thing(Thing {
1116 tb: "test".to_owned(),
1117 id: Id::from(1),
1118 })),
1119 Part::All
1120 ]))
1121 );
1122 }
1123}