Skip to main content

surql_parser/upstream/sql/
part.rs

1use crate::upstream::fmt::{CoverStmts, EscapeKwFreeIdent, Fmt};
2use crate::upstream::sql::{Expr, Idiom, Lookup};
3use surrealdb_types::{SqlFormat, ToSql, write_sql};
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub enum Part {
6	All,
7	Flatten,
8	Last,
9	First,
10	Field(String),
11	Where(Expr),
12	Graph(Lookup),
13	Value(Expr),
14	Start(Expr),
15	Method(String, Vec<Expr>),
16	Destructure(Vec<DestructurePart>),
17	Optional,
18	Recurse(Recurse, Option<Idiom>, Option<RecurseInstruction>),
19	Doc,
20	RepeatRecurse,
21}
22impl ToSql for Part {
23	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
24		match self {
25			Part::All => f.push_str(".*"),
26			Part::Last => f.push_str("[$]"),
27			Part::First => f.push_str("[0]"),
28			Part::Start(v) => v.fmt_sql(f, fmt),
29			Part::Field(v) => write_sql!(f, fmt, ".{}", EscapeKwFreeIdent(v)),
30			Part::Flatten => f.push('…'),
31			Part::Where(v) => write_sql!(f, fmt, "[WHERE {v}]"),
32			Part::Graph(v) => v.fmt_sql(f, fmt),
33			Part::Value(v) => write_sql!(f, fmt, "[{v}]"),
34			Part::Method(v, a) => {
35				write_sql!(
36					f,
37					fmt,
38					".{}({})",
39					EscapeKwFreeIdent(v),
40					Fmt::comma_separated(a.iter().map(CoverStmts))
41				)
42			}
43			Part::Destructure(v) => {
44				f.push_str(".{");
45				if !fmt.is_pretty() {
46					f.push(' ');
47				}
48				if !v.is_empty() {
49					let fmt = fmt.increment();
50					write_sql!(f, fmt, "{}", Fmt::pretty_comma_separated(v));
51				}
52				if fmt.is_pretty() {
53					f.push('}');
54				} else {
55					f.push_str(" }");
56				}
57			}
58			Part::Optional => f.push_str(".?"),
59			Part::Recurse(v, nest, instruction) => {
60				write_sql!(f, fmt, ".{{{v}");
61				if let Some(instruction) = instruction {
62					write_sql!(f, fmt, "+{instruction}");
63				}
64				f.push('}');
65				if let Some(nest) = nest {
66					f.push('(');
67					for p in nest.0.iter() {
68						p.fmt_sql(f, fmt);
69					}
70					f.push(')');
71				}
72			}
73			Part::Doc => f.push('@'),
74			Part::RepeatRecurse => f.push_str(".@"),
75		}
76	}
77}
78#[derive(Clone, Debug, PartialEq, Eq)]
79#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
80pub enum DestructurePart {
81	All(String),
82	Field(String),
83	Aliased(String, Idiom),
84	Destructure(String, Vec<DestructurePart>),
85}
86impl ToSql for DestructurePart {
87	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
88		match self {
89			DestructurePart::All(fd) => write_sql!(f, fmt, "{}.*", EscapeKwFreeIdent(fd)),
90			DestructurePart::Field(fd) => write_sql!(f, fmt, "{}", EscapeKwFreeIdent(fd)),
91			DestructurePart::Aliased(fd, v) => {
92				write_sql!(f, fmt, "{}: {v}", EscapeKwFreeIdent(fd))
93			}
94			DestructurePart::Destructure(fd, d) => {
95				write_sql!(
96					f,
97					fmt,
98					"{}{}",
99					EscapeKwFreeIdent(fd),
100					Part::Destructure(d.clone())
101				)
102			}
103		}
104	}
105}
106#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
107#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
108pub enum Recurse {
109	Fixed(u32),
110	Range(Option<u32>, Option<u32>),
111}
112impl ToSql for Recurse {
113	fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
114		match self {
115			Recurse::Fixed(v) => f.push_str(&v.to_string()),
116			Recurse::Range(beg, end) => match (beg, end) {
117				(None, None) => f.push_str(".."),
118				(Some(beg), None) => {
119					f.push_str(&beg.to_string());
120					f.push_str("..");
121				}
122				(None, Some(end)) => {
123					f.push_str("..");
124					f.push_str(&end.to_string());
125				}
126				(Some(beg), Some(end)) => {
127					f.push_str(&beg.to_string());
128					f.push_str("..");
129					f.push_str(&end.to_string());
130				}
131			},
132		}
133	}
134}
135#[derive(Clone, Debug, PartialEq, Eq)]
136pub enum RecurseInstruction {
137	Path { inclusive: bool },
138	Collect { inclusive: bool },
139	Shortest { expects: Expr, inclusive: bool },
140}
141impl ToSql for RecurseInstruction {
142	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
143		match self {
144			Self::Path { inclusive } => {
145				f.push_str("path");
146				if *inclusive {
147					f.push_str("+inclusive");
148				}
149			}
150			Self::Collect { inclusive } => {
151				f.push_str("collect");
152				if *inclusive {
153					f.push_str("+inclusive");
154				}
155			}
156			Self::Shortest { expects, inclusive } => {
157				write_sql!(f, fmt, "shortest={expects}");
158				if *inclusive {
159					f.push_str("+inclusive");
160				}
161			}
162		}
163	}
164}