Skip to main content

surrealdb/api/value/
mod.rs

1use std::cmp::{Ordering, PartialEq, PartialOrd};
2use std::fmt;
3use std::ops::{Deref, Index};
4use std::str::FromStr;
5
6use chrono::{DateTime, Utc};
7use revision::revisioned;
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10use uuid::Uuid;
11
12use crate::Result;
13use crate::core::dbs::Action as CoreAction;
14use crate::core::{syn, val};
15use crate::error::Api as ApiError;
16
17mod convert;
18pub(crate) use convert::{from_value as from_core_value, to_value as to_core_value};
19mod obj;
20pub use obj::{IntoIter, Iter, IterMut, Object};
21
22pub fn from_value<T: DeserializeOwned>(value: Value) -> Result<T> {
23	convert::from_value(value.0)
24}
25
26pub fn to_value<T: Serialize + 'static>(value: T) -> Result<Value> {
27	convert::to_value(value).map(Value)
28}
29
30// Keeping bytes implementation minimal since it might be a good idea to use
31// bytes crate here instead of a plain Vec<u8>.
32#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
33#[revisioned(revision = 1)]
34pub struct Bytes(Vec<u8>);
35
36impl Bytes {
37	pub fn copy_from_slice(slice: &[u8]) -> Self {
38		slice.to_vec().into()
39	}
40
41	pub fn len(&self) -> usize {
42		self.0.len()
43	}
44
45	pub fn is_empty(&self) -> bool {
46		self.0.is_empty()
47	}
48}
49
50impl PartialEq<[u8]> for Bytes {
51	fn eq(&self, other: &[u8]) -> bool {
52		self.0 == other
53	}
54}
55
56impl PartialOrd<[u8]> for Bytes {
57	fn partial_cmp(&self, other: &[u8]) -> Option<Ordering> {
58		self.0.as_slice().partial_cmp(other)
59	}
60}
61
62impl Deref for Bytes {
63	type Target = [u8];
64
65	fn deref(&self) -> &Self::Target {
66		self.0.as_slice()
67	}
68}
69
70impl From<Vec<u8>> for Bytes {
71	fn from(value: Vec<u8>) -> Self {
72		Bytes(value)
73	}
74}
75
76transparent_wrapper!(
77	#[derive(Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
78	pub struct Datetime(val::Datetime)
79);
80impl_serialize_wrapper!(Datetime);
81
82impl From<DateTime<Utc>> for Datetime {
83	fn from(v: DateTime<Utc>) -> Self {
84		Self(v.into())
85	}
86}
87
88transparent_wrapper!(
89	/// The key of a [`RecordId`].
90	#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
91	#[non_exhaustive]
92	pub struct RecordIdKey(val::RecordIdKey)
93);
94impl_serialize_wrapper!(RecordIdKey);
95
96impl From<Object> for RecordIdKey {
97	fn from(value: Object) -> Self {
98		Self::from_inner(val::RecordIdKey::Object(value.into_inner()))
99	}
100}
101
102impl TryFrom<RecordIdKey> for Object {
103	type Error = anyhow::Error;
104
105	fn try_from(value: RecordIdKey) -> Result<Self> {
106		if let val::RecordIdKey::Object(x) = value.0 {
107			Ok(Object::from_inner(x))
108		} else {
109			Err(anyhow::Error::new(ApiError::FromValue {
110				value: value.into(),
111				error: String::from("inner value is not an object"),
112			}))
113		}
114	}
115}
116
117//TODO: Null byte validity
118impl From<String> for RecordIdKey {
119	fn from(value: String) -> Self {
120		Self(val::RecordIdKey::String(value))
121	}
122}
123
124impl TryFrom<RecordIdKey> for String {
125	type Error = anyhow::Error;
126
127	fn try_from(value: RecordIdKey) -> Result<Self> {
128		if let val::RecordIdKey::String(x) = value.0 {
129			Ok(x)
130		} else {
131			Err(anyhow::Error::new(ApiError::FromValue {
132				value: value.into(),
133				error: String::from("inner value is not a string"),
134			}))
135		}
136	}
137}
138
139impl From<&String> for RecordIdKey {
140	fn from(value: &String) -> Self {
141		Self(val::RecordIdKey::String(value.clone()))
142	}
143}
144
145impl From<&str> for RecordIdKey {
146	fn from(value: &str) -> Self {
147		Self(val::RecordIdKey::String(value.to_owned()))
148	}
149}
150
151impl From<i64> for RecordIdKey {
152	fn from(value: i64) -> Self {
153		Self(val::RecordIdKey::Number(value))
154	}
155}
156
157impl TryFrom<RecordIdKey> for i64 {
158	type Error = anyhow::Error;
159
160	fn try_from(value: RecordIdKey) -> Result<Self> {
161		if let val::RecordIdKey::Number(x) = value.0 {
162			Ok(x)
163		} else {
164			Err(anyhow::Error::new(ApiError::FromValue {
165				value: value.into(),
166				error: String::from("inner value is not a number"),
167			}))
168		}
169	}
170}
171
172impl From<Uuid> for RecordIdKey {
173	fn from(value: Uuid) -> Self {
174		Self(val::RecordIdKey::Uuid(value.into()))
175	}
176}
177
178impl TryFrom<RecordIdKey> for Uuid {
179	type Error = anyhow::Error;
180
181	fn try_from(value: RecordIdKey) -> Result<Self> {
182		if let val::RecordIdKey::Uuid(x) = value.0 {
183			Ok(*x)
184		} else {
185			Err(anyhow::Error::new(ApiError::FromValue {
186				value: value.into(),
187				error: String::from("inner value is not a UUID"),
188			}))
189		}
190	}
191}
192
193impl From<Vec<Value>> for RecordIdKey {
194	fn from(value: Vec<Value>) -> Self {
195		let res = Value::array_to_core(value);
196		Self(val::RecordIdKey::Array(val::Array(res)))
197	}
198}
199
200#[expect(clippy::fallible_impl_from)]
201impl From<RecordIdKey> for Value {
202	fn from(key: RecordIdKey) -> Self {
203		match key.0 {
204			val::RecordIdKey::String(x) => Value::from_inner(val::Value::from(x)),
205			val::RecordIdKey::Number(x) => Value::from_inner(val::Value::from(x)),
206			val::RecordIdKey::Object(x) => Value::from_inner(val::Value::from(x)),
207			val::RecordIdKey::Array(x) => Value::from_inner(val::Value::from(x)),
208			val::RecordIdKey::Uuid(x) => Value::from_inner(val::Value::from(x)),
209			_ => panic!("lib recieved generate variant of record id"),
210		}
211	}
212}
213
214impl From<RecordId> for Value {
215	fn from(key: RecordId) -> Self {
216		Value::from_inner(val::Value::RecordId(key.0))
217	}
218}
219
220impl FromStr for Value {
221	type Err = anyhow::Error;
222
223	fn from_str(s: &str) -> Result<Self> {
224		Ok(Value::from_inner(crate::core::syn::value(s)?))
225	}
226}
227
228#[derive(Debug)]
229pub struct RecordIdKeyFromValueError(());
230
231impl fmt::Display for RecordIdKeyFromValueError {
232	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233		writeln!(
234			f,
235			"tried to convert a value to a record id key with a value type that is not allowed in a record id key"
236		)
237	}
238}
239
240impl TryFrom<Value> for RecordIdKey {
241	type Error = RecordIdKeyFromValueError;
242
243	fn try_from(key: Value) -> std::result::Result<Self, Self::Error> {
244		match key.0 {
245			val::Value::Strand(x) => {
246				Ok(RecordIdKey::from_inner(val::RecordIdKey::String(x.into_string())))
247			}
248			val::Value::Number(val::Number::Int(x)) => {
249				Ok(RecordIdKey::from_inner(val::RecordIdKey::Number(x)))
250			}
251			val::Value::Object(x) => Ok(RecordIdKey::from_inner(val::RecordIdKey::Object(x))),
252			val::Value::Array(x) => Ok(RecordIdKey::from_inner(val::RecordIdKey::Array(x))),
253			_ => Err(RecordIdKeyFromValueError(())),
254		}
255	}
256}
257
258transparent_wrapper!(
259	/// Struct representing a record id.
260	///
261	/// Record id's consist of a table name and a key.
262	/// For example the record id `user:tkwse1j5o0anqjxonvzx` has the table `user` and the key `tkwse1j5o0anqjxonvzx`.
263	#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
264	pub struct RecordId(val::RecordId)
265);
266impl_serialize_wrapper!(RecordId);
267
268impl RecordId {
269	pub fn from_table_key<S, K>(table: S, key: K) -> Self
270	where
271		S: Into<String>,
272		K: Into<RecordIdKey>,
273	{
274		let tb = table.into();
275		let key = key.into();
276		Self(val::RecordId::new(tb, key.0))
277	}
278
279	pub fn table(&self) -> &str {
280		&self.0.table
281	}
282
283	pub fn key(&self) -> &RecordIdKey {
284		RecordIdKey::from_inner_ref(&self.0.key)
285	}
286}
287
288impl FromStr for RecordId {
289	type Err = anyhow::Error;
290
291	fn from_str(s: &str) -> Result<Self> {
292		syn::record_id(s).map(RecordId::from_inner)
293	}
294}
295
296impl<S, I> From<(S, I)> for RecordId
297where
298	S: Into<String>,
299	RecordIdKey: From<I>,
300{
301	fn from(value: (S, I)) -> Self {
302		Self::from_table_key(value.0, value.1)
303	}
304}
305
306transparent_wrapper!(
307	/// The number type of surrealql.
308	/// Can contain either a 64 bit float, 64 bit integer or a decimal.
309	#[derive( Clone, PartialEq, PartialOrd)]
310	pub struct Number(val::Number)
311);
312impl_serialize_wrapper!(Number);
313
314transparent_wrapper!(
315	#[derive(Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
316	pub struct Value(pub(crate) val::Value)
317);
318impl_serialize_wrapper!(Value);
319
320impl Value {
321	#[expect(dead_code)]
322	pub(crate) fn core_to_array(v: Vec<val::Value>) -> Vec<Value> {
323		unsafe {
324			// SAFETY: Because Value is `repr(transparent)` transmuting between value and
325			// corevalue is safe.
326			std::mem::transmute::<Vec<val::Value>, Vec<Value>>(v)
327		}
328	}
329
330	#[expect(dead_code)]
331	pub(crate) fn core_to_array_ref(v: &Vec<val::Value>) -> &Vec<Value> {
332		unsafe {
333			// SAFETY: Because Value is `repr(transparent)` transmuting between value and
334			// corevalue is safe.
335			std::mem::transmute::<&Vec<val::Value>, &Vec<Value>>(v)
336		}
337	}
338
339	#[expect(dead_code)]
340	pub(crate) fn core_to_array_mut(v: &mut Vec<val::Value>) -> &mut Vec<Value> {
341		unsafe {
342			// SAFETY: Because Value is `repr(transparent)` transmuting between value and
343			// corevalue is safe.
344			std::mem::transmute::<&mut Vec<val::Value>, &mut Vec<Value>>(v)
345		}
346	}
347
348	pub(crate) fn array_to_core(v: Vec<Value>) -> Vec<val::Value> {
349		unsafe {
350			// SAFETY: Because Value is `repr(transparent)` transmuting between value and
351			// corevalue is safe.
352			std::mem::transmute::<Vec<Value>, Vec<val::Value>>(v)
353		}
354	}
355
356	#[expect(dead_code)]
357	pub(crate) fn array_to_core_ref(v: &Vec<Value>) -> &Vec<val::Value> {
358		unsafe {
359			// SAFETY: Because Value is `repr(transparent)` transmuting between value and
360			// corevalue is safe.
361			std::mem::transmute::<&Vec<Value>, &Vec<val::Value>>(v)
362		}
363	}
364
365	#[expect(dead_code)]
366	pub(crate) fn array_to_core_mut(v: &mut Vec<Value>) -> &mut Vec<val::Value> {
367		unsafe {
368			// SAFETY: Because Value is `repr(transparent)` transmuting between value and
369			// corevalue is safe.
370			std::mem::transmute::<&mut Vec<Value>, &mut Vec<val::Value>>(v)
371		}
372	}
373}
374
375impl Index<usize> for Value {
376	type Output = Self;
377
378	fn index(&self, index: usize) -> &Self::Output {
379		match &self.0 {
380			val::Value::Array(map) => {
381				map.0.get(index).map(Self::from_inner_ref).unwrap_or(&Value(val::Value::None))
382			}
383			_ => &Value(val::Value::None),
384		}
385	}
386}
387
388impl Index<&str> for Value {
389	type Output = Self;
390
391	fn index(&self, index: &str) -> &Self::Output {
392		match &self.0 {
393			val::Value::Object(map) => {
394				map.0.get(index).map(Self::from_inner_ref).unwrap_or(&Value(val::Value::None))
395			}
396			_ => &Value(val::Value::None),
397		}
398	}
399}
400
401impl Value {
402	/// Accesses the value found at a certain field
403	/// if an object, and a certain index if an array.
404	/// Will not err if no value is found at this point,
405	/// instead returning a Value::None. If an Option<&Value>
406	/// is desired, the .into_option() method can be used
407	/// to perform the conversion.
408	pub fn get<Idx>(&self, index: Idx) -> &Value
409	where
410		Value: Index<Idx, Output = Value>,
411	{
412		self.index(index)
413	}
414
415	/// Converts a Value into an Option<&Value>, returning
416	/// a Some in all cases except Value::None.
417	pub fn into_option(&self) -> Option<&Value> {
418		match self {
419			Value(val::Value::None) => None,
420			v => Some(v),
421		}
422	}
423
424	/// Checks to see if a Value is a Value::None.
425	pub fn is_none(&self) -> bool {
426		matches!(&self, Value(val::Value::None))
427	}
428}
429
430pub struct ConversionError {
431	from: &'static str,
432	expected: &'static str,
433}
434
435impl fmt::Display for ConversionError {
436	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437		writeln!(
438			f,
439			"failed to convert into `{}` from value with type `{:?}`",
440			self.expected, self.from
441		)
442	}
443}
444
445/// The action performed on a record
446///
447/// This is used in live query notifications.
448#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
449#[non_exhaustive]
450pub enum Action {
451	Create,
452	Update,
453	Delete,
454}
455
456impl Action {
457	#[allow(dead_code, reason = "Used by other engines except the HTTP one")]
458	pub(crate) fn from_core(action: CoreAction) -> Self {
459		match action {
460			CoreAction::Create => Self::Create,
461			CoreAction::Update => Self::Update,
462			CoreAction::Delete => Self::Delete,
463			_ => panic!("unimplemented variant of action"),
464		}
465	}
466}
467
468/// A live query notification
469///
470/// Live queries return a stream of notifications. The notification contains an
471/// `action` that triggered the change in the database record and `data` itself.
472/// For deletions the data is the record before it was deleted. For everything
473/// else, it's the newly created record or updated record depending on whether
474/// the action is create or update.
475#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
476#[non_exhaustive]
477pub struct Notification<R> {
478	pub query_id: Uuid,
479	pub action: Action,
480	pub data: R,
481}