attribute and json

main
Ziyang Hu 2 years ago
parent c364e9ae54
commit 26d1718af1

@ -21,6 +21,7 @@ serde_derive = "1.0.137"
rmp = "0.8.11"
rmp-serde = "1.1.0"
rmpv = "1.0.0"
base64 = "0.13.0"
ordered-float = { version = "3.0", features = ["serde"] }
cozorocks = { path = "cozorocks" }

@ -0,0 +1,252 @@
use crate::data::encode::Encoded;
use crate::data::id::{AttrId, EntityId, TxId};
use crate::data::keyword::Keyword;
use crate::data::value::Value;
use anyhow::Result;
use rmp_serde::Serializer;
use serde::Serialize;
use serde_derive::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::fmt::{Display, Formatter};
#[derive(Debug, thiserror::Error)]
pub enum AttributeError {
#[error("cannot convert {0} to {1}")]
Conversion(String, String),
}
#[repr(u8)]
#[derive(Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Deserialize, Serialize)]
pub(crate) enum AttributeCardinality {
One = 1,
Many = 2,
}
impl Display for AttributeCardinality {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AttributeCardinality::One => write!(f, "one"),
AttributeCardinality::Many => write!(f, "many"),
}
}
}
impl TryFrom<&'_ str> for AttributeCardinality {
type Error = AttributeError;
fn try_from(value: &'_ str) -> std::result::Result<Self, Self::Error> {
match value {
"one" => Ok(AttributeCardinality::One),
"many" => Ok(AttributeCardinality::Many),
s => Err(AttributeError::Conversion(
s.to_string(),
"AttributeCardinality".to_string(),
)),
}
}
}
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Deserialize, Serialize)]
pub(crate) enum AttributeTyping {
Ref = 1,
Component = 2,
Bool = 3,
Int = 4,
Float = 5,
String = 6,
Keyword = 7,
Uuid = 8,
Timestamp = 9,
Bytes = 10,
Tuple = 11,
}
impl Display for AttributeTyping {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AttributeTyping::Ref => write!(f, "ref"),
AttributeTyping::Component => write!(f, "component"),
AttributeTyping::Bool => write!(f, "bool"),
AttributeTyping::Int => write!(f, "int"),
AttributeTyping::Float => write!(f, "float"),
AttributeTyping::String => write!(f, "string"),
AttributeTyping::Keyword => write!(f, "keyword"),
AttributeTyping::Uuid => write!(f, "uuid"),
AttributeTyping::Timestamp => write!(f, "timestamp"),
AttributeTyping::Bytes => write!(f, "bytes"),
AttributeTyping::Tuple => write!(f, "tuple"),
}
}
}
impl TryFrom<&'_ str> for AttributeTyping {
type Error = AttributeError;
fn try_from(value: &'_ str) -> std::result::Result<Self, Self::Error> {
use AttributeTyping::*;
Ok(match value {
"ref" => Ref,
"component" => Component,
"bool" => Bool,
"int" => Int,
"float" => Float,
"string" => String,
"keyword" => Keyword,
"uuid" => Uuid,
"timestamp" => Timestamp,
"bytes" => Bytes,
"tuple" => Tuple,
s => {
return Err(AttributeError::Conversion(
s.to_string(),
"AttributeTyping".to_string(),
))
}
})
}
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum TypeError {
#[error("provided value {1} is not of type {0:?}")]
Typing(AttributeTyping, String),
}
impl AttributeTyping {
fn type_err(&self, val: Value) -> TypeError {
TypeError::Typing(*self, format!("{:?}", val))
}
pub(crate) fn coerce_value<'a>(&self, val: Value<'a>) -> Result<Value<'a>> {
match self {
AttributeTyping::Ref | AttributeTyping::Component => match val {
val @ Value::EnId(_) => Ok(val),
Value::Int(s) if s > 0 => Ok(Value::EnId(EntityId(s as u64))),
val => Err(self.type_err(val).into()),
},
AttributeTyping::Bool => {
if matches!(val, Value::Bool(_)) {
Ok(val)
} else {
Err(self.type_err(val).into())
}
}
AttributeTyping::Int => {
if matches!(val, Value::Int(_)) {
Ok(val)
} else {
Err(self.type_err(val).into())
}
}
AttributeTyping::Float => match val {
v @ Value::Float(_) => Ok(v),
Value::Int(i) => Ok(Value::Float((i as f64).into())),
val => Err(self.type_err(val).into()),
},
AttributeTyping::String => {
if matches!(val, Value::String(_)) {
Ok(val)
} else {
Err(self.type_err(val).into())
}
}
AttributeTyping::Keyword => match val {
val @ Value::Keyword(_) => Ok(val),
Value::String(s) => Ok(Value::Keyword(Keyword::try_from(s.as_ref())?)),
val => Err(self.type_err(val).into()),
},
AttributeTyping::Uuid => {
if matches!(val, Value::Uuid(_)) {
Ok(val)
} else {
Err(self.type_err(val).into())
}
}
AttributeTyping::Timestamp => match val {
val @ Value::Timestamp(_) => Ok(val),
Value::Int(i) => Ok(Value::Timestamp(i)),
val => Err(self.type_err(val).into()),
},
AttributeTyping::Bytes => {
if matches!(val, Value::Bytes(_)) {
Ok(val)
} else {
Err(self.type_err(val).into())
}
}
AttributeTyping::Tuple => {
if matches!(val, Value::Tuple(_)) {
Ok(val)
} else {
Err(self.type_err(val).into())
}
}
}
}
}
#[repr(u8)]
#[derive(Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Deserialize, Serialize)]
pub(crate) enum AttributeIndex {
None = 0,
Indexed = 1,
Unique = 2,
Identity = 3,
}
impl Display for AttributeIndex {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AttributeIndex::None => write!(f, "none"),
AttributeIndex::Indexed => write!(f, "index"),
AttributeIndex::Unique => write!(f, "unique"),
AttributeIndex::Identity => write!(f, "identity"),
}
}
}
impl TryFrom<&'_ str> for AttributeIndex {
type Error = AttributeError;
fn try_from(value: &'_ str) -> std::result::Result<Self, Self::Error> {
use AttributeIndex::*;
Ok(match value {
"none" => None,
"index" => Indexed,
"unique" => Unique,
"identity" => Identity,
s => {
return Err(AttributeError::Conversion(
s.to_string(),
"AttributeIndex".to_string(),
))
}
})
}
}
#[derive(Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Deserialize, Serialize)]
pub(crate) struct Attribute {
#[serde(rename = "i")]
pub(crate) id: AttrId,
#[serde(rename = "n")]
pub(crate) alias: Keyword,
#[serde(rename = "c")]
pub(crate) cardinality: AttributeCardinality,
#[serde(rename = "t")]
pub(crate) val_type: AttributeTyping,
#[serde(rename = "u")]
pub(crate) indexing: AttributeIndex,
#[serde(rename = "h")]
pub(crate) with_history: bool,
}
const LARGE_VEC_SIZE: usize = 60;
impl Attribute {
pub(crate) fn encode(&self) -> Encoded<LARGE_VEC_SIZE> {
let mut inner = SmallVec::<[u8; LARGE_VEC_SIZE]>::new();
self.serialize(&mut Serializer::new(&mut inner)).unwrap();
Encoded { inner }
}
pub(crate) fn decode(data: &[u8]) -> Result<Self> {
Ok(rmp_serde::from_slice(data)?)
}
}

