1use std::cmp::Ordering;
2
3use common::fmt::EscapeKwFreeIdent;
4use surrealdb_strand::Strand;
5use surrealdb_types::{SqlFormat, ToSql};
6
7use crate::expr::method::is_builtin_method;
8use crate::expr::{Expr, Idiom, Literal, Lookup};
9
10#[derive(Clone, Debug, Eq, PartialEq, Hash)]
11pub enum Part {
12 All,
13 Flatten,
14 Last,
15 First,
16 Field(Strand),
17 Where(Expr),
18 Lookup(Box<Lookup>),
19 Value(Expr),
20 Start(Expr),
22 Method(Strand, Vec<Expr>),
23 Destructure(Vec<DestructurePart>),
24 Optional,
25 Recurse(Recurse, Option<Idiom>, Option<RecurseInstruction>),
26 Doc,
27 RepeatRecurse,
28}
29
30impl Part {
31 pub fn index_int(idx: i64) -> Self {
33 Part::Value(Expr::Literal(Literal::Integer(idx)))
34 }
35
36 pub fn is_index(&self) -> bool {
37 matches!(self, Part::Value(Expr::Literal(Literal::Integer(_))) | Part::First | Part::Last)
38 }
39
40 pub fn as_old_index(&self) -> Option<usize> {
49 match self {
50 Part::Value(Expr::Literal(l)) => match l {
51 crate::expr::Literal::Integer(i) => Some(*i as usize),
52 crate::expr::Literal::Float(f) => Some(*f as usize),
53 crate::expr::Literal::Decimal(d) => Some(usize::try_from(*d).unwrap_or_default()),
54 _ => None,
55 },
56 _ => None,
57 }
58 }
59
60 pub fn read_only(&self) -> bool {
66 match self {
67 Part::Start(v) => v.read_only(),
68 Part::Where(v) => v.read_only(),
69 Part::Value(v) => v.read_only(),
70 Part::Method(name, v) => is_builtin_method(name) && v.iter().all(Expr::read_only),
75 Part::Lookup(v) => v.read_only(),
76 Part::Destructure(v) => v.iter().all(DestructurePart::read_only),
77 Part::Recurse(_, alias, instruction) => {
78 alias.as_ref().map(|x| x.read_only()).unwrap_or(true)
79 && instruction.as_ref().map(|x| x.read_only()).unwrap_or(true)
80 }
81 Part::All
82 | Part::Flatten
83 | Part::Last
84 | Part::First
85 | Part::Field(_)
86 | Part::Optional
87 | Part::Doc
88 | Part::RepeatRecurse => true,
89 }
90 }
91 pub fn alias(&self) -> Option<&Idiom> {
93 match self {
94 Part::Lookup(v) => v.alias.as_ref(),
95 _ => None,
96 }
97 }
98
99 fn recursion_plan(&self) -> Option<RecursionPlan> {
100 match self {
101 Part::RepeatRecurse => Some(RecursionPlan::Repeat),
102 Part::Destructure(parts) => {
103 for (j, p) in parts.iter().enumerate() {
104 let plan = match p {
105 DestructurePart::Aliased(field, v) => v.find_recursion_plan().map(|plan| {
106 (
107 field.to_owned(),
108 plan.0.to_vec(),
109 Box::new(plan.1.clone()),
110 plan.2.to_vec(),
111 )
112 }),
113 DestructurePart::Destructure(field, parts) => {
114 Part::Destructure(parts.to_owned()).recursion_plan().map(|plan| {
115 (
116 field.to_owned(),
117 vec![Part::Field(field.to_owned())],
118 Box::new(plan),
119 vec![],
120 )
121 })
122 }
123 _ => None,
124 };
125
126 if let Some((field, before, plan, after)) = plan {
127 let mut parts = parts.clone();
128 parts.remove(j);
129 return Some(RecursionPlan::Destructure {
130 parts,
131 field,
132 before,
133 plan,
134 after,
135 });
136 }
137 }
138
139 None
140 }
141 _ => None,
142 }
143 }
144
145 pub fn to_raw_string(&self) -> String {
146 match self {
147 Part::Start(v) => v.to_raw_string(),
148 Part::Field(v) => {
149 let mut s = ".".to_string();
150 EscapeKwFreeIdent(v.as_str()).fmt_sql(&mut s, SqlFormat::SingleLine);
151 s
152 }
153 _ => self.to_sql(),
154 }
155 }
156
157 fn discriminant_value(&self) -> u8 {
159 match self {
160 Part::Field(_) => 0,
161 Part::All => 1,
162 Part::Flatten => 2,
163 Part::Last => 3,
164 Part::First => 4,
165 Part::Where(_) => 5,
166 Part::Lookup(_) => 6,
167 Part::Value(_) => 7,
168 Part::Start(_) => 8,
169 Part::Method(_, _) => 9,
170 Part::Destructure(_) => 10,
171 Part::Optional => 11,
172 Part::Recurse(_, _, _) => 12,
173 Part::Doc => 13,
174 Part::RepeatRecurse => 14,
175 }
176 }
177}
178
179impl ToSql for Part {
180 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
181 let part: crate::sql::part::Part = self.clone().into();
182 part.fmt_sql(f, fmt);
183 }
184}
185
186impl PartialOrd for Part {
187 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
188 let self_disc = self.discriminant_value();
189 let other_disc = other.discriminant_value();
190
191 match self_disc.cmp(&other_disc) {
192 Ordering::Equal => {
193 match (self, other) {
195 (Part::Field(a), Part::Field(b)) => a.partial_cmp(b),
196 (Part::Method(name_a, args_a), Part::Method(name_b, args_b)) => {
197 match name_a.partial_cmp(name_b) {
199 Some(Ordering::Equal) => args_a.len().partial_cmp(&args_b.len()),
200 other => other,
201 }
202 }
203 (Part::All, Part::All)
207 | (Part::Flatten, Part::Flatten)
208 | (Part::Last, Part::Last)
209 | (Part::First, Part::First)
210 | (Part::Optional, Part::Optional)
211 | (Part::Doc, Part::Doc)
212 | (Part::RepeatRecurse, Part::RepeatRecurse) => Some(Ordering::Equal),
213 (Part::Where(_), Part::Where(_))
218 | (Part::Lookup(_), Part::Lookup(_))
219 | (Part::Value(_), Part::Value(_))
220 | (Part::Start(_), Part::Start(_))
221 | (Part::Destructure(_), Part::Destructure(_))
222 | (Part::Recurse(_, _, _), Part::Recurse(_, _, _)) => Some(Ordering::Equal),
223
224 _ => None,
225 }
226 }
227 ordering => Some(ordering),
228 }
229 }
230}
231
232#[derive(Clone, Debug)]
235pub enum RecursionPlan {
236 Repeat,
237 Destructure {
238 parts: Vec<DestructurePart>,
240 field: Strand,
242 before: Vec<Part>,
244 plan: Box<RecursionPlan>,
246 after: Vec<Part>,
248 },
249}
250
251pub trait FindRecursionPlan<'a> {
252 fn find_recursion_plan(&'a self) -> Option<(&'a [Part], RecursionPlan, &'a [Part])>;
253}
254
255impl<'a> FindRecursionPlan<'a> for &'a [Part] {
256 fn find_recursion_plan(&'a self) -> Option<(&'a [Part], RecursionPlan, &'a [Part])> {
257 for (i, p) in self.iter().enumerate() {
258 if let Some(plan) = p.recursion_plan() {
259 return Some((&self[..i], plan, &self[(i + 1)..]));
260 }
261 }
262
263 None
264 }
265}
266
267impl<'a> FindRecursionPlan<'a> for &'a Idiom {
268 fn find_recursion_plan(&'a self) -> Option<(&'a [Part], RecursionPlan, &'a [Part])> {
269 for (i, p) in self.iter().enumerate() {
270 if let Some(plan) = p.recursion_plan() {
271 return Some((&self[..i], plan, &self[(i + 1)..]));
272 }
273 }
274
275 None
276 }
277}
278
279pub trait SplitByRepeatRecurse<'a> {
282 fn split_by_repeat_recurse(&'a self) -> Option<(&'a [Part], &'a [Part])>;
283}
284
285impl<'a> SplitByRepeatRecurse<'a> for &'a [Part] {
286 fn split_by_repeat_recurse(&'a self) -> Option<(&'a [Part], &'a [Part])> {
287 self.iter()
288 .position(|p| matches!(p, Part::RepeatRecurse))
289 .map(|i| (&self[..i], &self[(i + 1)..]))
293 }
294}
295
296impl<'a> SplitByRepeatRecurse<'a> for &'a Idiom {
297 fn split_by_repeat_recurse(&'a self) -> Option<(&'a [Part], &'a [Part])> {
298 self.iter()
299 .position(|p| matches!(p, Part::RepeatRecurse))
300 .map(|i| (&self[..i], &self[(i + 1)..]))
304 }
305}
306
307pub trait Next<'a> {
310 fn next(&'a self) -> &'a [Part];
311}
312
313impl<'a> Next<'a> for &'a [Part] {
314 fn next(&'a self) -> &'a [Part] {
315 match self.len() {
316 0 => &[],
317 _ => &self[1..],
318 }
319 }
320}
321
322pub trait NextMethod<'a> {
325 fn next_method(&'a self) -> &'a [Part];
326}
327
328impl<'a> NextMethod<'a> for &'a [Part] {
329 fn next_method(&'a self) -> &'a [Part] {
330 match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
331 None => &[],
332 Some(i) => &self[i..],
333 }
334 }
335}
336
337impl<'a> NextMethod<'a> for &'a Idiom {
338 fn next_method(&'a self) -> &'a [Part] {
339 match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
340 None => &[],
341 Some(i) => &self[i..],
342 }
343 }
344}
345
346#[derive(Clone, Debug, Eq, PartialEq, Hash)]
349pub enum DestructurePart {
350 All(Strand),
351 Field(Strand),
352 Aliased(Strand, Idiom),
353 Destructure(Strand, Vec<DestructurePart>),
354}
355
356impl DestructurePart {
357 pub fn read_only(&self) -> bool {
360 match self {
361 DestructurePart::All(_) | DestructurePart::Field(_) => true,
362 DestructurePart::Aliased(_, v) => v.read_only(),
363 DestructurePart::Destructure(_, v) => v.iter().all(DestructurePart::read_only),
364 }
365 }
366
367 pub fn field(&self) -> &str {
368 match self {
369 DestructurePart::All(v) => v.as_str(),
370 DestructurePart::Field(v) => v.as_str(),
371 DestructurePart::Aliased(v, _) => v.as_str(),
372 DestructurePart::Destructure(v, _) => v.as_str(),
373 }
374 }
375
376 pub fn path(&self) -> Vec<Part> {
377 match self {
378 DestructurePart::All(v) => vec![Part::Field(v.clone()), Part::All],
379 DestructurePart::Field(v) => vec![Part::Field(v.clone())],
380 DestructurePart::Aliased(_, v) => v.0.clone(),
381 DestructurePart::Destructure(f, d) => {
382 vec![Part::Field(f.clone()), Part::Destructure(d.clone())]
383 }
384 }
385 }
386
387 pub fn idiom(&self) -> Idiom {
388 Idiom(self.path())
389 }
390}
391
392impl ToSql for DestructurePart {
393 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
394 let stmt: crate::sql::part::DestructurePart = self.clone().into();
395 stmt.fmt_sql(f, fmt);
396 }
397}
398
399#[derive(Clone, Debug, Eq, PartialEq, Hash)]
402pub enum Recurse {
403 Fixed(u32),
404 Range(Option<u32>, Option<u32>),
405}
406
407impl ToSql for Recurse {
408 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
409 let recurse: crate::sql::part::Recurse = self.clone().into();
410 recurse.fmt_sql(f, fmt);
411 }
412}
413
414#[derive(Clone, Debug, Eq, PartialEq, Hash)]
417pub enum RecurseInstruction {
418 Path {
419 inclusive: bool,
421 },
422 Collect {
423 inclusive: bool,
425 },
426 Shortest {
427 expects: Expr,
429 inclusive: bool,
431 },
432}
433
434impl RecurseInstruction {
435 pub fn read_only(&self) -> bool {
438 match self {
439 RecurseInstruction::Path {
440 ..
441 }
442 | RecurseInstruction::Collect {
443 ..
444 } => true,
445 RecurseInstruction::Shortest {
446 expects,
447 ..
448 } => expects.read_only(),
449 }
450 }
451}
452
453impl ToSql for RecurseInstruction {
454 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
455 let stmt: crate::sql::part::RecurseInstruction = self.clone().into();
456 stmt.fmt_sql(f, fmt);
457 }
458}