surrealdb_core/rpc/format/msgpack/
convert.rs

1use crate::sql::Datetime;
2use crate::sql::Duration;
3use crate::sql::Number;
4use crate::sql::Thing;
5use crate::sql::Uuid;
6use crate::sql::Value;
7use rmpv::Value as Data;
8
9const TAG_NONE: i8 = 1;
10const TAG_UUID: i8 = 2;
11const TAG_DECIMAL: i8 = 3;
12const TAG_DURATION: i8 = 4;
13const TAG_DATETIME: i8 = 5;
14const TAG_RECORDID: i8 = 6;
15
16#[derive(Debug)]
17pub struct Pack(pub Data);
18
19impl TryFrom<Pack> for Value {
20	type Error = &'static str;
21	fn try_from(val: Pack) -> Result<Self, &'static str> {
22		match val.0 {
23			Data::Nil => Ok(Value::Null),
24			Data::Boolean(v) => Ok(Value::from(v)),
25			Data::Integer(v) if v.is_i64() => match v.as_i64() {
26				Some(v) => Ok(Value::from(v)),
27				None => Ok(Value::Null),
28			},
29			Data::Integer(v) if v.is_u64() => match v.as_u64() {
30				Some(v) => Ok(Value::from(v)),
31				None => Ok(Value::Null),
32			},
33			Data::F32(v) => Ok(Value::from(v)),
34			Data::F64(v) => Ok(Value::from(v)),
35			Data::String(v) => match v.into_str() {
36				Some(v) => Ok(Value::from(v)),
37				None => Ok(Value::Null),
38			},
39			Data::Binary(v) => Ok(Value::Bytes(v.into())),
40			Data::Array(v) => {
41				v.into_iter().map(|v| Value::try_from(Pack(v))).collect::<Result<Value, &str>>()
42			}
43			Data::Map(v) => v
44				.into_iter()
45				.map(|(k, v)| {
46					let k = Value::try_from(Pack(k)).map(|k| k.as_raw_string());
47					let v = Value::try_from(Pack(v));
48					Ok((k?, v?))
49				})
50				.collect::<Result<Value, &str>>(),
51			Data::Ext(t, v) => {
52				match t {
53					// A literal uuid
54					TAG_NONE => Ok(Value::None),
55					// A literal uuid
56					TAG_UUID => match std::str::from_utf8(&v) {
57						Ok(v) => match Uuid::try_from(v) {
58							Ok(v) => Ok(v.into()),
59							_ => Err("Expected a valid UUID value"),
60						},
61						_ => Err("Expected a valid UTF-8 string"),
62					},
63					// A literal decimal
64					TAG_DECIMAL => match std::str::from_utf8(&v) {
65						Ok(v) => match Number::try_from(v) {
66							Ok(v) => Ok(v.into()),
67							_ => Err("Expected a valid Decimal value"),
68						},
69						_ => Err("Expected a valid UTF-8 string"),
70					},
71					// A literal duration
72					TAG_DURATION => match std::str::from_utf8(&v) {
73						Ok(v) => match Duration::try_from(v) {
74							Ok(v) => Ok(v.into()),
75							_ => Err("Expected a valid Duration value"),
76						},
77						_ => Err("Expected a valid UTF-8 string"),
78					},
79					// A literal datetime
80					TAG_DATETIME => match std::str::from_utf8(&v) {
81						Ok(v) => match Datetime::try_from(v) {
82							Ok(v) => Ok(v.into()),
83							_ => Err("Expected a valid Datetime value"),
84						},
85						_ => Err("Expected a valid UTF-8 string"),
86					},
87					// A literal recordid
88					TAG_RECORDID => match std::str::from_utf8(&v) {
89						Ok(v) => match Thing::try_from(v) {
90							Ok(v) => Ok(v.into()),
91							_ => Err("Expected a valid RecordID value"),
92						},
93						_ => Err("Expected a valid UTF-8 string"),
94					},
95					// An unknown tag
96					_ => Err("Encountered an unknown MessagePack tag"),
97				}
98			}
99			_ => Err("Encountered an unknown MessagePack data type"),
100		}
101	}
102}
103
104impl TryFrom<Value> for Pack {
105	type Error = &'static str;
106	fn try_from(val: Value) -> Result<Self, &'static str> {
107		match val {
108			Value::None => Ok(Pack(Data::Ext(TAG_NONE, vec![]))),
109			Value::Null => Ok(Pack(Data::Nil)),
110			Value::Bool(v) => Ok(Pack(Data::Boolean(v))),
111			Value::Number(v) => match v {
112				Number::Int(v) => Ok(Pack(Data::Integer(v.into()))),
113				Number::Float(v) => Ok(Pack(Data::F64(v))),
114				Number::Decimal(v) => {
115					Ok(Pack(Data::Ext(TAG_DECIMAL, v.to_string().as_bytes().to_vec())))
116				}
117			},
118			Value::Strand(v) => Ok(Pack(Data::String(v.0.into()))),
119			Value::Duration(v) => Ok(Pack(Data::Ext(TAG_DURATION, v.to_raw().as_bytes().to_vec()))),
120			Value::Datetime(v) => Ok(Pack(Data::Ext(TAG_DATETIME, v.to_raw().as_bytes().to_vec()))),
121			Value::Uuid(v) => Ok(Pack(Data::Ext(TAG_UUID, v.to_raw().as_bytes().to_vec()))),
122			Value::Array(v) => Ok(Pack(Data::Array(
123				v.into_iter()
124					.map(|v| {
125						let v = Pack::try_from(v)?.0;
126						Ok(v)
127					})
128					.collect::<Result<Vec<Data>, &str>>()?,
129			))),
130			Value::Object(v) => Ok(Pack(Data::Map(
131				v.into_iter()
132					.map(|(k, v)| {
133						let k = Data::String(k.into());
134						let v = Pack::try_from(v)?.0;
135						Ok((k, v))
136					})
137					.collect::<Result<Vec<(Data, Data)>, &str>>()?,
138			))),
139			Value::Bytes(v) => Ok(Pack(Data::Binary(v.into_inner()))),
140			Value::Thing(v) => Ok(Pack(Data::Ext(TAG_RECORDID, v.to_raw().as_bytes().to_vec()))),
141			// We shouldn't reach here
142			_ => Err("Found unsupported SurrealQL value being encoded into a msgpack value"),
143		}
144	}
145}