Skip to main content

surql_parser/upstream/syn/parser/
record_id.rs

1use super::{ParseResult, Parser};
2use crate::upstream::sql::lookup::LookupSubject;
3use crate::upstream::sql::{
4	Param, RecordIdKeyGen, RecordIdKeyLit, RecordIdKeyRangeLit, RecordIdLit,
5};
6use crate::upstream::syn::error::bail;
7use crate::upstream::syn::lexer::compound;
8use crate::upstream::syn::parser::mac::{expected, expected_whitespace, unexpected};
9use crate::upstream::syn::token::{TokenKind, t};
10use reblessive::Stk;
11use std::cmp::Ordering;
12use std::ops::Bound;
13use surrealdb_types::ToSql;
14impl Parser<'_> {
15	pub async fn parse_record_id_or_range(
16		&mut self,
17		stk: &mut Stk,
18		ident: String,
19	) -> ParseResult<RecordIdLit> {
20		expected_whitespace!(self, t!(":"));
21		if self.eat_whitespace(t!("..")) {
22			let end = if self.eat_whitespace(t!("=")) {
23				let id = stk.run(|stk| self.parse_record_id_key(stk)).await?;
24				Bound::Included(id)
25			} else if let Some(peek) = self.peek_whitespace()
26				&& Self::kind_starts_record_id_key(peek.kind)
27			{
28				let id = stk.run(|stk| self.parse_record_id_key(stk)).await?;
29				Bound::Excluded(id)
30			} else {
31				Bound::Unbounded
32			};
33			return Ok(RecordIdLit {
34				table: ident,
35				key: RecordIdKeyLit::Range(Box::new(RecordIdKeyRangeLit {
36					start: Bound::Unbounded,
37					end,
38				})),
39			});
40		}
41		let beg = if let Some(peek) = self.peek_whitespace()
42			&& Self::kind_starts_record_id_key(peek.kind)
43		{
44			let v = stk.run(|stk| self.parse_record_id_key(stk)).await?;
45			if self.eat_whitespace(t!(">")) {
46				Bound::Excluded(v)
47			} else {
48				Bound::Included(v)
49			}
50		} else {
51			Bound::Unbounded
52		};
53		if self.eat_whitespace(t!("..")) {
54			let end = if self.eat_whitespace(t!("=")) {
55				let id = stk.run(|stk| self.parse_record_id_key(stk)).await?;
56				Bound::Included(id)
57			} else if let Some(peek) = self.peek_whitespace()
58				&& Self::kind_starts_record_id_key(peek.kind)
59			{
60				let id = stk.run(|stk| self.parse_record_id_key(stk)).await?;
61				Bound::Excluded(id)
62			} else {
63				Bound::Unbounded
64			};
65			Ok(RecordIdLit {
66				table: ident,
67				key: RecordIdKeyLit::Range(Box::new(RecordIdKeyRangeLit { start: beg, end })),
68			})
69		} else {
70			let id = match beg {
71				Bound::Unbounded => {
72					if let Some(token) = self.peek_whitespace()
73						&& token.kind == t!("$param")
74					{
75						let param = self.next_token_value::<Param>()?;
76						bail!(
77							"Unexpected token `$param` expected a record-id key", @ token
78							.span =>
79							"Record-id's can be create from a param with `type::record(\"{}\",{})`",
80							ident, param.to_sql()
81						);
82					}
83					unexpected!(self, self.peek(), "a record-id key")
84				}
85				Bound::Excluded(_) => {
86					unexpected!(self, self.peek(), "the range operator `..`")
87				}
88				Bound::Included(v) => v,
89			};
90			Ok(RecordIdLit {
91				table: ident,
92				key: id,
93			})
94		}
95	}
96	pub async fn parse_id_range(&mut self, stk: &mut Stk) -> ParseResult<RecordIdKeyRangeLit> {
97		let beg = if let Some(peek) = self.peek_whitespace()
98			&& Self::kind_starts_record_id_key(peek.kind)
99		{
100			let v = stk.run(|stk| self.parse_record_id_key(stk)).await?;
101			if self.eat_whitespace(t!(">")) {
102				Bound::Excluded(v)
103			} else {
104				Bound::Included(v)
105			}
106		} else {
107			Bound::Unbounded
108		};
109		expected!(self, t!(".."));
110		let end = if self.eat_whitespace(t!("=")) {
111			let id = stk.run(|stk| self.parse_record_id_key(stk)).await?;
112			Bound::Included(id)
113		} else if let Some(peek) = self.peek_whitespace()
114			&& Self::kind_starts_record_id_key(peek.kind)
115		{
116			let id = stk.run(|stk| self.parse_record_id_key(stk)).await?;
117			Bound::Excluded(id)
118		} else {
119			Bound::Unbounded
120		};
121		Ok(RecordIdKeyRangeLit { start: beg, end })
122	}
123	pub async fn parse_lookup_subject(
124		&mut self,
125		stk: &mut Stk,
126		supports_referencing_field: bool,
127	) -> ParseResult<LookupSubject> {
128		let table = self.parse_ident()?;
129		if self.eat_whitespace(t!(":")) {
130			let range = self.parse_id_range(stk).await?;
131			let referencing_field = self
132				.parse_referencing_field(supports_referencing_field)
133				.await?;
134			Ok(LookupSubject::Range {
135				table,
136				range,
137				referencing_field,
138			})
139		} else {
140			Ok(LookupSubject::Table {
141				table,
142				referencing_field: self
143					.parse_referencing_field(supports_referencing_field)
144					.await?,
145			})
146		}
147	}
148	pub async fn parse_referencing_field(
149		&mut self,
150		supports_referencing_field: bool,
151	) -> ParseResult<Option<String>> {
152		if supports_referencing_field && self.eat(t!("FIELD")) {
153			Ok(Some(self.parse_ident()?))
154		} else {
155			Ok(None)
156		}
157	}
158	pub async fn parse_record_id(&mut self, stk: &mut Stk) -> ParseResult<RecordIdLit> {
159		let ident = self.parse_ident()?;
160		self.parse_record_id_from_ident(stk, ident).await
161	}
162	pub async fn parse_record_id_from_ident(
163		&mut self,
164		stk: &mut Stk,
165		ident: String,
166	) -> ParseResult<RecordIdLit> {
167		expected!(self, t!(":"));
168		let id = stk.run(|ctx| self.parse_record_id_key(ctx)).await?;
169		Ok(RecordIdLit {
170			table: ident,
171			key: id,
172		})
173	}
174	pub async fn parse_record_id_key(&mut self, stk: &mut Stk) -> ParseResult<RecordIdKeyLit> {
175		let Some(token) = self.peek_whitespace() else {
176			bail!("Unexpected whitespace after record-id table", @ self.peek().span)
177		};
178		match token.kind {
179			t!("u'") | t!("u\"") => Ok(RecordIdKeyLit::Uuid(self.next_token_value()?)),
180			t!("{") => {
181				self.pop_peek();
182				let object = self.parse_object(stk, token.span).await?;
183				Ok(RecordIdKeyLit::Object(object))
184			}
185			t!("[") => {
186				self.pop_peek();
187				let array = self.parse_array(stk, token.span).await?;
188				Ok(RecordIdKeyLit::Array(array))
189			}
190			t!("+") => {
191				self.pop_peek();
192				let digits_token = if let Some(digits_token) = self.peek_whitespace() {
193					match digits_token.kind {
194						TokenKind::Digits => digits_token,
195						_ => unexpected!(self, digits_token, "an integer"),
196					}
197				} else {
198					unexpected!(self, token, "a record-id key")
199				};
200				if let Some(next) = self.peek_whitespace() {
201					match next.kind {
202						t!(".") => {
203							unexpected!(
204								self, next, "an integer", =>
205								"Numeric Record-id keys can only be integers"
206							);
207						}
208						x if Self::kind_is_identifier(x) => {
209							let span = token.span.covers(next.span);
210							bail!("Unexpected token `{x}` expected an integer", @ span);
211						}
212						_ => {}
213					}
214				}
215				let digits_str = self.span_str(digits_token.span);
216				if let Ok(number) = digits_str.parse() {
217					Ok(RecordIdKeyLit::Number(number))
218				} else {
219					Ok(RecordIdKeyLit::String(digits_str.to_owned()))
220				}
221			}
222			t!("-") => {
223				self.pop_peek();
224				let token = expected!(self, TokenKind::Digits);
225				if let Ok(number) = self.lex_compound(token, compound::integer::<u64>) {
226					match number.value.cmp(&((i64::MAX as u64) + 1)) {
227						Ordering::Less => Ok(RecordIdKeyLit::Number(-(number.value as i64))),
228						Ordering::Equal => Ok(RecordIdKeyLit::Number(i64::MIN)),
229						Ordering::Greater => Ok(RecordIdKeyLit::String(format!(
230							"-{}",
231							self.span_str(number.span)
232						))),
233					}
234				} else {
235					let strand = format!("-{}", self.span_str(token.span));
236					Ok(RecordIdKeyLit::String(strand))
237				}
238			}
239			TokenKind::Digits => {
240				if self.settings.flexible_record_id
241					&& let Some(next) = self.peek_whitespace1()
242					&& (Self::kind_is_identifier(next.kind)
243						|| next.kind == TokenKind::NaN
244						|| next.kind == TokenKind::Infinity)
245				{
246					let ident = self.parse_flexible_ident()?;
247					return Ok(RecordIdKeyLit::String(ident));
248				}
249				self.pop_peek();
250				let digits_str = self.span_str(token.span);
251				if let Ok(number) = digits_str.parse::<i64>() {
252					Ok(RecordIdKeyLit::Number(number))
253				} else {
254					Ok(RecordIdKeyLit::String(digits_str.to_owned()))
255				}
256			}
257			t!("ULID") => {
258				let token = self.pop_peek();
259				if self.eat(t!("(")) {
260					expected!(self, t!(")"));
261					Ok(RecordIdKeyLit::Generate(RecordIdKeyGen::Ulid))
262				} else {
263					let slice = self.span_str(token.span);
264					Ok(RecordIdKeyLit::String(slice.to_owned()))
265				}
266			}
267			t!("UUID") => {
268				let token = self.pop_peek();
269				if self.eat(t!("(")) {
270					expected!(self, t!(")"));
271					Ok(RecordIdKeyLit::Generate(RecordIdKeyGen::Uuid))
272				} else {
273					let slice = self.span_str(token.span);
274					Ok(RecordIdKeyLit::String(slice.to_owned()))
275				}
276			}
277			t!("RAND") => {
278				let token = self.pop_peek();
279				if self.eat(t!("(")) {
280					expected!(self, t!(")"));
281					Ok(RecordIdKeyLit::Generate(RecordIdKeyGen::Rand))
282				} else {
283					let slice = self.span_str(token.span);
284					Ok(RecordIdKeyLit::String(slice.to_owned()))
285				}
286			}
287			_ => {
288				let ident = if self.settings.flexible_record_id {
289					self.parse_flexible_ident()?
290				} else {
291					self.parse_ident()?
292				};
293				Ok(RecordIdKeyLit::String(ident))
294			}
295		}
296	}
297}