Skip to main content

surrealdb_types/flatbuffers/
surrealism.rs

1use std::ops::Bound;
2
3use anyhow::Context;
4use surrealdb_protocol::fb::v1 as proto_fb;
5
6use super::{FromFlatbuffers, ToFlatbuffers};
7use crate::{Kind, Value};
8
9/// Encode a slice of `(&str, Kind)` pairs into a FlatBuffers `ArgumentList`.
10pub fn encode_argument_list(args: &[(&str, Kind)]) -> anyhow::Result<Vec<u8>> {
11	let mut fbb = flatbuffers::FlatBufferBuilder::new();
12	let offsets: Vec<_> = args
13		.iter()
14		.map(|(name, kind)| -> anyhow::Result<_> {
15			let key_offset = fbb.create_string(name);
16			let kind_offset = kind.to_fb(&mut fbb)?;
17			Ok(proto_fb::Argument::create(
18				&mut fbb,
19				&proto_fb::ArgumentArgs {
20					key: Some(key_offset),
21					value: Some(kind_offset),
22				},
23			))
24		})
25		.collect::<anyhow::Result<Vec<_>>>()?;
26	let vec = fbb.create_vector(&offsets);
27	let root = proto_fb::ArgumentList::create(
28		&mut fbb,
29		&proto_fb::ArgumentListArgs {
30			arguments: Some(vec),
31		},
32	);
33	fbb.finish(root, None);
34	Ok(fbb.finished_data().to_vec())
35}
36
37/// Decode a FlatBuffers `ArgumentList` into a `Vec<(String, Kind)>`.
38pub fn decode_argument_list(bytes: &[u8]) -> anyhow::Result<Vec<(String, Kind)>> {
39	let root = flatbuffers::root::<proto_fb::ArgumentList>(bytes)
40		.context("Failed to decode ArgumentList")?;
41	let arguments = root.arguments().context("Missing arguments in ArgumentList")?;
42	arguments
43		.iter()
44		.map(|arg| {
45			let key = arg.key().context("Missing key in Argument")?.to_string();
46			let kind_fb = arg.value().context("Missing value in Argument")?;
47			let kind = Kind::from_fb(kind_fb)?;
48			Ok((key, kind))
49		})
50		.collect()
51}
52
53/// Encode a slice of Values into a FlatBuffers `ValueList`.
54pub fn encode_value_list(values: &[Value]) -> anyhow::Result<Vec<u8>> {
55	let mut fbb = flatbuffers::FlatBufferBuilder::new();
56	let offsets: Vec<_> =
57		values.iter().map(|v| v.to_fb(&mut fbb)).collect::<anyhow::Result<Vec<_>>>()?;
58	let vec = fbb.create_vector(&offsets);
59	let root = proto_fb::ValueList::create(
60		&mut fbb,
61		&proto_fb::ValueListArgs {
62			values: Some(vec),
63		},
64	);
65	fbb.finish(root, None);
66	Ok(fbb.finished_data().to_vec())
67}
68
69/// Decode a FlatBuffers `ValueList` into a `Vec<Value>`.
70pub fn decode_value_list(bytes: &[u8]) -> anyhow::Result<Vec<Value>> {
71	let root =
72		flatbuffers::root::<proto_fb::ValueList>(bytes).context("Failed to decode ValueList")?;
73	let values = root.values().context("Missing values in ValueList")?;
74	values.iter().map(Value::from_fb).collect()
75}
76
77/// Encode a slice of Kinds into a FlatBuffers `KindList`.
78pub fn encode_kind_list(kinds: &[Kind]) -> anyhow::Result<Vec<u8>> {
79	let mut fbb = flatbuffers::FlatBufferBuilder::new();
80	let offsets: Vec<_> =
81		kinds.iter().map(|k| k.to_fb(&mut fbb)).collect::<anyhow::Result<Vec<_>>>()?;
82	let vec = fbb.create_vector(&offsets);
83	let root = proto_fb::KindList::create(
84		&mut fbb,
85		&proto_fb::KindListArgs {
86			kinds: Some(vec),
87		},
88	);
89	fbb.finish(root, None);
90	Ok(fbb.finished_data().to_vec())
91}
92
93/// Decode a FlatBuffers `KindList` into a `Vec<Kind>`.
94pub fn decode_kind_list(bytes: &[u8]) -> anyhow::Result<Vec<Kind>> {
95	let root =
96		flatbuffers::root::<proto_fb::KindList>(bytes).context("Failed to decode KindList")?;
97	let kinds = root.kinds().context("Missing kinds in KindList")?;
98	kinds.iter().map(Kind::from_fb).collect()
99}
100
101/// Encode a slice of `(String, Value)` pairs into a FlatBuffers `StringKeyValueList`.
102pub fn encode_string_key_values(entries: &[(String, Value)]) -> anyhow::Result<Vec<u8>> {
103	let mut fbb = flatbuffers::FlatBufferBuilder::new();
104	let offsets: Vec<_> = entries
105		.iter()
106		.map(|(key, value)| -> anyhow::Result<_> {
107			let key_offset = fbb.create_string(key);
108			let value_offset = value.to_fb(&mut fbb)?;
109			Ok(proto_fb::StringKeyValue::create(
110				&mut fbb,
111				&proto_fb::StringKeyValueArgs {
112					key: Some(key_offset),
113					value: Some(value_offset),
114				},
115			))
116		})
117		.collect::<anyhow::Result<Vec<_>>>()?;
118	let vec = fbb.create_vector(&offsets);
119	let root = proto_fb::StringKeyValueList::create(
120		&mut fbb,
121		&proto_fb::StringKeyValueListArgs {
122			entries: Some(vec),
123		},
124	);
125	fbb.finish(root, None);
126	Ok(fbb.finished_data().to_vec())
127}
128
129/// Decode a FlatBuffers `StringKeyValueList` into a `Vec<(String, Value)>`.
130pub fn decode_string_key_values(bytes: &[u8]) -> anyhow::Result<Vec<(String, Value)>> {
131	let root = flatbuffers::root::<proto_fb::StringKeyValueList>(bytes)
132		.context("Failed to decode StringKeyValueList")?;
133	let entries = root.entries().context("Missing entries in StringKeyValueList")?;
134	entries
135		.iter()
136		.map(|entry| {
137			let key = entry.key().context("Missing key in StringKeyValue")?.to_string();
138			let value_fb = entry.value().context("Missing value in StringKeyValue")?;
139			let value = Value::from_fb(value_fb)?;
140			Ok((key, value))
141		})
142		.collect()
143}
144
145/// Encode a slice of `Option<Value>` into a FlatBuffers `OptionalValueList`.
146pub fn encode_optional_values(values: &[Option<Value>]) -> anyhow::Result<Vec<u8>> {
147	let mut fbb = flatbuffers::FlatBufferBuilder::new();
148	let offsets: Vec<_> = values
149		.iter()
150		.map(|opt| -> anyhow::Result<_> {
151			match opt {
152				Some(value) => {
153					let value_offset = value.to_fb(&mut fbb)?;
154					Ok(proto_fb::OptionalValue::create(
155						&mut fbb,
156						&proto_fb::OptionalValueArgs {
157							present: true,
158							value: Some(value_offset),
159						},
160					))
161				}
162				None => Ok(proto_fb::OptionalValue::create(
163					&mut fbb,
164					&proto_fb::OptionalValueArgs {
165						present: false,
166						value: None,
167					},
168				)),
169			}
170		})
171		.collect::<anyhow::Result<Vec<_>>>()?;
172	let vec = fbb.create_vector(&offsets);
173	let root = proto_fb::OptionalValueList::create(
174		&mut fbb,
175		&proto_fb::OptionalValueListArgs {
176			values: Some(vec),
177		},
178	);
179	fbb.finish(root, None);
180	Ok(fbb.finished_data().to_vec())
181}
182
183/// Decode a FlatBuffers `OptionalValueList` into a `Vec<Option<Value>>`.
184pub fn decode_optional_values(bytes: &[u8]) -> anyhow::Result<Vec<Option<Value>>> {
185	let root = flatbuffers::root::<proto_fb::OptionalValueList>(bytes)
186		.context("Failed to decode OptionalValueList")?;
187	let values = root.values().context("Missing values in OptionalValueList")?;
188	values
189		.iter()
190		.map(|opt| {
191			if opt.present() {
192				let value_fb =
193					opt.value().context("OptionalValue marked present but missing value")?;
194				Ok(Some(Value::from_fb(value_fb)?))
195			} else {
196				Ok(None)
197			}
198		})
199		.collect()
200}
201
202fn bound_to_tag(bound: &Bound<String>) -> (proto_fb::StringBoundTag, Option<&str>) {
203	match bound {
204		Bound::Unbounded => (proto_fb::StringBoundTag::Unbounded, None),
205		Bound::Included(s) => (proto_fb::StringBoundTag::Included, Some(s.as_str())),
206		Bound::Excluded(s) => (proto_fb::StringBoundTag::Excluded, Some(s.as_str())),
207	}
208}
209
210fn tag_to_bound(
211	tag: proto_fb::StringBoundTag,
212	value: Option<&str>,
213) -> anyhow::Result<Bound<String>> {
214	match tag {
215		proto_fb::StringBoundTag::Unbounded => Ok(Bound::Unbounded),
216		proto_fb::StringBoundTag::Included => {
217			let s = value.context("Included bound missing value")?;
218			Ok(Bound::Included(s.to_string()))
219		}
220		proto_fb::StringBoundTag::Excluded => {
221			let s = value.context("Excluded bound missing value")?;
222			Ok(Bound::Excluded(s.to_string()))
223		}
224		_ => anyhow::bail!("Unknown StringBoundTag: {:?}", tag),
225	}
226}
227
228/// Encode a pair of string bounds into a FlatBuffers `StringRange`.
229pub fn encode_string_range(start: &Bound<String>, end: &Bound<String>) -> anyhow::Result<Vec<u8>> {
230	let mut fbb = flatbuffers::FlatBufferBuilder::new();
231	let (start_tag, start_val) = bound_to_tag(start);
232	let (end_tag, end_val) = bound_to_tag(end);
233	let start_offset = start_val.map(|s| fbb.create_string(s));
234	let end_offset = end_val.map(|s| fbb.create_string(s));
235	let root = proto_fb::StringRange::create(
236		&mut fbb,
237		&proto_fb::StringRangeArgs {
238			start_tag,
239			start_value: start_offset,
240			end_tag,
241			end_value: end_offset,
242		},
243	);
244	fbb.finish(root, None);
245	Ok(fbb.finished_data().to_vec())
246}
247
248/// Decode a FlatBuffers `StringRange` into a pair of bounds.
249pub fn decode_string_range(bytes: &[u8]) -> anyhow::Result<(Bound<String>, Bound<String>)> {
250	let root = flatbuffers::root::<proto_fb::StringRange>(bytes)
251		.context("Failed to decode StringRange")?;
252	let start = tag_to_bound(root.start_tag(), root.start_value())?;
253	let end = tag_to_bound(root.end_tag(), root.end_value())?;
254	Ok((start, end))
255}
256
257#[cfg(test)]
258mod tests {
259	use std::ops::Bound;
260
261	use rstest::rstest;
262
263	use super::*;
264	use crate::{Kind, Number, Value};
265
266	#[test]
267	fn test_value_list_roundtrip() {
268		let values =
269			vec![Value::String("hello".into()), Value::Number(Number::Int(42)), Value::Bool(true)];
270		let bytes = encode_value_list(&values).unwrap();
271		let decoded = decode_value_list(&bytes).unwrap();
272		assert_eq!(values, decoded);
273	}
274
275	#[test]
276	fn test_value_list_empty() {
277		let values: Vec<Value> = vec![];
278		let bytes = encode_value_list(&values).unwrap();
279		let decoded = decode_value_list(&bytes).unwrap();
280		assert_eq!(values, decoded);
281	}
282
283	#[test]
284	fn test_kind_list_roundtrip() {
285		let kinds = vec![Kind::String, Kind::Int, Kind::Bool];
286		let bytes = encode_kind_list(&kinds).unwrap();
287		let decoded = decode_kind_list(&bytes).unwrap();
288		assert_eq!(kinds, decoded);
289	}
290
291	#[test]
292	fn test_kind_list_empty() {
293		let kinds: Vec<Kind> = vec![];
294		let bytes = encode_kind_list(&kinds).unwrap();
295		let decoded = decode_kind_list(&bytes).unwrap();
296		assert_eq!(kinds, decoded);
297	}
298
299	#[test]
300	fn test_argument_list_roundtrip() {
301		let args: Vec<(&str, Kind)> =
302			vec![("name", Kind::String), ("age", Kind::Int), ("active", Kind::Bool)];
303		let bytes = encode_argument_list(&args).unwrap();
304		let decoded = decode_argument_list(&bytes).unwrap();
305		let expected: Vec<(String, Kind)> =
306			args.into_iter().map(|(n, k)| (n.to_string(), k)).collect();
307		assert_eq!(expected, decoded);
308	}
309
310	#[test]
311	fn test_argument_list_empty() {
312		let args: Vec<(&str, Kind)> = vec![];
313		let bytes = encode_argument_list(&args).unwrap();
314		let decoded = decode_argument_list(&bytes).unwrap();
315		assert!(decoded.is_empty());
316	}
317
318	#[test]
319	fn test_string_key_values_roundtrip() {
320		let entries = vec![
321			("key1".to_string(), Value::String("val1".into())),
322			("key2".to_string(), Value::Number(Number::Int(99))),
323		];
324		let bytes = encode_string_key_values(&entries).unwrap();
325		let decoded = decode_string_key_values(&bytes).unwrap();
326		assert_eq!(entries, decoded);
327	}
328
329	#[test]
330	fn test_string_key_values_empty() {
331		let entries: Vec<(String, Value)> = vec![];
332		let bytes = encode_string_key_values(&entries).unwrap();
333		let decoded = decode_string_key_values(&bytes).unwrap();
334		assert_eq!(entries, decoded);
335	}
336
337	#[test]
338	fn test_optional_values_roundtrip() {
339		let values = vec![
340			Some(Value::String("present".into())),
341			None,
342			Some(Value::Number(Number::Int(7))),
343			None,
344		];
345		let bytes = encode_optional_values(&values).unwrap();
346		let decoded = decode_optional_values(&bytes).unwrap();
347		assert_eq!(values, decoded);
348	}
349
350	#[test]
351	fn test_optional_values_all_none() {
352		let values: Vec<Option<Value>> = vec![None, None, None];
353		let bytes = encode_optional_values(&values).unwrap();
354		let decoded = decode_optional_values(&bytes).unwrap();
355		assert_eq!(values, decoded);
356	}
357
358	#[rstest]
359	#[case::unbounded_unbounded(Bound::Unbounded, Bound::Unbounded)]
360	#[case::included_excluded(
361		Bound::Included("a".to_string()),
362		Bound::Excluded("z".to_string())
363	)]
364	#[case::included_unbounded(
365		Bound::Included("start".to_string()),
366		Bound::Unbounded
367	)]
368	#[case::unbounded_excluded(
369		Bound::Unbounded,
370		Bound::Excluded("end".to_string())
371	)]
372	#[case::included_included(
373		Bound::Included("a".to_string()),
374		Bound::Included("z".to_string())
375	)]
376	fn test_string_range_roundtrip(#[case] start: Bound<String>, #[case] end: Bound<String>) {
377		let bytes = encode_string_range(&start, &end).unwrap();
378		let (decoded_start, decoded_end) = decode_string_range(&bytes).unwrap();
379		assert_eq!(start, decoded_start);
380		assert_eq!(end, decoded_end);
381	}
382}