surrealdb_core/rpc/format/msgpack/
convert.rs1use 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 TAG_NONE => Ok(Value::None),
55 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 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 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 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 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 _ => 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 _ => Err("Found unsupported SurrealQL value being encoded into a msgpack value"),
143 }
144 }
145}