use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum FieldType {
U8,
U16,
U32,
U64,
U128,
I64,
Bool,
Bytes(usize),
VarBytes,
Address,
Hash,
}
impl FieldType {
pub fn fixed_size(&self) -> Option<usize> {
match self {
FieldType::U8 => Some(1),
FieldType::U16 => Some(2),
FieldType::U32 => Some(4),
FieldType::U64 => Some(8),
FieldType::U128 => Some(16),
FieldType::I64 => Some(8),
FieldType::Bool => Some(1),
FieldType::Bytes(n) => Some(*n),
FieldType::Address => Some(32),
FieldType::Hash => Some(32),
FieldType::VarBytes => None,
}
}
pub fn type_name(&self) -> &'static str {
match self {
FieldType::U8 => "u8",
FieldType::U16 => "u16",
FieldType::U32 => "u32",
FieldType::U64 => "u64",
FieldType::U128 => "u128",
FieldType::I64 => "i64",
FieldType::Bool => "bool",
FieldType::Bytes(_) => "bytes",
FieldType::VarBytes => "varbytes",
FieldType::Address => "address",
FieldType::Hash => "hash",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Field {
pub name: String,
pub field_type: FieldType,
pub is_key: bool,
pub indexed: bool,
}
impl Field {
pub fn key(name: impl Into<String>, field_type: FieldType) -> Self {
Self {
name: name.into(),
field_type,
is_key: true,
indexed: false,
}
}
pub fn value(name: impl Into<String>, field_type: FieldType) -> Self {
Self {
name: name.into(),
field_type,
is_key: false,
indexed: false,
}
}
pub fn indexed_value(name: impl Into<String>, field_type: FieldType) -> Self {
Self {
name: name.into(),
field_type,
is_key: false,
indexed: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum FieldValue {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
I64(i64),
Bool(bool),
Bytes(Vec<u8>),
Null,
}
impl FieldValue {
pub fn type_name(&self) -> &'static str {
match self {
FieldValue::U8(_) => "u8",
FieldValue::U16(_) => "u16",
FieldValue::U32(_) => "u32",
FieldValue::U64(_) => "u64",
FieldValue::U128(_) => "u128",
FieldValue::I64(_) => "i64",
FieldValue::Bool(_) => "bool",
FieldValue::Bytes(_) => "bytes",
FieldValue::Null => "null",
}
}
pub fn as_u64(&self) -> Option<u64> {
match self {
FieldValue::U8(v) => Some(*v as u64),
FieldValue::U16(v) => Some(*v as u64),
FieldValue::U32(v) => Some(*v as u64),
FieldValue::U64(v) => Some(*v),
_ => None,
}
}
pub fn as_u128(&self) -> Option<u128> {
match self {
FieldValue::U128(v) => Some(*v),
other => other.as_u64().map(|v| v as u128),
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
FieldValue::Bytes(v) => Some(v.as_slice()),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Schema {
pub name: String,
pub fields: Vec<Field>,
}
impl Schema {
pub fn new(name: impl Into<String>, fields: Vec<Field>) -> Self {
Self {
name: name.into(),
fields,
}
}
pub fn field(&self, name: &str) -> Option<(usize, &Field)> {
self.fields.iter().enumerate().find(|(_, f)| f.name == name)
}
pub fn key_fields(&self) -> Vec<(usize, &Field)> {
self.fields
.iter()
.enumerate()
.filter(|(_, f)| f.is_key)
.collect()
}
pub fn indexed_fields(&self) -> Vec<(usize, &Field)> {
self.fields
.iter()
.enumerate()
.filter(|(_, f)| f.indexed)
.collect()
}
pub fn value_fields(&self) -> Vec<(usize, &Field)> {
self.fields
.iter()
.enumerate()
.filter(|(_, f)| !f.is_key)
.collect()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Record {
pub values: Vec<FieldValue>,
}
impl Record {
pub fn new(values: Vec<FieldValue>) -> Self {
Self { values }
}
pub fn get(&self, idx: usize) -> Option<&FieldValue> {
self.values.get(idx)
}
pub fn get_by_name<'a>(&'a self, schema: &Schema, name: &str) -> Option<&'a FieldValue> {
let (idx, _) = schema.field(name)?;
self.values.get(idx)
}
pub fn validate(&self, schema: &Schema) -> Result<(), crate::error::RelError> {
if self.values.len() != schema.fields.len() {
return Err(crate::error::RelError::Schema(format!(
"record has {} values, schema '{}' expects {}",
self.values.len(),
schema.name,
schema.fields.len()
)));
}
for (field, value) in schema.fields.iter().zip(self.values.iter()) {
let ok = match (&field.field_type, value) {
(FieldType::U8, FieldValue::U8(_)) => true,
(FieldType::U16, FieldValue::U16(_)) => true,
(FieldType::U32, FieldValue::U32(_)) => true,
(FieldType::U64, FieldValue::U64(_)) => true,
(FieldType::U128, FieldValue::U128(_)) => true,
(FieldType::I64, FieldValue::I64(_)) => true,
(FieldType::Bool, FieldValue::Bool(_)) => true,
(FieldType::Bytes(n), FieldValue::Bytes(b)) => b.len() == *n,
(FieldType::VarBytes, FieldValue::Bytes(_)) => true,
(FieldType::Address, FieldValue::Bytes(b)) => b.len() == 32,
(FieldType::Hash, FieldValue::Bytes(b)) => b.len() == 32,
(_, FieldValue::Null) => true, _ => false,
};
if !ok {
return Err(crate::error::RelError::TypeMismatch {
field: field.name.clone(),
expected: field.field_type.type_name().to_string(),
got: value.type_name().to_string(),
});
}
}
Ok(())
}
}