@ -31,7 +31,7 @@ pub enum StorageTagError {
}
pub(crate) struct Encoded<const N: usize> {
inner: SmallVec<[u8; N]>,
pub(crate) inner: SmallVec<[u8; N]>,
}
impl<const N: usize> Encoded<N> {

@ -0,0 +1,187 @@
use crate::data::attr::{Attribute, AttributeCardinality, AttributeIndex, AttributeTyping};
use crate::data::id::{AttrId, EntityId, TxId};
use crate::data::keyword::Keyword;
use crate::data::value::Value;
use serde_json::json;
pub(crate) use serde_json::Value as JsonValue;
#[derive(Debug, thiserror::Error)]
pub enum JsonError {
#[error("cannot convert JSON value {0} to {1}")]
Conversion(JsonValue, String),
#[error("missing field {1} in value {0}")]
MissingField(JsonValue, String),
}
impl From<JsonValue> for Value<'_> {
fn from(v: JsonValue) -> Self {
match v {
JsonValue::Null => Value::Null,
JsonValue::Bool(b) => Value::Bool(b),
JsonValue::Number(n) => match n.as_i64() {
Some(i) => Value::Int(i),
None => match n.as_f64() {
Some(f) => Value::Float(f.into()),
None => Value::String(n.to_string().into()),
},
},
JsonValue::String(s) => Value::String(s.into()),
JsonValue::Array(arr) => Value::Tuple(arr.into_iter().map(Value::from).collect()),
JsonValue::Object(d) => Value::Tuple(
d.into_iter()
.map(|(k, v)| Value::Tuple([Value::String(k.into()), Value::from(v)].into()))
.collect(),
),
}
}
}
impl From<Value<'_>> for JsonValue {
fn from(v: Value<'_>) -> Self {
match v {
Value::Null => JsonValue::Null,
Value::Bool(b) => JsonValue::Bool(b),
Value::Int(i) => JsonValue::Number(i.into()),
Value::Float(f) => json!(f.0),
Value::String(t) => JsonValue::String(t.into_owned()),
Value::Uuid(uuid) => JsonValue::String(uuid.to_string()),
Value::Bytes(bytes) => JsonValue::String(base64::encode(bytes)),
Value::Tuple(l) => {
JsonValue::Array(l.iter().map(|v| JsonValue::from(v.clone())).collect())
}
Value::DescVal(v) => JsonValue::from(*v.0),
Value::Bottom => JsonValue::Null,
Value::EnId(i) => JsonValue::Number(i.0.into()),
Value::Keyword(t) => JsonValue::String(t.to_string()),
Value::Timestamp(i) => JsonValue::Number(i.into()),
}
}
}
impl TryFrom<&'_ JsonValue> for Keyword {
type Error = anyhow::Error;
fn try_from(value: &'_ JsonValue) -> Result<Self, Self::Error> {
let s = value
.as_str()
.ok_or_else(|| JsonError::Conversion(value.clone(), "Keyword".to_string()))?;
Ok(Keyword::try_from(s)?)
}
}
impl TryFrom<&'_ JsonValue> for Attribute {
type Error = anyhow::Error;
fn try_from(value: &'_ JsonValue) -> Result<Self, Self::Error> {
let map = value
.as_object()
.ok_or_else(|| JsonError::Conversion(value.clone(), "Attribute".to_string()))?;
let id = match map.get("id") {
None => AttrId(0),
Some(v) => AttrId::try_from(v)?,
};
let alias = map
.get("alias")
.ok_or_else(|| JsonError::MissingField(value.clone(), "alias".to_string()))?;
let alias = Keyword::try_from(alias)?;
let cardinality = map
.get("cardinality")
.ok_or_else(|| JsonError::MissingField(value.clone(), "cardinality".to_string()))?
.as_str()
.ok_or_else(|| {
JsonError::Conversion(value.clone(), "AttributeCardinality".to_string())
})?;
let cardinality = AttributeCardinality::try_from(cardinality)?;
let val_type = map
.get("type")
.ok_or_else(|| JsonError::MissingField(value.clone(), "type".to_string()))?
.as_str()
.ok_or_else(|| JsonError::Conversion(value.clone(), "AttributeTyping".to_string()))?;
let val_type = AttributeTyping::try_from(val_type)?;
let indexing = match map.get("type") {
None => AttributeIndex::None,
Some(v) => AttributeIndex::try_from(v.as_str().ok_or_else(|| {
JsonError::Conversion(value.clone(), "AttributeIndexing".to_string())
})?)?,
};
let with_history = match map.get("with_history") {
None => true,
Some(v) => v.as_bool().ok_or_else(|| {
JsonError::Conversion(value.clone(), "AttributeWithHistory".to_string())
})?,
};
Ok(Attribute {
id,
alias,
cardinality,
val_type,
indexing,
with_history,
})
}
}
impl From<Attribute> for JsonValue {
fn from(attr: Attribute) -> Self {
json!({
"id": attr.id.0,
"alias": attr.alias.to_string(),
"cardinality": attr.cardinality.to_string(),
"type": attr.val_type.to_string(),
"index": attr.indexing.to_string(),
"with_history": attr.with_history
})
}
}
impl From<AttrId> for JsonValue {
fn from(id: AttrId) -> Self {
JsonValue::Number(id.0.into())
}
}
impl TryFrom<&'_ JsonValue> for AttrId {
type Error = JsonError;
fn try_from(value: &'_ JsonValue) -> Result<Self, Self::Error> {
let v = value
.as_u64()
.ok_or_else(|| JsonError::Conversion(value.clone(), "AttrId".to_string()))?;
Ok(AttrId(v))
}
}
impl From<EntityId> for JsonValue {
fn from(id: EntityId) -> Self {
JsonValue::Number(id.0.into())
}
}
impl TryFrom<&'_ JsonValue> for EntityId {
type Error = JsonError;
fn try_from(value: &'_ JsonValue) -> Result<Self, Self::Error> {
let v = value
.as_u64()
.ok_or_else(|| JsonError::Conversion(value.clone(), "EntityId".to_string()))?;
Ok(EntityId(v))
}
}
impl From<TxId> for JsonValue {
fn from(id: TxId) -> Self {
JsonValue::Number(id.0.into())
}
}
impl TryFrom<&'_ JsonValue> for TxId {
type Error = JsonError;
fn try_from(value: &'_ JsonValue) -> Result<Self, Self::Error> {
let v = value
.as_u64()
.ok_or_else(|| JsonError::Conversion(value.clone(), "TxId".to_string()))?;
Ok(TxId(v))
}
}

