1use super::id::range::IdRange;
2use super::{Cond, Expression, Ident, Idiom, Operator, Part, Table};
3use crate::ctx::Context;
4use crate::dbs::Options;
5use crate::doc::CursorDoc;
6use crate::err::Error;
7use crate::key::r#ref::Ref;
8use crate::kvs::KeyDecode as _;
9use crate::sql::{escape::escape_rid, id::Id, Strand, Value};
10use crate::syn;
11use futures::StreamExt;
12use reblessive::tree::Stk;
13use revision::revisioned;
14use serde::{Deserialize, Serialize};
15use std::fmt;
16use std::ops::Bound;
17use std::str::FromStr;
18
19const ID: &str = "id";
20pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Thing";
21
22#[revisioned(revision = 1)]
23#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
24#[serde(rename = "$surrealdb::private::sql::Thing")]
25#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26#[non_exhaustive]
27pub struct Thing {
28 pub tb: String,
30 pub id: Id,
31}
32
33impl Thing {
34 pub fn to_cond(self) -> Option<Cond> {
36 match &self.id {
37 Id::Range(r) => match (&r.beg, &r.end) {
38 (Bound::Unbounded, Bound::Unbounded) => None,
39 (Bound::Unbounded, Bound::Excluded(id)) => {
40 Some(Cond(Value::Expression(Box::new(Expression::new(
41 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
42 Operator::LessThan,
43 Thing::from((self.tb, id.to_owned())).into(),
44 )))))
45 }
46 (Bound::Unbounded, Bound::Included(id)) => {
47 Some(Cond(Value::Expression(Box::new(Expression::new(
48 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
49 Operator::LessThanOrEqual,
50 Thing::from((self.tb, id.to_owned())).into(),
51 )))))
52 }
53 (Bound::Excluded(id), Bound::Unbounded) => {
54 Some(Cond(Value::Expression(Box::new(Expression::new(
55 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
56 Operator::MoreThan,
57 Thing::from((self.tb, id.to_owned())).into(),
58 )))))
59 }
60 (Bound::Included(id), Bound::Unbounded) => {
61 Some(Cond(Value::Expression(Box::new(Expression::new(
62 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
63 Operator::MoreThanOrEqual,
64 Thing::from((self.tb, id.to_owned())).into(),
65 )))))
66 }
67 (Bound::Included(lid), Bound::Included(rid)) => {
68 Some(Cond(Value::Expression(Box::new(Expression::new(
69 Value::Expression(Box::new(Expression::new(
70 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
71 Operator::MoreThanOrEqual,
72 Thing::from((self.tb.clone(), lid.to_owned())).into(),
73 ))),
74 Operator::And,
75 Value::Expression(Box::new(Expression::new(
76 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
77 Operator::LessThanOrEqual,
78 Thing::from((self.tb, rid.to_owned())).into(),
79 ))),
80 )))))
81 }
82 (Bound::Included(lid), Bound::Excluded(rid)) => {
83 Some(Cond(Value::Expression(Box::new(Expression::new(
84 Value::Expression(Box::new(Expression::new(
85 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
86 Operator::MoreThanOrEqual,
87 Thing::from((self.tb.clone(), lid.to_owned())).into(),
88 ))),
89 Operator::And,
90 Value::Expression(Box::new(Expression::new(
91 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
92 Operator::LessThan,
93 Thing::from((self.tb, rid.to_owned())).into(),
94 ))),
95 )))))
96 }
97 (Bound::Excluded(lid), Bound::Included(rid)) => {
98 Some(Cond(Value::Expression(Box::new(Expression::new(
99 Value::Expression(Box::new(Expression::new(
100 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
101 Operator::MoreThan,
102 Thing::from((self.tb.clone(), lid.to_owned())).into(),
103 ))),
104 Operator::And,
105 Value::Expression(Box::new(Expression::new(
106 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
107 Operator::LessThanOrEqual,
108 Thing::from((self.tb, rid.to_owned())).into(),
109 ))),
110 )))))
111 }
112 (Bound::Excluded(lid), Bound::Excluded(rid)) => {
113 Some(Cond(Value::Expression(Box::new(Expression::new(
114 Value::Expression(Box::new(Expression::new(
115 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
116 Operator::MoreThan,
117 Thing::from((self.tb.clone(), lid.to_owned())).into(),
118 ))),
119 Operator::And,
120 Value::Expression(Box::new(Expression::new(
121 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
122 Operator::LessThan,
123 Thing::from((self.tb, rid.to_owned())).into(),
124 ))),
125 )))))
126 }
127 },
128 _ => Some(Cond(Value::Expression(Box::new(Expression::new(
129 Idiom(vec![Part::from(Ident(ID.to_owned()))]).into(),
130 Operator::Equal,
131 Thing::from((self.tb, self.id)).into(),
132 ))))),
133 }
134 }
135
136 pub(crate) async fn refs(
137 &self,
138 ctx: &Context,
139 opt: &Options,
140 ft: Option<&Table>,
141 ff: Option<&Idiom>,
142 ) -> Result<Vec<Thing>, Error> {
143 let (ns, db) = opt.ns_db()?;
144
145 let (prefix, suffix) = match (ft, ff) {
146 (Some(ft), Some(ff)) => {
147 let ff = ff.to_string();
148
149 (
150 crate::key::r#ref::ffprefix(ns, db, &self.tb, &self.id, ft, &ff),
151 crate::key::r#ref::ffsuffix(ns, db, &self.tb, &self.id, ft, &ff),
152 )
153 }
154 (Some(ft), None) => (
155 crate::key::r#ref::ftprefix(ns, db, &self.tb, &self.id, ft),
156 crate::key::r#ref::ftsuffix(ns, db, &self.tb, &self.id, ft),
157 ),
158 (None, None) => (
159 crate::key::r#ref::prefix(ns, db, &self.tb, &self.id),
160 crate::key::r#ref::suffix(ns, db, &self.tb, &self.id),
161 ),
162 (None, Some(_)) => {
163 return Err(Error::Unreachable(
164 "A foreign field was passed without a foreign table".into(),
165 ))
166 }
167 };
168
169 let range = prefix?..suffix?;
170 let txn = ctx.tx();
171 let mut stream = txn.stream_keys(range, None);
172
173 let mut keys: Vec<Vec<u8>> = vec![];
175 while let Some(res) = stream.next().await {
176 keys.push(res?);
177 }
178
179 let mut ids = Vec::new();
180 for x in keys.iter() {
181 let key = Ref::decode(x)?;
182 ids.push(Thing {
183 tb: key.ft.to_string(),
184 id: key.fk,
185 })
186 }
187
188 Ok(ids)
189 }
190}
191
192impl From<(&str, Id)> for Thing {
193 fn from((tb, id): (&str, Id)) -> Self {
194 Self {
195 tb: tb.to_owned(),
196 id,
197 }
198 }
199}
200
201impl From<(String, Id)> for Thing {
202 fn from((tb, id): (String, Id)) -> Self {
203 Self {
204 tb,
205 id,
206 }
207 }
208}
209
210impl From<(&str, IdRange)> for Thing {
211 fn from((tb, id): (&str, IdRange)) -> Self {
212 Self {
213 tb: tb.to_owned(),
214 id: id.into(),
215 }
216 }
217}
218
219impl From<(String, IdRange)> for Thing {
220 fn from((tb, id): (String, IdRange)) -> Self {
221 Self {
222 tb,
223 id: id.into(),
224 }
225 }
226}
227
228impl From<(String, String)> for Thing {
229 fn from((tb, id): (String, String)) -> Self {
230 Self::from((tb, Id::from(id)))
231 }
232}
233
234impl From<(&str, &str)> for Thing {
235 fn from((tb, id): (&str, &str)) -> Self {
236 Self::from((tb.to_owned(), Id::from(id)))
237 }
238}
239
240impl FromStr for Thing {
241 type Err = ();
242 fn from_str(s: &str) -> Result<Self, Self::Err> {
243 Self::try_from(s)
244 }
245}
246
247impl TryFrom<String> for Thing {
248 type Error = ();
249 fn try_from(v: String) -> Result<Self, Self::Error> {
250 Self::try_from(v.as_str())
251 }
252}
253
254impl TryFrom<Strand> for Thing {
255 type Error = ();
256 fn try_from(v: Strand) -> Result<Self, Self::Error> {
257 Self::try_from(v.as_str())
258 }
259}
260
261impl TryFrom<&str> for Thing {
262 type Error = ();
263 fn try_from(v: &str) -> Result<Self, Self::Error> {
264 match syn::thing_with_range(v) {
265 Ok(v) => Ok(v),
266 _ => Err(()),
267 }
268 }
269}
270
271impl Thing {
272 pub fn to_raw(&self) -> String {
274 self.to_string()
275 }
276 pub fn is_range(&self) -> bool {
278 matches!(self.id, Id::Range(_))
279 }
280 pub fn is_record_type(&self, types: &[Table]) -> bool {
282 types.is_empty() || types.iter().any(|tb| tb.0 == self.tb)
283 }
284}
285
286impl fmt::Display for Thing {
287 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288 write!(f, "{}:{}", escape_rid(&self.tb), self.id)
289 }
290}
291
292impl Thing {
293 pub(crate) async fn compute(
295 &self,
296 stk: &mut Stk,
297 ctx: &Context,
298 opt: &Options,
299 doc: Option<&CursorDoc>,
300 ) -> Result<Value, Error> {
301 Ok(Value::Thing(Thing {
302 tb: self.tb.clone(),
303 id: self.id.compute(stk, ctx, opt, doc).await?,
304 }))
305 }
306}
307
308#[cfg(test)]
309mod test {
310 use std::{ops::Bound, str::FromStr};
311
312 use crate::sql::{Array, Id, IdRange, Object, Value};
313
314 use super::Thing;
315
316 #[test]
317 fn from() {
318 {
319 let string = "foo:bar";
320 let thing = Thing {
321 tb: "foo".into(),
322 id: Id::String("bar".into()),
323 };
324 assert_eq!(thing, Thing::from_str(string).unwrap());
325 }
326 {
327 let string = "foo:1";
328 let thing = Thing {
329 tb: "foo".into(),
330 id: Id::Number(1),
331 };
332 assert_eq!(thing, Thing::from_str(string).unwrap());
333 }
334 {
335 let string = "foo:[1, 'bar']";
336 let thing = Thing {
337 tb: "foo".into(),
338 id: Id::Array(Array(vec![1i64.into(), "bar".into()])),
339 };
340 assert_eq!(thing, Thing::from_str(string).unwrap());
341 }
342 {
343 let string = "foo:{bar: 1}";
344 let thing = Thing {
345 tb: "foo".into(),
346 id: Id::Object(Object([("bar".to_string(), Value::from(1))].into_iter().collect())),
347 };
348 assert_eq!(thing, Thing::from_str(string).unwrap());
349 }
350 {
351 let string = "foo:1..=2";
352 let thing = Thing {
353 tb: "foo".into(),
354 id: Id::Range(Box::new(
355 IdRange::try_from((
356 Bound::Included(Id::Number(1)),
357 Bound::Included(Id::Number(2)),
358 ))
359 .unwrap(),
360 )),
361 };
362 assert_eq!(thing, Thing::from_str(string).unwrap());
363 }
364 }
365}