Skip to content

Commit

Permalink
Cleanup grpc types (#1244)
Browse files Browse the repository at this point in the history
  • Loading branch information
broody authored Dec 8, 2023
1 parent d7a174c commit 8b758b5
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 264 deletions.
14 changes: 13 additions & 1 deletion crates/dojo-types/src/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,19 @@ impl Primitive {
set_primitive!(set_contract_address, ContractAddress, FieldElement);

pub fn to_numeric(&self) -> usize {
Self::iter().position(|p| p == *self).unwrap()
match self {
Primitive::U8(_) => 0,
Primitive::U16(_) => 1,
Primitive::U32(_) => 2,
Primitive::U64(_) => 3,
Primitive::U128(_) => 4,
Primitive::U256(_) => 5,
Primitive::USize(_) => 6,
Primitive::Bool(_) => 7,
Primitive::Felt252(_) => 8,
Primitive::ClassHash(_) => 9,
Primitive::ContractAddress(_) => 10,
}
}

pub fn from_numeric(value: usize) -> Option<Self> {
Expand Down
3 changes: 2 additions & 1 deletion crates/torii/client/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use starknet_crypto::FieldElement;
use tokio::sync::RwLock as AsyncRwLock;
use torii_grpc::client::EntityUpdateStreaming;
use torii_grpc::proto::world::RetrieveEntitiesResponse;
use torii_grpc::types::{Entity, KeysClause, Query};
use torii_grpc::types::schema::Entity;
use torii_grpc::types::{KeysClause, Query};

use self::error::{Error, ParseError};
use self::storage::ModelStorage;
Expand Down
118 changes: 16 additions & 102 deletions crates/torii/grpc/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use std::pin::Pin;
use std::str::FromStr;
use std::sync::Arc;

use dojo_types::primitive::Primitive;
use dojo_types::schema::{Struct, Ty};
use dojo_types::schema::Ty;
use futures::Stream;
use proto::world::{
MetadataRequest, MetadataResponse, RetrieveEntitiesRequest, RetrieveEntitiesResponse,
Expand All @@ -28,7 +27,7 @@ use tonic::transport::Server;
use tonic::{Request, Response, Status};
use torii_core::cache::ModelCache;
use torii_core::error::{Error, ParseError, QueryError};
use torii_core::model::build_sql_query;
use torii_core::model::{build_sql_query, map_row_to_ty};

use self::subscription::SubscribeRequest;
use crate::proto::types::clause::ClauseType;
Expand Down Expand Up @@ -132,11 +131,15 @@ impl DojoWorld {
let entity_query = format!("{} WHERE entities.id = ?", build_sql_query(&schemas)?);
let row = sqlx::query(&entity_query).bind(&entity_id).fetch_one(&self.pool).await?;

let mut models = Vec::with_capacity(schemas.len());
for schema in schemas {
let struct_ty = schema.as_struct().expect("schema should be struct");
models.push(Self::map_row_to_struct(&schema.name(), struct_ty, &row)?.into());
}
let models = schemas
.iter()
.map(|s| {
let mut struct_ty = s.as_struct().expect("schema should be struct").to_owned();
map_row_to_ty(&s.name(), &mut struct_ty, &row)?;

Ok(struct_ty.try_into().unwrap())
})
.collect::<Result<Vec<_>, Error>>()?;

let key = FieldElement::from_str(&entity_id).map_err(ParseError::FromStr)?;
entities.push(proto::types::Entity { key: key.to_bytes_be().to_vec(), models })
Expand Down Expand Up @@ -310,103 +313,14 @@ impl DojoWorld {
let models = schemas
.iter()
.map(|schema| {
let struct_ty = schema.as_struct().expect("schema should be struct");
Self::map_row_to_struct(&schema.name(), struct_ty, row).map(Into::into)
})
.collect::<Result<Vec<proto::types::Model>, Error>>()?;

Ok(proto::types::Entity { key: key.to_bytes_be().to_vec(), models })
}

/// Helper function to map Sqlite row to proto::types::Struct
// TODO: refactor this to use `map_row_to_ty` from core and implement Ty to protobuf conversion
fn map_row_to_struct(
path: &str,
struct_ty: &Struct,
row: &SqliteRow,
) -> Result<proto::types::Struct, Error> {
let children = struct_ty
.children
.iter()
.map(|member| {
let column_name = format!("{}.{}", path, member.name);
let name = member.name.clone();
let ty_type = match &member.ty {
Ty::Primitive(primitive) => {
let value_type = match primitive {
Primitive::Bool(_) => Some(proto::types::value::ValueType::BoolValue(
row.try_get::<bool, &str>(&column_name)?,
)),
Primitive::U8(_)
| Primitive::U16(_)
| Primitive::U32(_)
| Primitive::U64(_)
| Primitive::USize(_) => {
let value = row.try_get::<i64, &str>(&column_name)?;
Some(proto::types::value::ValueType::UintValue(value as u64))
}
Primitive::U128(_)
| Primitive::Felt252(_)
| Primitive::ClassHash(_)
| Primitive::ContractAddress(_) => {
let value = row.try_get::<String, &str>(&column_name)?;
let felt =
FieldElement::from_str(&value).map_err(ParseError::FromStr)?;
Some(proto::types::value::ValueType::ByteValue(
felt.to_bytes_be().to_vec(),
))
}
Primitive::U256(_) => {
let value = row.try_get::<String, &str>(&column_name)?;
Some(proto::types::value::ValueType::StringValue(value))
}
};

Some(proto::types::ty::TyType::Primitive(proto::types::Primitive {
value: Some(proto::types::Value { value_type }),
r#type: primitive.to_numeric() as i32,
}))
}
Ty::Enum(enum_ty) => {
let value = row.try_get::<String, &str>(&column_name)?;
let options = enum_ty
.options
.iter()
.map(|r#enum| proto::types::EnumOption {
name: r#enum.name.clone(),
ty: None,
})
.collect::<Vec<_>>();
let option =
options.iter().position(|o| o.name == value).expect("wrong enum value")
as u32;

Some(proto::types::ty::TyType::Enum(proto::types::Enum {
option,
options,
name: member.ty.name(),
}))
}
Ty::Struct(struct_ty) => {
let path = [path, &struct_ty.name].join("$");
Some(proto::types::ty::TyType::Struct(Self::map_row_to_struct(
&path, struct_ty, row,
)?))
}
ty => {
unimplemented!("unimplemented type_enum: {ty}");
}
};
let mut struct_ty = schema.as_struct().expect("schema should be struct").to_owned();
map_row_to_ty(&schema.name(), &mut struct_ty, row)?;

Ok(proto::types::Member {
name,
ty: Some(proto::types::Ty { ty_type }),
key: member.key,
})
Ok(struct_ty.try_into().unwrap())
})
.collect::<Result<Vec<proto::types::Member>, Error>>()?;
.collect::<Result<Vec<_>, Error>>()?;

Ok(proto::types::Struct { name: struct_ty.name.clone(), children })
Ok(proto::types::Entity { key: key.to_bytes_be().to_vec(), models })
}
}

Expand Down
162 changes: 2 additions & 160 deletions crates/torii/grpc/src/types.rs → crates/torii/grpc/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,16 @@ use std::collections::HashMap;
use std::str::FromStr;

use dojo_types::primitive::Primitive;
use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty};
use dojo_types::schema::Ty;
use serde::{Deserialize, Serialize};
use starknet::core::types::{
ContractStorageDiffItem, FromByteSliceError, FromStrError, StateDiff, StateUpdate, StorageEntry,
};
use starknet_crypto::FieldElement;

use crate::client::Error as ClientError;
use crate::proto::{self};

#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)]
pub struct Entity {
pub key: FieldElement,
pub models: Vec<Model>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)]
pub struct Model {
pub name: String,
pub members: Vec<Member>,
}
pub mod schema;

#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)]
pub struct Query {
Expand Down Expand Up @@ -170,34 +159,6 @@ impl TryFrom<proto::types::KeysClause> for KeysClause {
}
}

impl TryFrom<proto::types::Entity> for Entity {
type Error = ClientError;
fn try_from(entity: proto::types::Entity) -> Result<Self, Self::Error> {
Ok(Self {
key: FieldElement::from_byte_slice_be(&entity.key).map_err(ClientError::SliceError)?,
models: entity
.models
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()?,
})
}
}

impl TryFrom<proto::types::Model> for Model {
type Error = ClientError;
fn try_from(model: proto::types::Model) -> Result<Self, Self::Error> {
Ok(Self {
name: model.name,
members: model
.members
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()?,
})
}
}

impl From<MemberClause> for proto::types::MemberClause {
fn from(value: MemberClause) -> Self {
Self {
Expand Down Expand Up @@ -233,125 +194,6 @@ impl From<Value> for proto::types::Value {
}
}

impl From<proto::types::EnumOption> for EnumOption {
fn from(option: proto::types::EnumOption) -> Self {
EnumOption { name: option.name, ty: Ty::Tuple(vec![]) }
}
}

impl From<proto::types::Enum> for Enum {
fn from(r#enum: proto::types::Enum) -> Self {
Enum {
name: r#enum.name.clone(),
option: Some(r#enum.option as u8),
options: r#enum.options.into_iter().map(Into::into).collect::<Vec<_>>(),
}
}
}

impl TryFrom<proto::types::Struct> for Struct {
type Error = ClientError;
fn try_from(r#struct: proto::types::Struct) -> Result<Self, Self::Error> {
Ok(Struct {
name: r#struct.name,
children: r#struct
.children
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()?,
})
}
}

impl From<proto::types::Struct> for proto::types::Model {
fn from(r#struct: proto::types::Struct) -> Self {
Self { name: r#struct.name, members: r#struct.children }
}
}

// FIX: weird catch-22 issue - prost Enum has `try_from` trait we can use, however, using it results
// in wasm compile err about From<i32> missing. Implementing that trait results in clippy error
// about duplicate From<i32>... Workaround is to use deprecated `from_i32` and allow deprecation
// warning.
#[allow(deprecated)]
impl TryFrom<proto::types::Primitive> for Primitive {
type Error = ClientError;
fn try_from(primitive: proto::types::Primitive) -> Result<Self, Self::Error> {
let primitive_type = primitive.r#type;
let value_type = primitive
.value
.ok_or(ClientError::MissingExpectedData)?
.value_type
.ok_or(ClientError::MissingExpectedData)?;

let primitive = match &value_type {
proto::types::value::ValueType::BoolValue(bool) => Primitive::Bool(Some(*bool)),
proto::types::value::ValueType::UintValue(int) => {
match proto::types::PrimitiveType::from_i32(primitive_type) {
Some(proto::types::PrimitiveType::U8) => Primitive::U8(Some(*int as u8)),
Some(proto::types::PrimitiveType::U16) => Primitive::U16(Some(*int as u16)),
Some(proto::types::PrimitiveType::U32) => Primitive::U32(Some(*int as u32)),
Some(proto::types::PrimitiveType::U64) => Primitive::U64(Some(*int)),
Some(proto::types::PrimitiveType::Usize) => Primitive::USize(Some(*int as u32)),
_ => return Err(ClientError::UnsupportedType),
}
}
proto::types::value::ValueType::ByteValue(bytes) => {
match proto::types::PrimitiveType::from_i32(primitive_type) {
Some(proto::types::PrimitiveType::U128)
| Some(proto::types::PrimitiveType::Felt252)
| Some(proto::types::PrimitiveType::ClassHash)
| Some(proto::types::PrimitiveType::ContractAddress) => {
Primitive::Felt252(Some(
FieldElement::from_byte_slice_be(bytes)
.map_err(ClientError::SliceError)?,
))
}
_ => return Err(ClientError::UnsupportedType),
}
}
proto::types::value::ValueType::StringValue(_string) => {
match proto::types::PrimitiveType::from_i32(primitive_type) {
Some(proto::types::PrimitiveType::U256) => {
// TODO: Handle u256
Primitive::U256(None)
}
_ => return Err(ClientError::UnsupportedType),
}
}
_ => {
return Err(ClientError::UnsupportedType);
}
};

Ok(primitive)
}
}

impl TryFrom<proto::types::Ty> for Ty {
type Error = ClientError;
fn try_from(ty: proto::types::Ty) -> Result<Self, Self::Error> {
match ty.ty_type.ok_or(ClientError::MissingExpectedData)? {
proto::types::ty::TyType::Primitive(primitive) => {
Ok(Ty::Primitive(primitive.try_into()?))
}
proto::types::ty::TyType::Struct(r#struct) => Ok(Ty::Struct(r#struct.try_into()?)),
proto::types::ty::TyType::Enum(r#enum) => Ok(Ty::Enum(r#enum.into())),
}
}
}

impl TryFrom<proto::types::Member> for Member {
type Error = ClientError;
fn try_from(member: proto::types::Member) -> Result<Self, Self::Error> {
Ok(Member {
name: member.name,
ty: member.ty.ok_or(ClientError::MissingExpectedData)?.try_into()?,
key: member.key,
})
}
}

impl TryFrom<proto::types::StorageEntry> for StorageEntry {
type Error = FromStrError;
fn try_from(value: proto::types::StorageEntry) -> Result<Self, Self::Error> {
Expand Down
Loading

0 comments on commit 8b758b5

Please sign in to comment.