use super::key_spec::KeySpec;
use crate::collation::Collatable;
use crate::value::{Value, ValueType};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum IndexError {
#[error("Type mismatch: expected {0:?}, got {1:?}")]
TypeMismatch(ValueType, ValueType),
}
const JSON_TAG_NULL: u8 = 0x00;
const JSON_TAG_BOOL: u8 = 0x10;
const JSON_TAG_INT: u8 = 0x20; const JSON_TAG_FLOAT: u8 = 0x30; const JSON_TAG_STRING: u8 = 0x40;
pub fn encode_component_typed(value: &Value, expected_type: ValueType, descending: bool) -> Result<Vec<u8>, IndexError> {
let value = value.cast_to(expected_type).map_err(|_| IndexError::TypeMismatch(expected_type, ValueType::of(value)))?;
encode_value_component(&value, expected_type, descending)
}
fn encode_value_component(value: &Value, expected_type: ValueType, descending: bool) -> Result<Vec<u8>, IndexError> {
match (value, expected_type) {
(Value::String(s), ValueType::String) => {
if !descending {
let mut out = Vec::with_capacity(s.len() + 1);
for &b in s.as_bytes() {
if b == 0x00 {
out.push(0x00);
out.push(0xFF);
} else {
out.push(b);
}
}
out.push(0x00);
Ok(out)
} else {
let mut out = Vec::with_capacity(s.len() + 2);
for &b in s.as_bytes() {
let inv = 0xFFu8.wrapping_sub(b);
if inv == 0xFF {
out.push(0xFF);
out.push(0x00);
} else {
out.push(inv);
}
}
out.push(0xFF);
out.push(0xFF);
Ok(out)
}
}
(Value::I16(_) | Value::I32(_) | Value::I64(_), ValueType::I16 | ValueType::I32 | ValueType::I64) => {
let bytes = value.to_bytes();
if !descending {
Ok(bytes)
} else {
Ok(bytes.into_iter().map(|b| 0xFFu8.wrapping_sub(b)).collect())
}
}
(Value::F64(_), ValueType::F64) => {
let bytes = value.to_bytes();
if !descending {
Ok(bytes)
} else {
Ok(bytes.into_iter().map(|b| 0xFFu8.wrapping_sub(b)).collect())
}
}
(Value::Bool(_), ValueType::Bool) => {
let b = value.to_bytes()[0];
Ok(vec![if !descending { b } else { 0xFFu8.wrapping_sub(b) }])
}
(Value::EntityId(entity_id), ValueType::EntityId) => {
let bytes = entity_id.to_bytes();
if !descending {
Ok(bytes.to_vec())
} else {
Ok(bytes.into_iter().map(|b| 0xFFu8.wrapping_sub(b)).collect())
}
}
(Value::Object(bytes) | Value::Binary(bytes), ValueType::Binary | ValueType::Object) => {
if !descending {
let mut out = Vec::with_capacity(bytes.len() + 1);
for &b in bytes.iter() {
if b == 0x00 {
out.push(0x00);
out.push(0xFF);
} else {
out.push(b);
}
}
out.push(0x00);
Ok(out)
} else {
let mut out = Vec::with_capacity(bytes.len() + 2);
for &b in bytes.iter() {
let inv = 0xFFu8.wrapping_sub(b);
if inv == 0xFF {
out.push(0xFF);
out.push(0x00);
} else {
out.push(inv);
}
}
out.push(0xFF);
out.push(0xFF);
Ok(out)
}
}
(Value::Json(json), ValueType::Json) => Ok(encode_json_value(json, descending)),
_ => Err(IndexError::TypeMismatch(expected_type, ValueType::of(value))),
}
}
fn encode_json_value(json: &serde_json::Value, descending: bool) -> Vec<u8> {
let (tag, payload) = match json {
serde_json::Value::Null => (JSON_TAG_NULL, vec![]),
serde_json::Value::Bool(b) => (JSON_TAG_BOOL, vec![if *b { 1 } else { 0 }]),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
(JSON_TAG_INT, Value::I64(i).to_bytes())
} else if let Some(f) = n.as_f64() {
(JSON_TAG_FLOAT, Value::F64(f).to_bytes())
} else {
(JSON_TAG_NULL, vec![])
}
}
serde_json::Value::String(s) => {
let mut payload = Vec::with_capacity(s.len() + 1);
for &b in s.as_bytes() {
if b == 0x00 {
payload.push(0x00);
payload.push(0xFF);
} else {
payload.push(b);
}
}
payload.push(0x00); (JSON_TAG_STRING, payload)
}
serde_json::Value::Object(_) | serde_json::Value::Array(_) => (JSON_TAG_NULL, vec![]),
};
if !descending {
let mut out = Vec::with_capacity(1 + payload.len());
out.push(tag);
out.extend(payload);
out
} else {
let mut out = Vec::with_capacity(1 + payload.len());
out.push(0xFFu8.wrapping_sub(tag));
out.extend(payload.into_iter().map(|b| 0xFFu8.wrapping_sub(b)));
out
}
}
pub fn encode_tuple_values_with_key_spec(values: &[Value], key_spec: &KeySpec) -> Result<Vec<u8>, IndexError> {
let mut out = Vec::new();
for (i, v) in values.iter().enumerate() {
if i >= key_spec.keyparts.len() {
break; }
let keypart = &key_spec.keyparts[i];
let bytes = encode_component_typed(v, keypart.value_type, keypart.direction.is_desc())?;
out.extend_from_slice(&bytes);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::Value;
#[test]
fn test_desc_ordering() {
let a = encode_component_typed(&Value::String("a".to_string()), ValueType::String, true).unwrap();
let b = encode_component_typed(&Value::String("b".to_string()), ValueType::String, true).unwrap();
assert!(a > b);
}
#[test]
fn test_asc_ordering() {
let a = encode_component_typed(&Value::String("a".to_string()), ValueType::String, false).unwrap();
let b = encode_component_typed(&Value::String("b".to_string()), ValueType::String, false).unwrap();
assert!(a < b);
}
}