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 pub fn new(beg: Bound<Value>, end: Bound<Value>) -> Self {
169 Self {
170 beg,
171 end,
172 }
173 }
174
175 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 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#[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}