surrealdb_core/sql/
range.rs

1use super::Id;
2use crate::cnf::GENERATION_ALLOCATION_LIMIT;
3use crate::ctx::Context;
4use crate::dbs::Options;
5use crate::doc::CursorDoc;
6use crate::err::Error;
7use crate::sql::{Number, Subquery, Value};
8use crate::syn;
9use reblessive::tree::Stk;
10use revision::revisioned;
11use serde::{Deserialize, Serialize};
12use std::cmp::Ordering;
13use std::fmt;
14use std::ops::Bound;
15use std::str::FromStr;
16
17pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Range";
18
19#[revisioned(revision = 1)]
20#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
21#[serde(rename = "$surrealdb::private::sql::Range")]
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
23#[non_exhaustive]
24pub struct Range {
25	pub beg: Bound<Value>,
26	pub end: Bound<Value>,
27}
28
29impl Range {
30	pub fn slice<'a, T>(&self, s: &'a [T]) -> Option<&'a [T]> {
31		let r = match self.end {
32			Bound::Included(ref x) => {
33				let Value::Number(ref x) = x else {
34					return None;
35				};
36				let x = x.to_usize();
37				s.get(..=x)?
38			}
39			Bound::Excluded(ref x) => {
40				let Value::Number(ref x) = x else {
41					return None;
42				};
43				let x = x.to_usize();
44				s.get(..x)?
45			}
46			Bound::Unbounded => s,
47		};
48		let r = match self.beg {
49			Bound::Included(ref x) => {
50				let Value::Number(ref x) = x else {
51					return None;
52				};
53				let x = x.to_usize();
54				r.get(x..)?
55			}
56			Bound::Excluded(ref x) => {
57				let Value::Number(ref x) = x else {
58					return None;
59				};
60				let x = x.to_usize().saturating_add(1);
61				r.get(x..)?
62			}
63			Bound::Unbounded => r,
64		};
65		Some(r)
66	}
67
68	pub fn slice_mut<'a, T>(&self, s: &'a mut [T]) -> Option<&'a mut [T]> {
69		let r = match self.end {
70			Bound::Included(ref x) => {
71				let Value::Number(ref x) = x else {
72					return None;
73				};
74				let x = x.to_usize();
75				s.get_mut(..x)?
76			}
77			Bound::Excluded(ref x) => {
78				let Value::Number(ref x) = x else {
79					return None;
80				};
81				let x = x.to_usize();
82				s.get_mut(..=x)?
83			}
84			Bound::Unbounded => s,
85		};
86		let r = match self.beg {
87			Bound::Included(ref x) => {
88				let Value::Number(ref x) = x else {
89					return None;
90				};
91				let x = x.to_usize();
92				r.get_mut(x..)?
93			}
94			Bound::Excluded(ref x) => {
95				let Value::Number(ref x) = x else {
96					return None;
97				};
98				let x = x.to_usize().saturating_add(1);
99				r.get_mut(x..)?
100			}
101			Bound::Unbounded => r,
102		};
103		Some(r)
104	}
105}
106
107impl FromStr for Range {
108	type Err = ();
109	fn from_str(s: &str) -> Result<Self, Self::Err> {
110		Self::try_from(s)
111	}
112}
113
114impl TryFrom<&str> for Range {
115	type Error = ();
116	fn try_from(v: &str) -> Result<Self, Self::Error> {
117		match syn::range(v) {
118			Ok(v) => Ok(v),
119			_ => Err(()),
120		}
121	}
122}
123
124impl From<(Bound<Id>, Bound<Id>)> for Range {
125	fn from(v: (Bound<Id>, Bound<Id>)) -> Self {
126		fn convert(v: Bound<Id>) -> Bound<Value> {
127			match v {
128				Bound::Included(v) => Bound::Included(v.into()),
129				Bound::Excluded(v) => Bound::Excluded(v.into()),
130				Bound::Unbounded => Bound::Unbounded,
131			}
132		}
133
134		Self {
135			beg: convert(v.0),
136			end: convert(v.1),
137		}
138	}
139}
140
141impl TryInto<std::ops::Range<i64>> for Range {
142	type Error = Error;
143	fn try_into(self) -> Result<std::ops::Range<i64>, Self::Error> {
144		let beg = match self.beg {
145			Bound::Unbounded => i64::MIN,
146			Bound::Included(beg) => to_i64(beg)?,
147			Bound::Excluded(beg) => to_i64(beg)? + 1,
148		};
149
150		let end = match self.end {
151			Bound::Unbounded => i64::MAX,
152			Bound::Included(end) => to_i64(end)? + 1,
153			Bound::Excluded(end) => to_i64(end)?,
154		};
155
156		if (beg + *GENERATION_ALLOCATION_LIMIT as i64) < end {
157			Err(Error::RangeTooBig {
158				max: *GENERATION_ALLOCATION_LIMIT,
159			})
160		} else {
161			Ok(beg..end)
162		}
163	}
164}
165
166impl Range {
167	/// Construct a new range
168	pub fn new(beg: Bound<Value>, end: Bound<Value>) -> Self {
169		Self {
170			beg,
171			end,
172		}
173	}
174
175	/// Process this type returning a computed simple Value
176	pub(crate) async fn compute(
177		&self,
178		stk: &mut Stk,
179		ctx: &Context,
180		opt: &Options,
181		doc: Option<&CursorDoc>,
182	) -> Result<Value, Error> {
183		Ok(Value::Range(Box::new(Range {
184			beg: match &self.beg {
185				Bound::Included(v) => Bound::Included(v.compute(stk, ctx, opt, doc).await?),
186				Bound::Excluded(v) => Bound::Excluded(v.compute(stk, ctx, opt, doc).await?),
187				Bound::Unbounded => Bound::Unbounded,
188			},
189			end: match &self.end {
190				Bound::Included(v) => Bound::Included(v.compute(stk, ctx, opt, doc).await?),
191				Bound::Excluded(v) => Bound::Excluded(v.compute(stk, ctx, opt, doc).await?),
192				Bound::Unbounded => Bound::Unbounded,
193			},
194		})))
195	}
196
197	/// Validate that a Range contains only computed Values
198	pub fn validate_computed(&self) -> Result<(), Error> {
199		match &self.beg {
200			Bound::Included(ref v) | Bound::Excluded(ref v) => v.validate_computed()?,
201			Bound::Unbounded => {}
202		}
203		match &self.end {
204			Bound::Included(ref v) | Bound::Excluded(ref v) => v.validate_computed()?,
205			Bound::Unbounded => {}
206		}
207
208		Ok(())
209	}
210}
211
212impl PartialOrd for Range {
213	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
214		Some(self.cmp(other))
215	}
216}
217
218impl Ord for Range {
219	fn cmp(&self, other: &Self) -> Ordering {
220		match &self.beg {
221			Bound::Unbounded => match &other.beg {
222				Bound::Unbounded => Ordering::Equal,
223				_ => Ordering::Less,
224			},
225			Bound::Included(v) => match &other.beg {
226				Bound::Unbounded => Ordering::Greater,
227				Bound::Included(w) => match v.cmp(w) {
228					Ordering::Equal => match &self.end {
229						Bound::Unbounded => match &other.end {
230							Bound::Unbounded => Ordering::Equal,
231							_ => Ordering::Greater,
232						},
233						Bound::Included(v) => match &other.end {
234							Bound::Unbounded => Ordering::Less,
235							Bound::Included(w) => v.cmp(w),
236							_ => Ordering::Greater,
237						},
238						Bound::Excluded(v) => match &other.end {
239							Bound::Excluded(w) => v.cmp(w),
240							_ => Ordering::Less,
241						},
242					},
243					ordering => ordering,
244				},
245				_ => Ordering::Less,
246			},
247			Bound::Excluded(v) => match &other.beg {
248				Bound::Excluded(w) => match v.cmp(w) {
249					Ordering::Equal => match &self.end {
250						Bound::Unbounded => match &other.end {
251							Bound::Unbounded => Ordering::Equal,
252							_ => Ordering::Greater,
253						},
254						Bound::Included(v) => match &other.end {
255							Bound::Unbounded => Ordering::Less,
256							Bound::Included(w) => v.cmp(w),
257							_ => Ordering::Greater,
258						},
259						Bound::Excluded(v) => match &other.end {
260							Bound::Excluded(w) => v.cmp(w),
261							_ => Ordering::Less,
262						},
263					},
264					ordering => ordering,
265				},
266				_ => Ordering::Greater,
267			},
268		}
269	}
270}
271
272impl fmt::Display for Range {
273	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
274		fn bound_value(v: &Value) -> Value {
275			if v.can_be_range_bound() {
276				v.to_owned()
277			} else {
278				Value::Subquery(Box::new(Subquery::Value(v.to_owned())))
279			}
280		}
281
282		match &self.beg {
283			Bound::Unbounded => write!(f, ""),
284			Bound::Included(v) => write!(f, "{}", bound_value(v)),
285			Bound::Excluded(v) => write!(f, "{}>", bound_value(v)),
286		}?;
287		match &self.end {
288			Bound::Unbounded => write!(f, ".."),
289			Bound::Excluded(v) => write!(f, "..{}", bound_value(v)),
290			Bound::Included(v) => write!(f, "..={}", bound_value(v)),
291		}?;
292		Ok(())
293	}
294}
295
296fn to_i64(v: Value) -> Result<i64, Error> {
297	match v {
298		Value::Number(Number::Int(v)) => Ok(v),
299		v => Err(Error::InvalidRangeValue {
300			expected: "int".to_string(),
301			found: v.kindof().to_string(),
302		}),
303	}
304}
305
306// Structs needed for revision convertion from old ranges
307
308#[revisioned(revision = 1)]
309#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
310#[serde(rename = "$surrealdb::private::sql::Range")]
311#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
312#[non_exhaustive]
313pub struct OldRange {
314	pub tb: String,
315	pub beg: Bound<Id>,
316	pub end: Bound<Id>,
317}