1use crate::ctx::Context;
2use crate::dbs::{Options, Transaction};
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::syn;
6use crate::{strand::no_nul_bytes, Id, Value};
7use revision::revisioned;
8use serde::{Deserialize, Serialize};
9use std::cmp::Ordering;
10use std::fmt;
11use std::ops::Bound;
12use std::str::FromStr;
13
14pub(crate) const TOKEN: &str = "$surrealdb::private::crate::Range";
15
16#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
17#[serde(rename = "$surrealdb::private::crate::Range")]
18#[revisioned(revision = 1)]
19pub struct Range {
20 #[serde(with = "no_nul_bytes")]
21 pub tb: String,
22 pub beg: Bound<Id>,
23 pub end: Bound<Id>,
24}
25
26impl FromStr for Range {
27 type Err = ();
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 Self::try_from(s)
30 }
31}
32
33impl TryFrom<&str> for Range {
34 type Error = ();
35 fn try_from(v: &str) -> Result<Self, Self::Error> {
36 match syn::range(v) {
37 Ok(v) => Ok(v),
38 _ => Err(()),
39 }
40 }
41}
42
43impl Range {
44 pub(crate) async fn compute(
46 &self,
47 ctx: &Context<'_>,
48 opt: &Options,
49 txn: &Transaction,
50 doc: Option<&CursorDoc<'_>>,
51 ) -> Result<Value, Error> {
52 Ok(Value::Range(Box::new(Range {
53 tb: self.tb.clone(),
54 beg: match &self.beg {
55 Bound::Included(id) => Bound::Included(id.compute(ctx, opt, txn, doc).await?),
56 Bound::Excluded(id) => Bound::Excluded(id.compute(ctx, opt, txn, doc).await?),
57 Bound::Unbounded => Bound::Unbounded,
58 },
59 end: match &self.end {
60 Bound::Included(id) => Bound::Included(id.compute(ctx, opt, txn, doc).await?),
61 Bound::Excluded(id) => Bound::Excluded(id.compute(ctx, opt, txn, doc).await?),
62 Bound::Unbounded => Bound::Unbounded,
63 },
64 })))
65 }
66}
67
68impl PartialOrd for Range {
69 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
70 match self.tb.partial_cmp(&other.tb) {
71 Some(Ordering::Equal) => match &self.beg {
72 Bound::Unbounded => match &other.beg {
73 Bound::Unbounded => Some(Ordering::Equal),
74 _ => Some(Ordering::Less),
75 },
76 Bound::Included(v) => match &other.beg {
77 Bound::Unbounded => Some(Ordering::Greater),
78 Bound::Included(w) => match v.partial_cmp(w) {
79 Some(Ordering::Equal) => match &self.end {
80 Bound::Unbounded => match &other.end {
81 Bound::Unbounded => Some(Ordering::Equal),
82 _ => Some(Ordering::Greater),
83 },
84 Bound::Included(v) => match &other.end {
85 Bound::Unbounded => Some(Ordering::Less),
86 Bound::Included(w) => v.partial_cmp(w),
87 _ => Some(Ordering::Greater),
88 },
89 Bound::Excluded(v) => match &other.end {
90 Bound::Excluded(w) => v.partial_cmp(w),
91 _ => Some(Ordering::Less),
92 },
93 },
94 ordering => ordering,
95 },
96 _ => Some(Ordering::Less),
97 },
98 Bound::Excluded(v) => match &other.beg {
99 Bound::Excluded(w) => match v.partial_cmp(w) {
100 Some(Ordering::Equal) => match &self.end {
101 Bound::Unbounded => match &other.end {
102 Bound::Unbounded => Some(Ordering::Equal),
103 _ => Some(Ordering::Greater),
104 },
105 Bound::Included(v) => match &other.end {
106 Bound::Unbounded => Some(Ordering::Less),
107 Bound::Included(w) => v.partial_cmp(w),
108 _ => Some(Ordering::Greater),
109 },
110 Bound::Excluded(v) => match &other.end {
111 Bound::Excluded(w) => v.partial_cmp(w),
112 _ => Some(Ordering::Less),
113 },
114 },
115 ordering => ordering,
116 },
117 _ => Some(Ordering::Greater),
118 },
119 },
120 ordering => ordering,
121 }
122 }
123}
124
125impl fmt::Display for Range {
126 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 write!(f, "{}:", self.tb)?;
128 match &self.beg {
129 Bound::Unbounded => write!(f, ""),
130 Bound::Included(id) => write!(f, "{id}"),
131 Bound::Excluded(id) => write!(f, "{id}>"),
132 }?;
133 match &self.end {
134 Bound::Unbounded => write!(f, ".."),
135 Bound::Excluded(id) => write!(f, "..{id}"),
136 Bound::Included(id) => write!(f, "..={id}"),
137 }?;
138 Ok(())
139 }
140}