1use super::Id;
2use crate::{
3 ctx::Context,
4 dbs::Options,
5 doc::CursorDoc,
6 err::Error,
7 sql::{Range, Value},
8};
9use reblessive::tree::Stk;
10use revision::revisioned;
11use serde::{Deserialize, Serialize};
12use std::{cmp::Ordering, fmt, ops::Bound};
13
14#[revisioned(revision = 1)]
15#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17pub struct IdRange {
18 pub beg: Bound<Id>,
19 pub end: Bound<Id>,
20}
21
22impl TryFrom<(Bound<Id>, Bound<Id>)> for IdRange {
23 type Error = Error;
24 fn try_from((beg, end): (Bound<Id>, Bound<Id>)) -> Result<Self, Self::Error> {
25 if matches!(beg, Bound::Included(Id::Range(_)) | Bound::Excluded(Id::Range(_))) {
26 return Err(Error::IdInvalid {
27 value: "range".into(),
28 });
29 }
30
31 if matches!(end, Bound::Included(Id::Range(_)) | Bound::Excluded(Id::Range(_))) {
32 return Err(Error::IdInvalid {
33 value: "range".into(),
34 });
35 }
36
37 Ok(IdRange {
38 beg,
39 end,
40 })
41 }
42}
43
44impl TryFrom<Range> for IdRange {
45 type Error = Error;
46 fn try_from(v: Range) -> Result<Self, Self::Error> {
47 let beg = match v.beg {
48 Bound::Included(beg) => Bound::Included(Id::try_from(beg)?),
49 Bound::Excluded(beg) => Bound::Excluded(Id::try_from(beg)?),
50 Bound::Unbounded => Bound::Unbounded,
51 };
52
53 let end = match v.end {
54 Bound::Included(end) => Bound::Included(Id::try_from(end)?),
55 Bound::Excluded(end) => Bound::Excluded(Id::try_from(end)?),
56 Bound::Unbounded => Bound::Unbounded,
57 };
58
59 IdRange::try_from((beg, end))
61 }
62}
63
64impl TryFrom<Value> for IdRange {
65 type Error = Error;
66 fn try_from(v: Value) -> Result<Self, Self::Error> {
67 match v {
68 Value::Range(v) => IdRange::try_from(*v),
69 v => Err(Error::IdInvalid {
70 value: v.kindof().to_string(),
71 }),
72 }
73 }
74}
75
76impl PartialOrd for IdRange {
77 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
78 Some(self.cmp(other))
79 }
80}
81
82impl Ord for IdRange {
83 fn cmp(&self, other: &Self) -> Ordering {
84 match &self.beg {
85 Bound::Unbounded => match &other.beg {
86 Bound::Unbounded => Ordering::Equal,
87 _ => Ordering::Less,
88 },
89 Bound::Included(v) => match &other.beg {
90 Bound::Unbounded => Ordering::Greater,
91 Bound::Included(w) => match v.cmp(w) {
92 Ordering::Equal => match &self.end {
93 Bound::Unbounded => match &other.end {
94 Bound::Unbounded => Ordering::Equal,
95 _ => Ordering::Greater,
96 },
97 Bound::Included(v) => match &other.end {
98 Bound::Unbounded => Ordering::Less,
99 Bound::Included(w) => v.cmp(w),
100 _ => Ordering::Greater,
101 },
102 Bound::Excluded(v) => match &other.end {
103 Bound::Excluded(w) => v.cmp(w),
104 _ => Ordering::Less,
105 },
106 },
107 ordering => ordering,
108 },
109 _ => Ordering::Less,
110 },
111 Bound::Excluded(v) => match &other.beg {
112 Bound::Excluded(w) => match v.cmp(w) {
113 Ordering::Equal => match &self.end {
114 Bound::Unbounded => match &other.end {
115 Bound::Unbounded => Ordering::Equal,
116 _ => Ordering::Greater,
117 },
118 Bound::Included(v) => match &other.end {
119 Bound::Unbounded => Ordering::Less,
120 Bound::Included(w) => v.cmp(w),
121 _ => Ordering::Greater,
122 },
123 Bound::Excluded(v) => match &other.end {
124 Bound::Excluded(w) => v.cmp(w),
125 _ => Ordering::Less,
126 },
127 },
128 ordering => ordering,
129 },
130 _ => Ordering::Greater,
131 },
132 }
133 }
134}
135
136impl fmt::Display for IdRange {
137 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138 match &self.beg {
139 Bound::Unbounded => write!(f, ""),
140 Bound::Included(v) => write!(f, "{v}"),
141 Bound::Excluded(v) => write!(f, "{v}>"),
142 }?;
143 match &self.end {
144 Bound::Unbounded => write!(f, ".."),
145 Bound::Excluded(v) => write!(f, "..{v}"),
146 Bound::Included(v) => write!(f, "..={v}"),
147 }?;
148 Ok(())
149 }
150}
151
152impl IdRange {
153 pub(crate) async fn compute(
155 &self,
156 stk: &mut Stk,
157 ctx: &Context,
158 opt: &Options,
159 doc: Option<&CursorDoc>,
160 ) -> Result<IdRange, Error> {
161 let beg = match &self.beg {
162 Bound::Included(beg) => {
163 Bound::Included(stk.run(|stk| beg.compute(stk, ctx, opt, doc)).await?)
164 }
165 Bound::Excluded(beg) => {
166 Bound::Excluded(stk.run(|stk| beg.compute(stk, ctx, opt, doc)).await?)
167 }
168 Bound::Unbounded => Bound::Unbounded,
169 };
170
171 let end = match &self.end {
172 Bound::Included(end) => {
173 Bound::Included(stk.run(|stk| end.compute(stk, ctx, opt, doc)).await?)
174 }
175 Bound::Excluded(end) => {
176 Bound::Excluded(stk.run(|stk| end.compute(stk, ctx, opt, doc)).await?)
177 }
178 Bound::Unbounded => Bound::Unbounded,
179 };
180
181 IdRange::try_from((beg, end))
183 }
184}