mod collections;
mod geometry;
mod kind;
mod misc;
mod primitives;
mod record;
mod table;
mod value;
use anyhow::Context;
use crate::{Kind, SurrealValue, Value};
pub trait ToFlatbuffers {
type Output<'bldr>;
fn to_fb<'bldr>(
&self,
builder: &mut ::flatbuffers::FlatBufferBuilder<'bldr>,
) -> anyhow::Result<Self::Output<'bldr>>;
}
pub trait FromFlatbuffers {
type Input<'a>;
fn from_fb(input: Self::Input<'_>) -> anyhow::Result<Self>
where
Self: Sized;
}
pub fn encode(value: &Value) -> anyhow::Result<Vec<u8>> {
let mut fbb = flatbuffers::FlatBufferBuilder::new();
let value = value.to_fb(&mut fbb)?;
fbb.finish(value, None);
let data = fbb.finished_data().to_vec();
Ok(data)
}
pub fn decode<T: SurrealValue>(value: &[u8]) -> anyhow::Result<T> {
let value_fb = flatbuffers::root::<surrealdb_protocol::fb::v1::Value>(value)
.context("Failed to decode fb value")?;
let value = Value::from_fb(value_fb).context("Failed to decode value from fb value")?;
T::from_value(value).context("Failed to decode T from value")
}
pub fn encode_kind(kind: &Kind) -> anyhow::Result<Vec<u8>> {
let mut fbb = flatbuffers::FlatBufferBuilder::new();
let value = kind.to_fb(&mut fbb)?;
fbb.finish(value, None);
let data = fbb.finished_data().to_vec();
Ok(data)
}
pub fn decode_kind(value: &[u8]) -> anyhow::Result<Kind> {
let value_fb = flatbuffers::root::<surrealdb_protocol::fb::v1::Kind>(value)
.context("Failed to decode fb kind")?;
let kind = Kind::from_fb(value_fb).context("Failed to decode kind from fb kind")?;
Ok(kind)
}
#[cfg(test)]
mod tests {
use chrono::{DateTime, Utc};
use rstest::rstest;
use rust_decimal::Decimal;
use super::*;
use crate::{
Array, Bytes, Datetime, Duration, File, Geometry, Number, Object, Range, RecordId,
RecordIdKey, Regex, Table, Uuid, object,
};
#[rstest]
#[case::none(Value::None)]
#[case::null(Value::Null)]
#[case::bool(Value::Bool(true))]
#[case::bool(Value::Bool(false))]
#[case::int(Value::Number(Number::Int(42)))]
#[case::int(Value::Number(Number::Int(i64::MIN)))]
#[case::int(Value::Number(Number::Int(i64::MAX)))]
#[case::float(Value::Number(Number::Float(1.23)))]
#[case::float(Value::Number(Number::Float(f64::MIN)))]
#[case::float(Value::Number(Number::Float(f64::MAX)))]
#[case::float(Value::Number(Number::Float(f64::NAN)))]
#[case::float(Value::Number(Number::Float(f64::INFINITY)))]
#[case::float(Value::Number(Number::Float(f64::NEG_INFINITY)))]
#[case::decimal(Value::Number(Number::Decimal(Decimal::new(123, 2))))]
#[case::duration(Value::Duration(Duration::default()))]
#[case::datetime(Value::Datetime(Datetime(DateTime::<Utc>::from_timestamp(1_000_000_000, 0).unwrap())))]
#[case::uuid(Value::Uuid(Uuid::default()))]
#[case::string(Value::String("".to_string()))]
#[case::string(Value::String("Hello, World!".to_string()))]
#[case::bytes(Value::Bytes(Bytes(::bytes::Bytes::from(vec![1_u8, 2, 3, 4, 5]))))]
#[case::bytes(Value::Bytes(Bytes(::bytes::Bytes::from(vec![0_u8; 1024]))))]
#[case::table(Value::Table(Table::new("test_table")))]
#[case::record_id(Value::RecordId(RecordId::new("test_table", 42)))]
#[case::record_id(Value::RecordId(RecordId::new("test_table", "test_key")))]
#[case::record_id(Value::RecordId(RecordId::new(
"test_table",
RecordIdKey::Object(Object(std::collections::BTreeMap::from([
("key".to_string(), Value::String("value".to_string()))
])))
)))]
#[case::record_id(Value::RecordId(RecordId::new(
"test_table",
RecordIdKey::Array(Array(vec![
Value::Number(Number::Int(1)),
Value::Number(Number::Int(2)),
]))
)))]
#[case::file(Value::File(File::new("test_file", "test_file.txt")))]
#[case::range(Value::Range(Box::new(Range::new(
std::collections::Bound::Included(Value::Number(Number::Int(42))),
std::collections::Bound::Included(Value::Number(Number::Int(43)))
))))]
#[case::regex(Value::Regex(Regex(regex::Regex::new("").unwrap())))]
#[case::regex(Value::Regex(Regex(regex::Regex::new("test_regex").unwrap())))]
#[case::array(Value::Array(Array::from(vec![Value::Number(Number::Int(1)), Value::Number(Number::Float(2.0))])))]
#[case::object(Value::Object(object! { "key": "value".to_string() }))]
#[case::geometry(Value::Geometry(Geometry::Point(geo::Point::new(1.0, 2.0))))]
fn test_encode_decode(#[case] input: Value) {
let encoded = encode(&input).expect("Failed to encode");
let decoded = decode::<Value>(&encoded).expect("Failed to decode");
assert_eq!(input, decoded);
}
}