@ -1,10 +1,11 @@
use serde_derive::{Deserialize, Serialize};
use smartstring::{LazyCompact, SmartString};
use std::fmt::{Display, Formatter};
use std::str::Utf8Error;
#[derive(Debug, thiserror::Error)]
pub enum KeywordError {
#[error("invalid keyword: {0}")]
#[error("cannot convert to keyword: {0}")]
InvalidKeyword(String),
#[error(transparent)]
@ -19,6 +20,12 @@ pub struct Keyword {
pub(crate) ident: SmartString<LazyCompact>,
}
impl Display for Keyword {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{}", self.ns, self.ident)
}
}
impl TryFrom<&str> for Keyword {
type Error = KeywordError;
fn try_from(value: &str) -> Result<Self, Self::Error> {

@ -5,3 +5,5 @@ pub(crate) mod triple;
pub(crate) mod tx;
pub(crate) mod encode;
pub(crate) mod keyword;
pub(crate) mod attr;
pub(crate) mod json;

@ -15,10 +15,6 @@ pub enum Value<'a> {
Bool(bool),
#[serde(rename = "e")]
EnId(EntityId),
#[serde(rename = "a")]
AtId(AttrId),
#[serde(rename = "t")]
TxId(TxId),
#[serde(rename = "i")]
Int(i64),
#[serde(rename = "f")]
@ -43,3 +39,20 @@ pub enum Value<'a> {
#[serde(rename = "r")]
Bottom,
}
#[cfg(test)]
mod tests {
use crate::data::keyword::Keyword;
use crate::data::value::Value;
use std::collections::{BTreeMap, HashMap};
use std::mem::size_of;
#[test]
fn show_size() {
dbg!(size_of::<Value>());
dbg!(size_of::<Keyword>());
dbg!(size_of::<String>());
dbg!(size_of::<HashMap<String, String>>());
dbg!(size_of::<BTreeMap<String, String>>());
}
}

@ -60,7 +60,7 @@ impl Db {
}
pub(crate) fn transact(&self) -> SessionTx {
SessionTx {
tx: self.db.transact().start(),
tx: self.db.transact().set_snapshot(true).start(),
}
}
}

Loading…
Cancel
Save