feat: encoding of nested option types

This commit is contained in:
Jindřich Moravec 2024-02-01 21:22:32 +01:00
parent af5490e2dc
commit 896d40ab64

View file

@ -1,6 +1,7 @@
use crate::error::TypeConversionError;
use std::cmp::Ordering;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::format;
// ==============Types================
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -9,7 +10,7 @@ pub enum DbType {
Int,
Number,
Uuid,
Option(Box<DbType>)
Option(Box<DbType>),
}
// ==============Values================
@ -48,7 +49,7 @@ impl PartialOrd for IndexableValue {
(IndexableValue::None(_), IndexableValue::Some(_)) => Some(Ordering::Less),
(IndexableValue::Some(_), IndexableValue::None(_)) => Some(Ordering::Greater),
(IndexableValue::Some(v0), IndexableValue::Some(v1)) => v0.partial_cmp(v1),
_ => None
_ => None,
}
}
}
@ -64,29 +65,38 @@ impl Ord for IndexableValue {
(IndexableValue::Some(_), IndexableValue::None(_)) => Ordering::Greater,
(IndexableValue::Some(v0), IndexableValue::Some(v1)) => v0.cmp(v1),
_ =>
// SAFETY:
// We are using indexable values as keys in key-value maps.
// When validation is done, it can't happen that we will be comparing two values
// of different types.
// Ofcourse another option is to artificialy order e.g.
// None < Some(...) < String < Int < Uuid
// where ... is again None < Some(...) < String < Int < Uuid
// where ...
// infinitely deep total order. But this is pointless for our usecase.
unreachable!()
// SAFETY:
// We are using indexable values as keys in key-value maps.
// When validation is done, it can't happen that we will be comparing two values
// of different types.
// Ofcourse another option is to artificialy order e.g.
// None < Some(...) < String < Int < Uuid
// where ... is again None < Some(...) < String < Int < Uuid
// where ...
// infinitely deep total order. But this is pointless for our usecase.
{
unreachable!()
}
}
}
}
impl DbType {
fn new_n_option(n: usize, inside: DbType) -> DbType {
if n == 0 {
inside
} else {
DbType::Option(Box::new(DbType::new_n_option(n - 1, inside)))
}
}
pub fn is_indexable(&self) -> bool {
match self {
Self::String => true,
Self::Int => true,
Self::Number => false,
Self::Uuid => true,
Self::Option(type_) => type_.is_indexable()
Self::Option(type_) => type_.is_indexable(),
}
}
@ -121,24 +131,39 @@ impl DbType {
}
}
fn from_oid_and_size(oid: i32, size: i16) -> Option<DbType> {
match (oid, size) {
(25, -2) => Some(DbType::String),
(23, 8) => Some(DbType::Int),
(701, 8) => Some(DbType::Number),
(2950, 16) => Some(DbType::Uuid),
_ => None,
}
}
pub fn type_oid(&self) -> i32 {
// match self {
// Self::String => 25,
// Self::Int => 23,
// Self::Number => 701,
// Self::Uuid => 2950,
// }
todo!()
match self {
Self::String => 25,
Self::Int => 23,
Self::Number => 701,
Self::Uuid => 2950,
Self::Option(t) => {
let oid = t.type_oid();
let type_part = (oid & 0x0000FFFF) as u16;
let nest_part = ((oid & 0xFFFF0000) >> 16) as u16 + 1;
(type_part | (nest_part << 16)) as i32
}
}
}
pub fn type_size(&self) -> i16 {
// match self {
// Self::String => -2, // null terminated string
// Self::Int => 8,
// Self::Number => 8,
// Self::Uuid => 16,
// }
todo!()
match self {
Self::String => -2, // null terminated string
Self::Int => 8,
Self::Number => 8,
Self::Uuid => 16,
Self::Option(t) => t.type_size(),
}
}
}
@ -216,17 +241,20 @@ impl Value {
}
}
pub fn as_text_bytes(&self) -> Vec<u8> {
// match self {
// Self::Number(n) => format!("{n}").into_bytes(),
// Self::Indexable(i) => match i {
// IndexableValue::String(s) => format!("{s}\0").into_bytes(),
// IndexableValue::Int(i) => format!("{i}").into_bytes(),
// IndexableValue::Uuid(u) => format!("{u}").into_bytes(),
// },
// }
todo!()
match self {
Self::Number(n) => format!("{n}").into_bytes(),
Self::String(s) => format!("{s}\0").into_bytes(),
Self::Int(i) => format!("{i}").into_bytes(),
Self::Uuid(u) => format!("u{u}").into_bytes(),
Self::None(_) => b"None".into(),
Self::Some(val) => {
let mut bytes = Vec::from(b"Some(");
bytes.append(&mut val.as_text_bytes());
bytes.push(b')');
bytes
}
}
}
pub fn from_text_bytes(
@ -234,30 +262,68 @@ impl Value {
type_oid: i32,
type_size: i16,
) -> Result<Value, TypeConversionError> {
// match (type_oid, type_size) {
// (701, 8) => {
// let s = std::str::from_utf8(bytes)?;
// let n = s.parse::<f64>()?;
// Ok(Value::Number(n))
// }
// (25, -2) => {
// let s = std::str::from_utf8(bytes)?;
// let s = &s[..s.len() - 1]; // remove null terminator
// Ok(Value::Indexable(IndexableValue::String(s.to_string())))
// }
// (23, 8) => {
// let s = std::str::from_utf8(bytes)?;
// let n = s.parse::<u64>()?;
// Ok(Value::Indexable(IndexableValue::Int(n)))
// }
// (2950, 16) => {
// let s = std::str::from_utf8(bytes)?;
// let n = s.parse::<Uuid>()?;
// Ok(Value::Indexable(IndexableValue::Uuid(n)))
// }
// (oid, size) => Err(TypeConversionError::UnknownType { oid, size }),
// }
todo!()
let text = std::str::from_utf8(bytes)?;
let type_part = (type_oid & 0x0000FFFF) as u16;
let nest_part = ((type_oid as u32 & 0xFFFF0000) >> 16) as u16;
Self::internal_from_text_bytes(text, type_part, nest_part, type_size)
}
fn internal_from_text_bytes(
text: &str,
type_part: u16,
nest_part: u16,
type_size: i16,
) -> Result<Value, TypeConversionError> {
if text == "None" {
let db_type =
DbType::from_oid_and_size(type_part as i32, type_size).ok_or_else(|| {
TypeConversionError::UnknownType {
oid: type_part as i32,
size: type_size,
}
})?;
return if nest_part == 0 {
Ok(Value::None(db_type))
} else {
Ok(Value::None(DbType::new_n_option(
nest_part as usize,
db_type,
)))
};
}
if text.starts_with("Some(") {
let val = Self::from_text_bytes(
&text[5..text.len() - 1].as_bytes(),
type_part as i32,
type_size,
)?;
return Ok(Value::Some(Box::new(val)));
}
match (type_part, type_size) {
(701, 8) => {
let n = text.parse::<f64>()?;
Ok(Value::Number(n))
}
(25, -2) => {
let s = &text[..text.len() - 1]; // remove null terminator
Ok(Value::String(s.to_string()))
}
(23, 8) => {
let n = text.parse::<u64>()?;
Ok(Value::Int(n))
}
(2950, 16) => {
let n = text.parse::<Uuid>()?;
Ok(Value::Uuid(n))
}
(oid, size) => Err(TypeConversionError::UnknownType {
oid: oid as i32,
size,
}),
}
}
}
@ -267,7 +333,9 @@ impl IndexableValue {
IndexableValue::String(str) => Value::String(str),
IndexableValue::Int(n) => Value::Int(n),
IndexableValue::Uuid(id) => Value::Uuid(id),
IndexableValue::Some(indexable_value) => Value::Some(Box::new(indexable_value.to_value())),
IndexableValue::Some(indexable_value) => {
Value::Some(Box::new(indexable_value.to_value()))
}
IndexableValue::None(type_) => Value::None(type_),
}
}
@ -322,7 +390,6 @@ impl From<IndexableValue> for Value {
}
}
// Own string serialization so enums can be used as keys in maps
impl From<IndexableValue> for String {
fn from(value: IndexableValue) -> Self {