1use crate::ctx::Context;
2use crate::dbs::Options;
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::sql::Cond;
6use crate::sql::Expression;
7use crate::sql::Ident;
8use crate::sql::Idiom;
9use crate::sql::Operator;
10use crate::sql::Part;
11use crate::sql::Thing;
12use crate::sql::{strand::no_nul_bytes, Id, Value};
13use crate::syn;
14use reblessive::tree::Stk;
15use revision::revisioned;
16use serde::{Deserialize, Serialize};
17use std::cmp::Ordering;
18use std::fmt;
19use std::ops::Bound;
20use std::str::FromStr;
21
22const ID: &str = "id";
23pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Range";
24
25#[revisioned(revision = 1)]
26#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
27#[serde(rename = "$surrealdb::private::sql::Range")]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29#[non_exhaustive]
30pub struct Range {
31 #[serde(with = "no_nul_bytes")]
32 pub tb: String,
33 pub beg: Bound<Id>,
34 pub end: Bound<Id>,
35}
36
37impl FromStr for Range {
38 type Err = ();
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 Self::try_from(s)
41 }
42}
43
44impl TryFrom<&str> for Range {
45 type Error = ();
46 fn try_from(v: &str) -> Result<Self, Self::Error> {
47 match syn::range(v) {
48 Ok(v) => Ok(v),
49 _ => Err(()),
50 }
51 }
52}
53
54impl Range {
55 pub fn new(tb: String, beg: Bound<Id>, end: Bound<Id>) -> Self {
57 Self {
58 tb,
59 beg,
60 end,
61 }
62 }
63
64 pub fn to_cond(self) -> Option<Cond> {
66 match (self.beg, self.end) {
67 (Bound::Unbounded, Bound::Unbounded) => None,
68 (Bound::Unbounded, Bound::Excluded(id)) => {
69 Some(Cond(Value::Expression(Box::new(Expression::new(
70 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
71 Operator::LessThan,
72 Thing::from((self.tb, id)).into(),
73 )))))
74 }
75 (Bound::Unbounded, Bound::Included(id)) => {
76 Some(Cond(Value::Expression(Box::new(Expression::new(
77 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
78 Operator::LessThanOrEqual,
79 Thing::from((self.tb, id)).into(),
80 )))))
81 }
82 (Bound::Excluded(id), Bound::Unbounded) => {
83 Some(Cond(Value::Expression(Box::new(Expression::new(
84 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
85 Operator::MoreThan,
86 Thing::from((self.tb, id)).into(),
87 )))))
88 }
89 (Bound::Included(id), Bound::Unbounded) => {
90 Some(Cond(Value::Expression(Box::new(Expression::new(
91 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
92 Operator::MoreThanOrEqual,
93 Thing::from((self.tb, id)).into(),
94 )))))
95 }
96 (Bound::Included(lid), Bound::Included(rid)) => {
97 Some(Cond(Value::Expression(Box::new(Expression::new(
98 Value::Expression(Box::new(Expression::new(
99 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
100 Operator::MoreThanOrEqual,
101 Thing::from((self.tb.clone(), lid)).into(),
102 ))),
103 Operator::And,
104 Value::Expression(Box::new(Expression::new(
105 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
106 Operator::LessThanOrEqual,
107 Thing::from((self.tb, rid)).into(),
108 ))),
109 )))))
110 }
111 (Bound::Included(lid), Bound::Excluded(rid)) => {
112 Some(Cond(Value::Expression(Box::new(Expression::new(
113 Value::Expression(Box::new(Expression::new(
114 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
115 Operator::MoreThanOrEqual,
116 Thing::from((self.tb.clone(), lid)).into(),
117 ))),
118 Operator::And,
119 Value::Expression(Box::new(Expression::new(
120 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
121 Operator::LessThan,
122 Thing::from((self.tb, rid)).into(),
123 ))),
124 )))))
125 }
126 (Bound::Excluded(lid), Bound::Included(rid)) => {
127 Some(Cond(Value::Expression(Box::new(Expression::new(
128 Value::Expression(Box::new(Expression::new(
129 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
130 Operator::MoreThan,
131 Thing::from((self.tb.clone(), lid)).into(),
132 ))),
133 Operator::And,
134 Value::Expression(Box::new(Expression::new(
135 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
136 Operator::LessThanOrEqual,
137 Thing::from((self.tb, rid)).into(),
138 ))),
139 )))))
140 }
141 (Bound::Excluded(lid), Bound::Excluded(rid)) => {
142 Some(Cond(Value::Expression(Box::new(Expression::new(
143 Value::Expression(Box::new(Expression::new(
144 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
145 Operator::MoreThan,
146 Thing::from((self.tb.clone(), lid)).into(),
147 ))),
148 Operator::And,
149 Value::Expression(Box::new(Expression::new(
150 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
151 Operator::LessThan,
152 Thing::from((self.tb, rid)).into(),
153 ))),
154 )))))
155 }
156 }
157 }
158
159 pub(crate) async fn compute(
161 &self,
162 stk: &mut Stk,
163 ctx: &Context<'_>,
164 opt: &Options,
165 doc: Option<&CursorDoc<'_>>,
166 ) -> Result<Value, Error> {
167 Ok(Value::Range(Box::new(Range {
168 tb: self.tb.clone(),
169 beg: match &self.beg {
170 Bound::Included(id) => Bound::Included(id.compute(stk, ctx, opt, doc).await?),
171 Bound::Excluded(id) => Bound::Excluded(id.compute(stk, ctx, opt, doc).await?),
172 Bound::Unbounded => Bound::Unbounded,
173 },
174 end: match &self.end {
175 Bound::Included(id) => Bound::Included(id.compute(stk, ctx, opt, doc).await?),
176 Bound::Excluded(id) => Bound::Excluded(id.compute(stk, ctx, opt, doc).await?),
177 Bound::Unbounded => Bound::Unbounded,
178 },
179 })))
180 }
181}
182
183impl PartialOrd for Range {
184 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
185 match self.tb.partial_cmp(&other.tb) {
186 Some(Ordering::Equal) => match &self.beg {
187 Bound::Unbounded => match &other.beg {
188 Bound::Unbounded => Some(Ordering::Equal),
189 _ => Some(Ordering::Less),
190 },
191 Bound::Included(v) => match &other.beg {
192 Bound::Unbounded => Some(Ordering::Greater),
193 Bound::Included(w) => match v.partial_cmp(w) {
194 Some(Ordering::Equal) => match &self.end {
195 Bound::Unbounded => match &other.end {
196 Bound::Unbounded => Some(Ordering::Equal),
197 _ => Some(Ordering::Greater),
198 },
199 Bound::Included(v) => match &other.end {
200 Bound::Unbounded => Some(Ordering::Less),
201 Bound::Included(w) => v.partial_cmp(w),
202 _ => Some(Ordering::Greater),
203 },
204 Bound::Excluded(v) => match &other.end {
205 Bound::Excluded(w) => v.partial_cmp(w),
206 _ => Some(Ordering::Less),
207 },
208 },
209 ordering => ordering,
210 },
211 _ => Some(Ordering::Less),
212 },
213 Bound::Excluded(v) => match &other.beg {
214 Bound::Excluded(w) => match v.partial_cmp(w) {
215 Some(Ordering::Equal) => match &self.end {
216 Bound::Unbounded => match &other.end {
217 Bound::Unbounded => Some(Ordering::Equal),
218 _ => Some(Ordering::Greater),
219 },
220 Bound::Included(v) => match &other.end {
221 Bound::Unbounded => Some(Ordering::Less),
222 Bound::Included(w) => v.partial_cmp(w),
223 _ => Some(Ordering::Greater),
224 },
225 Bound::Excluded(v) => match &other.end {
226 Bound::Excluded(w) => v.partial_cmp(w),
227 _ => Some(Ordering::Less),
228 },
229 },
230 ordering => ordering,
231 },
232 _ => Some(Ordering::Greater),
233 },
234 },
235 ordering => ordering,
236 }
237 }
238}
239
240impl fmt::Display for Range {
241 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242 write!(f, "{}:", self.tb)?;
243 match &self.beg {
244 Bound::Unbounded => write!(f, ""),
245 Bound::Included(id) => write!(f, "{id}"),
246 Bound::Excluded(id) => write!(f, "{id}>"),
247 }?;
248 match &self.end {
249 Bound::Unbounded => write!(f, ".."),
250 Bound::Excluded(id) => write!(f, "..{id}"),
251 Bound::Included(id) => write!(f, "..={id}"),
252 }?;
253 Ok(())
254 }
255}