surrealdb/api/opt/
resource.rs

1use crate::api::{err::Error, Result};
2use crate::sql::{self, Array, Edges, Id, Object, Table, Thing, Value};
3use crate::syn;
4use std::ops::{self, Bound};
5
6/// A database resource
7#[derive(Debug)]
8pub enum Resource {
9	/// Table name
10	Table(Table),
11	/// Record ID
12	RecordId(Thing),
13	/// An object
14	Object(Object),
15	/// An array
16	Array(Array),
17	/// Edges
18	Edges(Edges),
19}
20
21impl Resource {
22	pub(crate) fn with_range(self, range: Range<Id>) -> Result<sql::Range> {
23		match self {
24			Resource::Table(Table(table)) => Ok(sql::Range {
25				tb: table,
26				beg: range.start,
27				end: range.end,
28			}),
29			Resource::RecordId(record_id) => Err(Error::RangeOnRecordId(record_id).into()),
30			Resource::Object(object) => Err(Error::RangeOnObject(object).into()),
31			Resource::Array(array) => Err(Error::RangeOnArray(array).into()),
32			Resource::Edges(edges) => Err(Error::RangeOnEdges(edges).into()),
33		}
34	}
35}
36
37impl From<Table> for Resource {
38	fn from(table: Table) -> Self {
39		Self::Table(table)
40	}
41}
42
43impl From<&Table> for Resource {
44	fn from(table: &Table) -> Self {
45		Self::Table(table.clone())
46	}
47}
48
49impl From<Thing> for Resource {
50	fn from(thing: Thing) -> Self {
51		Self::RecordId(thing)
52	}
53}
54
55impl From<&Thing> for Resource {
56	fn from(thing: &Thing) -> Self {
57		Self::RecordId(thing.clone())
58	}
59}
60
61impl From<Object> for Resource {
62	fn from(object: Object) -> Self {
63		Self::Object(object)
64	}
65}
66
67impl From<&Object> for Resource {
68	fn from(object: &Object) -> Self {
69		Self::Object(object.clone())
70	}
71}
72
73impl From<Array> for Resource {
74	fn from(array: Array) -> Self {
75		Self::Array(array)
76	}
77}
78
79impl From<&Array> for Resource {
80	fn from(array: &Array) -> Self {
81		Self::Array(array.clone())
82	}
83}
84
85impl From<Edges> for Resource {
86	fn from(edges: Edges) -> Self {
87		Self::Edges(edges)
88	}
89}
90
91impl From<&Edges> for Resource {
92	fn from(edges: &Edges) -> Self {
93		Self::Edges(edges.clone())
94	}
95}
96
97impl From<&str> for Resource {
98	fn from(s: &str) -> Self {
99		match syn::thing(s) {
100			Ok(thing) => Self::RecordId(thing),
101			Err(_) => Self::Table(s.into()),
102		}
103	}
104}
105
106impl From<&String> for Resource {
107	fn from(s: &String) -> Self {
108		Self::from(s.as_str())
109	}
110}
111
112impl From<String> for Resource {
113	fn from(s: String) -> Self {
114		match syn::thing(s.as_str()) {
115			Ok(thing) => Self::RecordId(thing),
116			Err(_) => Self::Table(s.into()),
117		}
118	}
119}
120
121impl<T, I> From<(T, I)> for Resource
122where
123	T: Into<String>,
124	I: Into<Id>,
125{
126	fn from((table, id): (T, I)) -> Self {
127		let record_id = (table.into(), id.into());
128		Self::RecordId(record_id.into())
129	}
130}
131
132impl From<Resource> for Value {
133	fn from(resource: Resource) -> Self {
134		match resource {
135			Resource::Table(resource) => resource.into(),
136			Resource::RecordId(resource) => resource.into(),
137			Resource::Object(resource) => resource.into(),
138			Resource::Array(resource) => resource.into(),
139			Resource::Edges(resource) => resource.into(),
140		}
141	}
142}
143
144/// A trait for converting inputs into database resources
145pub trait IntoResource<Response>: Sized {
146	/// Converts an input into a database resource
147	fn into_resource(self) -> Result<Resource>;
148}
149
150impl IntoResource<Value> for Resource {
151	fn into_resource(self) -> Result<Resource> {
152		Ok(self)
153	}
154}
155
156impl<R> IntoResource<Option<R>> for Object {
157	fn into_resource(self) -> Result<Resource> {
158		Ok(Resource::Object(self))
159	}
160}
161
162impl<R> IntoResource<Option<R>> for Thing {
163	fn into_resource(self) -> Result<Resource> {
164		Ok(Resource::RecordId(self))
165	}
166}
167
168impl<R> IntoResource<Option<R>> for &Thing {
169	fn into_resource(self) -> Result<Resource> {
170		Ok(Resource::RecordId(self.clone()))
171	}
172}
173
174impl<R, T, I> IntoResource<Option<R>> for (T, I)
175where
176	T: Into<String>,
177	I: Into<Id>,
178{
179	fn into_resource(self) -> Result<Resource> {
180		let (table, id) = self;
181		let record_id = (table.into(), id.into());
182		Ok(Resource::RecordId(record_id.into()))
183	}
184}
185
186impl<R> IntoResource<Vec<R>> for Array {
187	fn into_resource(self) -> Result<Resource> {
188		Ok(Resource::Array(self))
189	}
190}
191
192impl<R> IntoResource<Vec<R>> for Edges {
193	fn into_resource(self) -> Result<Resource> {
194		Ok(Resource::Edges(self))
195	}
196}
197
198impl<R> IntoResource<Vec<R>> for Table {
199	fn into_resource(self) -> Result<Resource> {
200		Ok(Resource::Table(self))
201	}
202}
203
204fn blacklist_colon(input: &str) -> Result<()> {
205	match input.contains(':') {
206		true => {
207			// We already know this string contains a colon
208			let (table, id) = input.split_once(':').unwrap();
209			Err(Error::TableColonId {
210				table: table.to_owned(),
211				id: id.to_owned(),
212			}
213			.into())
214		}
215		false => Ok(()),
216	}
217}
218
219impl<R> IntoResource<Vec<R>> for &str {
220	fn into_resource(self) -> Result<Resource> {
221		blacklist_colon(self)?;
222		Ok(Resource::Table(Table(self.to_owned())))
223	}
224}
225
226impl<R> IntoResource<Vec<R>> for &String {
227	fn into_resource(self) -> Result<Resource> {
228		blacklist_colon(self)?;
229		Ok(Resource::Table(Table(self.to_owned())))
230	}
231}
232
233impl<R> IntoResource<Vec<R>> for String {
234	fn into_resource(self) -> Result<Resource> {
235		blacklist_colon(&self)?;
236		Ok(Resource::Table(Table(self)))
237	}
238}
239
240/// Holds the `start` and `end` bounds of a range query
241#[derive(Debug)]
242pub struct Range<T> {
243	pub(crate) start: Bound<T>,
244	pub(crate) end: Bound<T>,
245}
246
247impl<T> From<(Bound<T>, Bound<T>)> for Range<Id>
248where
249	T: Into<Id>,
250{
251	fn from((start, end): (Bound<T>, Bound<T>)) -> Self {
252		Self {
253			start: match start {
254				Bound::Included(idx) => Bound::Included(idx.into()),
255				Bound::Excluded(idx) => Bound::Excluded(idx.into()),
256				Bound::Unbounded => Bound::Unbounded,
257			},
258			end: match end {
259				Bound::Included(idx) => Bound::Included(idx.into()),
260				Bound::Excluded(idx) => Bound::Excluded(idx.into()),
261				Bound::Unbounded => Bound::Unbounded,
262			},
263		}
264	}
265}
266
267impl<T> From<ops::Range<T>> for Range<Id>
268where
269	T: Into<Id>,
270{
271	fn from(
272		ops::Range {
273			start,
274			end,
275		}: ops::Range<T>,
276	) -> Self {
277		Self {
278			start: Bound::Included(start.into()),
279			end: Bound::Excluded(end.into()),
280		}
281	}
282}
283
284impl<T> From<ops::RangeInclusive<T>> for Range<Id>
285where
286	T: Into<Id>,
287{
288	fn from(range: ops::RangeInclusive<T>) -> Self {
289		let (start, end) = range.into_inner();
290		Self {
291			start: Bound::Included(start.into()),
292			end: Bound::Included(end.into()),
293		}
294	}
295}
296
297impl<T> From<ops::RangeFrom<T>> for Range<Id>
298where
299	T: Into<Id>,
300{
301	fn from(
302		ops::RangeFrom {
303			start,
304		}: ops::RangeFrom<T>,
305	) -> Self {
306		Self {
307			start: Bound::Included(start.into()),
308			end: Bound::Unbounded,
309		}
310	}
311}
312
313impl<T> From<ops::RangeTo<T>> for Range<Id>
314where
315	T: Into<Id>,
316{
317	fn from(
318		ops::RangeTo {
319			end,
320		}: ops::RangeTo<T>,
321	) -> Self {
322		Self {
323			start: Bound::Unbounded,
324			end: Bound::Excluded(end.into()),
325		}
326	}
327}
328
329impl<T> From<ops::RangeToInclusive<T>> for Range<Id>
330where
331	T: Into<Id>,
332{
333	fn from(
334		ops::RangeToInclusive {
335			end,
336		}: ops::RangeToInclusive<T>,
337	) -> Self {
338		Self {
339			start: Bound::Unbounded,
340			end: Bound::Included(end.into()),
341		}
342	}
343}
344
345impl From<ops::RangeFull> for Range<Id> {
346	fn from(_: ops::RangeFull) -> Self {
347		Self {
348			start: Bound::Unbounded,
349			end: Bound::Unbounded,
350		}
351	}
352}