1use surrealdb_types::{SqlFormat, ToSql, write_sql};
2
3use crate::fmt::{CoverStmts, EscapeKwFreeIdent, Fmt};
4use crate::sql::{Expr, Idiom, Lookup};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub(crate) enum Part {
8 All,
9 Flatten,
10 Last,
11 First,
12 Field(String),
13 Where(Expr),
14 Graph(Lookup),
15 Value(Expr),
16 Start(Expr),
17 Method(String, Vec<Expr>),
18 Destructure(Vec<DestructurePart>),
19 Optional,
20 Recurse(Recurse, Option<Idiom>, Option<RecurseInstruction>),
21 Doc,
22 RepeatRecurse,
23}
24
25impl From<Part> for crate::expr::Part {
26 fn from(v: Part) -> Self {
27 match v {
28 Part::All => Self::All,
29 Part::Flatten => Self::Flatten,
30 Part::Last => Self::Last,
31 Part::First => Self::First,
32 Part::Field(ident) => Self::Field(ident),
33 Part::Where(value) => Self::Where(value.into()),
34 Part::Graph(graph) => Self::Lookup(graph.into()),
35 Part::Value(value) => Self::Value(value.into()),
36 Part::Start(value) => Self::Start(value.into()),
37 Part::Method(method, values) => {
38 Self::Method(method, values.into_iter().map(Into::into).collect())
39 }
40 Part::Destructure(parts) => {
41 Self::Destructure(parts.into_iter().map(Into::into).collect())
42 }
43 Part::Optional => Self::Optional,
44 Part::Recurse(recurse, idiom, instructions) => {
45 let idiom = idiom.map(|idiom| idiom.into());
46 let instructions = instructions.map(Into::into);
47 crate::expr::Part::Recurse(recurse.into(), idiom, instructions)
48 }
49 Part::Doc => crate::expr::Part::Doc,
50 Part::RepeatRecurse => crate::expr::Part::RepeatRecurse,
51 }
52 }
53}
54
55impl From<crate::expr::Part> for Part {
56 fn from(v: crate::expr::Part) -> Self {
57 match v {
58 crate::expr::Part::All => Self::All,
59 crate::expr::Part::Flatten => Self::Flatten,
60 crate::expr::Part::Last => Self::Last,
61 crate::expr::Part::First => Self::First,
62 crate::expr::Part::Field(ident) => Self::Field(ident),
63 crate::expr::Part::Where(value) => Self::Where(value.into()),
64 crate::expr::Part::Lookup(graph) => Self::Graph(graph.into()),
65 crate::expr::Part::Value(value) => Self::Value(value.into()),
66 crate::expr::Part::Start(value) => Self::Start(value.into()),
67 crate::expr::Part::Method(method, values) => {
68 Self::Method(method, values.into_iter().map(Into::into).collect())
69 }
70 crate::expr::Part::Destructure(parts) => {
71 Self::Destructure(parts.into_iter().map(Into::<DestructurePart>::into).collect())
72 }
73 crate::expr::Part::Optional => Self::Optional,
74 crate::expr::Part::Recurse(recurse, idiom, instructions) => Self::Recurse(
75 recurse.into(),
76 idiom.map(|idiom| idiom.into()),
77 instructions.map(Into::into),
78 ),
79 crate::expr::Part::Doc => Self::Doc,
80 crate::expr::Part::RepeatRecurse => Self::RepeatRecurse,
81 }
82 }
83}
84
85impl ToSql for Part {
86 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
87 match self {
88 Part::All => f.push_str(".*"),
89 Part::Last => f.push_str("[$]"),
90 Part::First => f.push_str("[0]"),
91 Part::Start(v) => v.fmt_sql(f, fmt),
92 Part::Field(v) => write_sql!(f, fmt, ".{}", EscapeKwFreeIdent(v)),
93 Part::Flatten => f.push('…'),
94 Part::Where(v) => write_sql!(f, fmt, "[WHERE {v}]"),
95 Part::Graph(v) => v.fmt_sql(f, fmt),
96 Part::Value(v) => write_sql!(f, fmt, "[{v}]"),
97 Part::Method(v, a) => {
98 write_sql!(
99 f,
100 fmt,
101 ".{}({})",
102 EscapeKwFreeIdent(v),
103 Fmt::comma_separated(a.iter().map(CoverStmts))
104 )
105 }
106 Part::Destructure(v) => {
107 f.push_str(".{");
108 if !fmt.is_pretty() {
109 f.push(' ');
110 }
111 if !v.is_empty() {
112 let fmt = fmt.increment();
113 write_sql!(f, fmt, "{}", Fmt::pretty_comma_separated(v));
114 }
115 if fmt.is_pretty() {
116 f.push('}');
117 } else {
118 f.push_str(" }");
119 }
120 }
121 Part::Optional => f.push_str(".?"),
122 Part::Recurse(v, nest, instruction) => {
123 write_sql!(f, fmt, ".{{{v}");
124 if let Some(instruction) = instruction {
125 write_sql!(f, fmt, "+{instruction}");
126 }
127 f.push('}');
128
129 if let Some(nest) = nest {
130 f.push('(');
131 for p in nest.0.iter() {
132 p.fmt_sql(f, fmt);
133 }
134 f.push(')');
135 }
136 }
137 Part::Doc => f.push('@'),
138 Part::RepeatRecurse => f.push_str(".@"),
139 }
140 }
141}
142
143#[derive(Clone, Debug, PartialEq, Eq)]
146#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
147pub enum DestructurePart {
148 All(String),
149 Field(String),
150 Aliased(String, Idiom),
151 Destructure(String, Vec<DestructurePart>),
152}
153
154impl ToSql for DestructurePart {
155 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
156 match self {
157 DestructurePart::All(fd) => write_sql!(f, fmt, "{}.*", EscapeKwFreeIdent(fd)),
158 DestructurePart::Field(fd) => write_sql!(f, fmt, "{}", EscapeKwFreeIdent(fd)),
159 DestructurePart::Aliased(fd, v) => write_sql!(f, fmt, "{}: {v}", EscapeKwFreeIdent(fd)),
160 DestructurePart::Destructure(fd, d) => {
161 write_sql!(f, fmt, "{}{}", EscapeKwFreeIdent(fd), Part::Destructure(d.clone()))
162 }
163 }
164 }
165}
166
167impl From<DestructurePart> for crate::expr::part::DestructurePart {
168 fn from(v: DestructurePart) -> Self {
169 match v {
170 DestructurePart::All(v) => Self::All(v),
171 DestructurePart::Field(v) => Self::Field(v),
172 DestructurePart::Aliased(v, idiom) => Self::Aliased(v, idiom.into()),
173 DestructurePart::Destructure(v, d) => {
174 Self::Destructure(v, d.into_iter().map(Into::into).collect())
175 }
176 }
177 }
178}
179
180impl From<crate::expr::part::DestructurePart> for DestructurePart {
181 fn from(v: crate::expr::part::DestructurePart) -> Self {
182 match v {
183 crate::expr::part::DestructurePart::All(v) => Self::All(v),
184 crate::expr::part::DestructurePart::Field(v) => Self::Field(v),
185 crate::expr::part::DestructurePart::Aliased(v, idiom) => Self::Aliased(v, idiom.into()),
186 crate::expr::part::DestructurePart::Destructure(v, d) => {
187 Self::Destructure(v, d.into_iter().map(Into::<DestructurePart>::into).collect())
188 }
189 }
190 }
191}
192
193#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
196#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
197pub enum Recurse {
198 Fixed(u32),
199 Range(Option<u32>, Option<u32>),
200}
201
202impl ToSql for Recurse {
203 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
204 match self {
205 Recurse::Fixed(v) => f.push_str(&v.to_string()),
206 Recurse::Range(beg, end) => match (beg, end) {
207 (None, None) => f.push_str(".."),
208 (Some(beg), None) => {
209 f.push_str(&beg.to_string());
210 f.push_str("..");
211 }
212 (None, Some(end)) => {
213 f.push_str("..");
214 f.push_str(&end.to_string());
215 }
216 (Some(beg), Some(end)) => {
217 f.push_str(&beg.to_string());
218 f.push_str("..");
219 f.push_str(&end.to_string());
220 }
221 },
222 }
223 }
224}
225
226impl From<Recurse> for crate::expr::part::Recurse {
227 fn from(v: Recurse) -> Self {
228 match v {
229 Recurse::Fixed(v) => Self::Fixed(v),
230 Recurse::Range(min, max) => Self::Range(min, max),
231 }
232 }
233}
234
235impl From<crate::expr::part::Recurse> for Recurse {
236 fn from(v: crate::expr::part::Recurse) -> Self {
237 match v {
238 crate::expr::part::Recurse::Fixed(v) => Self::Fixed(v),
239 crate::expr::part::Recurse::Range(min, max) => Self::Range(min, max),
240 }
241 }
242}
243#[derive(Clone, Debug, PartialEq, Eq)]
246pub enum RecurseInstruction {
247 Path {
248 inclusive: bool,
250 },
251 Collect {
252 inclusive: bool,
254 },
255 Shortest {
256 expects: Expr,
258 inclusive: bool,
260 },
261}
262
263impl ToSql for RecurseInstruction {
264 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
265 match self {
266 Self::Path {
267 inclusive,
268 } => {
269 f.push_str("path");
270
271 if *inclusive {
272 f.push_str("+inclusive");
273 }
274 }
275 Self::Collect {
276 inclusive,
277 } => {
278 f.push_str("collect");
279
280 if *inclusive {
281 f.push_str("+inclusive");
282 }
283 }
284 Self::Shortest {
285 expects,
286 inclusive,
287 } => {
288 write_sql!(f, fmt, "shortest={expects}");
289
290 if *inclusive {
291 f.push_str("+inclusive");
292 }
293 }
294 }
295 }
296}
297
298impl From<RecurseInstruction> for crate::expr::part::RecurseInstruction {
299 fn from(v: RecurseInstruction) -> Self {
300 match v {
301 RecurseInstruction::Path {
302 inclusive,
303 } => Self::Path {
304 inclusive,
305 },
306 RecurseInstruction::Collect {
307 inclusive,
308 } => Self::Collect {
309 inclusive,
310 },
311 RecurseInstruction::Shortest {
312 expects,
313 inclusive,
314 } => Self::Shortest {
315 expects: expects.into(),
316 inclusive,
317 },
318 }
319 }
320}
321
322impl From<crate::expr::part::RecurseInstruction> for RecurseInstruction {
323 fn from(v: crate::expr::part::RecurseInstruction) -> Self {
324 match v {
325 crate::expr::part::RecurseInstruction::Path {
326 inclusive,
327 } => Self::Path {
328 inclusive,
329 },
330 crate::expr::part::RecurseInstruction::Collect {
331 inclusive,
332 } => Self::Collect {
333 inclusive,
334 },
335 crate::expr::part::RecurseInstruction::Shortest {
336 expects,
337 inclusive,
338 } => Self::Shortest {
339 expects: expects.into(),
340 inclusive,
341 },
342 }
343 }
344}