1use crate::{
2 cnf::IDIOM_RECURSION_LIMIT,
3 ctx::Context,
4 dbs::Options,
5 doc::CursorDoc,
6 err::Error,
7 exe::try_join_all_buffered,
8 sql::{fmt::Fmt, strand::no_nul_bytes, Graph, Ident, Idiom, Number, Value},
9};
10use reblessive::tree::Stk;
11use revision::revisioned;
12use serde::{Deserialize, Serialize};
13use std::fmt;
14use std::fmt::Write;
15use std::str;
16
17use super::{
18 fmt::{is_pretty, pretty_indent},
19 value::idiom_recursion::{clean_iteration, compute_idiom_recursion, is_final, Recursion},
20};
21
22#[revisioned(revision = 4)]
23#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[non_exhaustive]
26#[allow(clippy::large_enum_variant)]
27pub enum Part {
28 All,
29 Flatten,
30 Last,
31 First,
32 Field(Ident),
33 Index(Number),
34 Where(Value),
35 Graph(Graph),
36 Value(Value),
37 Start(Value),
38 Method(#[serde(with = "no_nul_bytes")] String, Vec<Value>),
39 #[revision(start = 2)]
40 Destructure(Vec<DestructurePart>),
41 Optional,
42 #[revision(
43 start = 3,
44 end = 4,
45 convert_fn = "convert_recurse_add_instruction",
46 fields_name = "OldRecurseFields"
47 )]
48 Recurse(Recurse, Option<Idiom>),
49 #[revision(start = 4)]
50 Recurse(Recurse, Option<Idiom>, Option<RecurseInstruction>),
51 #[revision(start = 3)]
52 Doc,
53 #[revision(start = 3)]
54 RepeatRecurse,
55}
56
57impl Part {
58 fn convert_recurse_add_instruction(
59 fields: OldRecurseFields,
60 _revision: u16,
61 ) -> Result<Self, revision::Error> {
62 Ok(Part::Recurse(fields.0, fields.1, None))
63 }
64}
65
66impl From<i32> for Part {
67 fn from(v: i32) -> Self {
68 Self::Index(v.into())
69 }
70}
71
72impl From<isize> for Part {
73 fn from(v: isize) -> Self {
74 Self::Index(v.into())
75 }
76}
77
78impl From<usize> for Part {
79 fn from(v: usize) -> Self {
80 Self::Index(v.into())
81 }
82}
83
84impl From<String> for Part {
85 fn from(v: String) -> Self {
86 Self::Field(v.into())
87 }
88}
89
90impl From<Number> for Part {
91 fn from(v: Number) -> Self {
92 Self::Index(v)
93 }
94}
95
96impl From<Ident> for Part {
97 fn from(v: Ident) -> Self {
98 Self::Field(v)
99 }
100}
101
102impl From<Graph> for Part {
103 fn from(v: Graph) -> Self {
104 Self::Graph(v)
105 }
106}
107
108impl From<&str> for Part {
109 fn from(v: &str) -> Self {
110 match v.parse::<isize>() {
111 Ok(v) => Self::from(v),
112 _ => Self::from(v.to_owned()),
113 }
114 }
115}
116
117impl Part {
118 pub(crate) fn writeable(&self) -> bool {
120 match self {
121 Part::Start(v) => v.writeable(),
122 Part::Where(v) => v.writeable(),
123 Part::Value(v) => v.writeable(),
124 Part::Method(_, v) => v.iter().any(Value::writeable),
125 _ => false,
126 }
127 }
128 pub(crate) fn alias(&self) -> Option<&Idiom> {
130 match self {
131 Part::Graph(v) => v.alias.as_ref(),
132 _ => None,
133 }
134 }
135
136 fn recursion_plan(&self) -> Option<RecursionPlan> {
137 match self {
138 Part::RepeatRecurse => Some(RecursionPlan::Repeat),
139 Part::Destructure(parts) => {
140 for (j, p) in parts.iter().enumerate() {
141 let plan = match p {
142 DestructurePart::Aliased(field, v) => v.find_recursion_plan().map(|plan| {
143 (
144 field.to_owned(),
145 plan.0.to_vec(),
146 Box::new(plan.1.to_owned()),
147 plan.2.to_vec(),
148 )
149 }),
150 DestructurePart::Destructure(field, parts) => {
151 Part::Destructure(parts.to_owned()).recursion_plan().map(|plan| {
152 (
153 field.to_owned(),
154 vec![Part::Field(field.to_owned())],
155 Box::new(plan),
156 vec![],
157 )
158 })
159 }
160 _ => None,
161 };
162
163 if let Some((field, before, plan, after)) = plan {
164 let mut parts = parts.clone();
165 parts.remove(j);
166 return Some(RecursionPlan::Destructure {
167 parts,
168 field,
169 before,
170 plan,
171 after,
172 });
173 }
174 }
175
176 None
177 }
178 _ => None,
179 }
180 }
181}
182
183impl fmt::Display for Part {
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 match self {
186 Part::All => f.write_str("[*]"),
187 Part::Last => f.write_str("[$]"),
188 Part::First => f.write_str("[0]"),
189 Part::Start(v) => write!(f, "{v}"),
190 Part::Field(v) => write!(f, ".{v}"),
191 Part::Flatten => f.write_str("…"),
192 Part::Index(v) => write!(f, "[{v}]"),
193 Part::Where(v) => write!(f, "[WHERE {v}]"),
194 Part::Graph(v) => write!(f, "{v}"),
195 Part::Value(v) => write!(f, "[{v}]"),
196 Part::Method(v, a) => write!(f, ".{v}({})", Fmt::comma_separated(a)),
197 Part::Destructure(v) => {
198 f.write_str(".{")?;
199 if !is_pretty() {
200 f.write_char(' ')?;
201 }
202 if !v.is_empty() {
203 let indent = pretty_indent();
204 write!(f, "{}", Fmt::pretty_comma_separated(v))?;
205 drop(indent);
206 }
207 if is_pretty() {
208 f.write_char('}')
209 } else {
210 f.write_str(" }")
211 }
212 }
213 Part::Optional => write!(f, "?"),
214 Part::Recurse(v, nest, instruction) => {
215 write!(f, ".{{{v}")?;
216 if let Some(instruction) = instruction {
217 write!(f, "+{instruction}")?;
218 }
219 write!(f, "}}")?;
220
221 if let Some(nest) = nest {
222 write!(f, "({nest})")?;
223 }
224
225 Ok(())
226 }
227 Part::Doc => write!(f, "@"),
228 Part::RepeatRecurse => write!(f, ".@"),
229 }
230 }
231}
232
233#[derive(Clone, Debug)]
236pub enum RecursionPlan {
237 Repeat,
238 Destructure {
239 parts: Vec<DestructurePart>,
241 field: Ident,
243 before: Vec<Part>,
245 plan: Box<RecursionPlan>,
247 after: Vec<Part>,
249 },
250}
251
252impl<'a> RecursionPlan {
253 pub async fn compute(
254 &self,
255 stk: &mut Stk,
256 ctx: &Context,
257 opt: &Options,
258 doc: Option<&CursorDoc>,
259 rec: Recursion<'a>,
260 ) -> Result<Value, Error> {
261 match rec.current {
262 Value::Array(value) => stk
263 .scope(|scope| {
264 let futs = value.iter().map(|value| {
265 scope.run(|stk| {
266 let rec = rec.with_current(value);
267 self.compute_inner(stk, ctx, opt, doc, rec)
268 })
269 });
270 try_join_all_buffered(futs)
271 })
272 .await
273 .map(Into::into),
274 _ => stk.run(|stk| self.compute_inner(stk, ctx, opt, doc, rec)).await,
275 }
276 }
277
278 pub async fn compute_inner(
279 &self,
280 stk: &mut Stk,
281 ctx: &Context,
282 opt: &Options,
283 doc: Option<&CursorDoc>,
284 rec: Recursion<'a>,
285 ) -> Result<Value, Error> {
286 match self {
287 Self::Repeat => compute_idiom_recursion(stk, ctx, opt, doc, rec).await,
288 Self::Destructure {
289 parts,
290 field,
291 before,
292 plan,
293 after,
294 } => {
295 let v = stk.run(|stk| rec.current.get(stk, ctx, opt, doc, before)).await?;
296 let v = plan.compute(stk, ctx, opt, doc, rec.with_current(&v)).await?;
297 let v = stk.run(|stk| v.get(stk, ctx, opt, doc, after)).await?;
298 let v = clean_iteration(v);
299
300 if rec.iterated < rec.min && is_final(&v) {
301 return Ok(Value::None);
306 }
307
308 let path = &[Part::Destructure(parts.to_owned())];
309 match stk.run(|stk| rec.current.get(stk, ctx, opt, doc, path)).await? {
310 Value::Object(mut obj) => {
311 obj.insert(field.to_raw(), v);
312 Ok(Value::Object(obj))
313 }
314 Value::None => Ok(Value::None),
315 v => Err(Error::Unreachable(format!(
316 "Expected an object or none, found {}.",
317 v.kindof()
318 ))),
319 }
320 }
321 }
322 }
323}
324
325pub trait FindRecursionPlan<'a> {
326 fn find_recursion_plan(&'a self) -> Option<(&'a [Part], RecursionPlan, &'a [Part])>;
327}
328
329impl<'a> FindRecursionPlan<'a> for &'a [Part] {
330 fn find_recursion_plan(&'a self) -> Option<(&'a [Part], RecursionPlan, &'a [Part])> {
331 for (i, p) in self.iter().enumerate() {
332 if let Some(plan) = p.recursion_plan() {
333 return Some((&self[..i], plan, &self[(i + 1)..]));
334 }
335 }
336
337 None
338 }
339}
340
341impl<'a> FindRecursionPlan<'a> for &'a Idiom {
342 fn find_recursion_plan(&'a self) -> Option<(&'a [Part], RecursionPlan, &'a [Part])> {
343 for (i, p) in self.iter().enumerate() {
344 if let Some(plan) = p.recursion_plan() {
345 return Some((&self[..i], plan, &self[(i + 1)..]));
346 }
347 }
348
349 None
350 }
351}
352
353pub trait SplitByRepeatRecurse<'a> {
356 fn split_by_repeat_recurse(&'a self) -> Option<(&'a [Part], &'a [Part])>;
357}
358
359impl<'a> SplitByRepeatRecurse<'a> for &'a [Part] {
360 fn split_by_repeat_recurse(&'a self) -> Option<(&'a [Part], &'a [Part])> {
361 self.iter()
362 .position(|p| matches!(p, Part::RepeatRecurse))
363 .map(|i| (&self[..i], &self[(i + 1)..]))
367 }
368}
369
370impl<'a> SplitByRepeatRecurse<'a> for &'a Idiom {
371 fn split_by_repeat_recurse(&'a self) -> Option<(&'a [Part], &'a [Part])> {
372 self.iter()
373 .position(|p| matches!(p, Part::RepeatRecurse))
374 .map(|i| (&self[..i], &self[(i + 1)..]))
378 }
379}
380
381pub trait Next<'a> {
384 fn next(&'a self) -> &'a [Part];
385}
386
387impl<'a> Next<'a> for &'a [Part] {
388 fn next(&'a self) -> &'a [Part] {
389 match self.len() {
390 0 => &[],
391 _ => &self[1..],
392 }
393 }
394}
395
396pub trait Skip<'a> {
399 fn skip(&'a self, amount: usize) -> &'a [Part];
400}
401
402impl<'a> Skip<'a> for &'a [Part] {
403 fn skip(&'a self, amount: usize) -> &'a [Part] {
404 match self.len() {
405 0 => &[],
406 _ => &self[amount..],
407 }
408 }
409}
410
411pub trait NextMethod<'a> {
414 fn next_method(&'a self) -> &'a [Part];
415}
416
417impl<'a> NextMethod<'a> for &'a [Part] {
418 fn next_method(&'a self) -> &'a [Part] {
419 match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
420 None => &[],
421 Some(i) => &self[i..],
422 }
423 }
424}
425
426impl<'a> NextMethod<'a> for &'a Idiom {
427 fn next_method(&'a self) -> &'a [Part] {
428 match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
429 None => &[],
430 Some(i) => &self[i..],
431 }
432 }
433}
434
435#[revisioned(revision = 1)]
438#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
439#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
440#[non_exhaustive]
441pub enum DestructurePart {
442 All(Ident),
443 Field(Ident),
444 Aliased(Ident, Idiom),
445 Destructure(Ident, Vec<DestructurePart>),
446}
447
448impl DestructurePart {
449 pub fn field(&self) -> &Ident {
450 match self {
451 DestructurePart::All(v) => v,
452 DestructurePart::Field(v) => v,
453 DestructurePart::Aliased(v, _) => v,
454 DestructurePart::Destructure(v, _) => v,
455 }
456 }
457
458 pub fn path(&self) -> Vec<Part> {
459 match self {
460 DestructurePart::All(v) => vec![Part::Field(v.clone()), Part::All],
461 DestructurePart::Field(v) => vec![Part::Field(v.clone())],
462 DestructurePart::Aliased(_, v) => v.0.clone(),
463 DestructurePart::Destructure(f, d) => {
464 vec![Part::Field(f.clone()), Part::Destructure(d.clone())]
465 }
466 }
467 }
468
469 pub fn idiom(&self) -> Idiom {
470 Idiom(self.path())
471 }
472}
473
474impl fmt::Display for DestructurePart {
475 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
476 match self {
477 DestructurePart::All(fd) => write!(f, "{fd}.*"),
478 DestructurePart::Field(fd) => write!(f, "{fd}"),
479 DestructurePart::Aliased(fd, v) => write!(f, "{fd}: {v}"),
480 DestructurePart::Destructure(fd, d) => {
481 write!(f, "{fd}{}", Part::Destructure(d.clone()))
482 }
483 }
484 }
485}
486
487#[revisioned(revision = 1)]
490#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
491#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
492#[non_exhaustive]
493pub enum Recurse {
494 Fixed(u32),
495 Range(Option<u32>, Option<u32>),
496}
497
498impl TryInto<(u32, Option<u32>)> for Recurse {
499 type Error = Error;
500 fn try_into(self) -> Result<(u32, Option<u32>), Error> {
501 let v = match self {
502 Recurse::Fixed(v) => (v, Some(v)),
503 Recurse::Range(min, max) => {
504 let min = min.unwrap_or(1);
505 (min, max)
506 }
507 };
508
509 match v {
510 (min, _) if min < 1 => Err(Error::InvalidBound {
511 found: min.to_string(),
512 expected: "at least 1".into(),
513 }),
514 (_, Some(max)) if max > (*IDIOM_RECURSION_LIMIT as u32) => Err(Error::InvalidBound {
515 found: max.to_string(),
516 expected: format!("{} at most", *IDIOM_RECURSION_LIMIT),
517 }),
518 v => Ok(v),
519 }
520 }
521}
522
523impl fmt::Display for Recurse {
524 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
525 match self {
526 Recurse::Fixed(v) => write!(f, "{v}"),
527 Recurse::Range(beg, end) => match (beg, end) {
528 (None, None) => write!(f, ".."),
529 (Some(beg), None) => write!(f, "{beg}.."),
530 (None, Some(end)) => write!(f, "..{end}"),
531 (Some(beg), Some(end)) => write!(f, "{beg}..{end}"),
532 },
533 }
534 }
535}
536
537#[revisioned(revision = 1)]
540#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
541#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
542#[non_exhaustive]
543pub enum RecurseInstruction {
544 Path {
545 inclusive: bool,
547 },
548 Collect {
549 inclusive: bool,
551 },
552 Shortest {
553 expects: Value,
555 inclusive: bool,
557 },
558}
559
560macro_rules! to_vec_value {
561 (&$v: expr) => {
562 match $v {
563 Value::Array(v) => &v.0,
564 v => &vec![v.to_owned()],
565 }
566 };
567 ($v: expr) => {
568 match $v {
569 Value::Array(v) => v.0,
570 v => vec![v],
571 }
572 };
573}
574
575macro_rules! walk_paths {
576 (
577 $stk: ident,
578 $ctx: ident,
579 $opt: ident,
580 $doc: ident,
581 $rec: ident,
582 $finished: ident,
583 $inclusive: ident,
584 $expects: expr
585 ) => {{
586 let mut open: Vec<Value> = vec![];
589
590 let paths = to_vec_value!(&$rec.current);
592
593 for path in paths.iter() {
595 let path = to_vec_value!(&path);
597
598 let Some(last) = path.last() else {
601 continue;
602 };
603
604 let res = $stk.run(|stk| last.get(stk, $ctx, $opt, $doc, $rec.path)).await?;
606
607 if is_final(&res) || &res == last {
614 if $expects.is_none()
615 && ($rec.iterated > 1 || *$inclusive)
616 && $rec.iterated >= $rec.min
617 {
618 $finished.push(path.to_owned().into());
619 }
620
621 continue;
622 }
623
624 let steps = to_vec_value!(res);
626
627 let reached_max = $rec.max.is_some_and(|max| $rec.iterated >= max);
629
630 for step in steps.iter() {
632 let val = if $rec.iterated == 1 && !*$inclusive {
635 Value::from(vec![step.to_owned()])
636 } else {
637 let mut path = path.to_owned();
638 path.push(step.to_owned());
639 Value::from(path)
640 };
641
642 if let Some(expects) = $expects {
646 if step == expects {
647 let steps = to_vec_value!(val);
648
649 for step in steps {
650 $finished.push(step);
651 }
652
653 return Ok(Value::None);
654 }
655 }
656
657 if reached_max {
660 if $expects.is_none() {
661 $finished.push(val);
662 }
663 } else {
664 open.push(val);
665 }
666 }
667 }
668
669 Ok(open.into())
670 }};
671}
672
673impl RecurseInstruction {
674 pub(crate) async fn compute(
675 &self,
676 stk: &mut Stk,
677 ctx: &Context,
678 opt: &Options,
679 doc: Option<&CursorDoc>,
680 rec: Recursion<'_>,
681 finished: &mut Vec<Value>,
682 ) -> Result<Value, Error> {
683 match self {
684 Self::Path {
685 inclusive,
686 } => {
687 walk_paths!(stk, ctx, opt, doc, rec, finished, inclusive, Option::<&Value>::None)
688 }
689 Self::Shortest {
690 expects,
691 inclusive,
692 } => {
693 let expects =
694 Value::from(expects.compute(stk, ctx, opt, doc).await?.coerce_to_record()?);
695 walk_paths!(stk, ctx, opt, doc, rec, finished, inclusive, Some(&expects))
696 }
697 Self::Collect {
698 inclusive,
699 } => {
700 macro_rules! persist {
701 ($finished:ident, $subject:expr) => {
702 match $subject {
703 Value::Array(v) => {
704 for v in v.iter() {
705 if !$finished.contains(v) {
707 $finished.push(v.to_owned());
708 }
709 }
710 }
711 v => {
712 if !$finished.contains(v) {
713 $finished.push(v.to_owned());
715 }
716 }
717 }
718 };
719 }
720
721 if rec.iterated == 1 && *inclusive {
723 persist!(finished, rec.current);
724 }
725
726 let res = stk.run(|stk| rec.current.get(stk, ctx, opt, doc, rec.path)).await?;
728 let res = clean_iteration(res);
730
731 persist!(finished, &res);
733
734 Ok(res)
736 }
737 }
738 }
739}
740
741impl fmt::Display for RecurseInstruction {
742 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
743 match self {
744 Self::Path {
745 inclusive,
746 } => {
747 write!(f, "path")?;
748
749 if *inclusive {
750 write!(f, "+inclusive")?;
751 }
752
753 Ok(())
754 }
755 Self::Collect {
756 inclusive,
757 } => {
758 write!(f, "collect")?;
759
760 if *inclusive {
761 write!(f, "+inclusive")?;
762 }
763
764 Ok(())
765 }
766 Self::Shortest {
767 expects,
768 inclusive,
769 } => {
770 write!(f, "shortest={expects}")?;
771
772 if *inclusive {
773 write!(f, "+inclusive")?;
774 }
775
776 Ok(())
777 }
778 }
779 }
780}