Skip to content

Commit

Permalink
improve dojo Model deserializing in from_values() functions
Browse files Browse the repository at this point in the history
  • Loading branch information
remybar committed Sep 16, 2024
1 parent f0d9827 commit 4c8a3c4
Show file tree
Hide file tree
Showing 19 changed files with 2,241 additions and 685 deletions.
15 changes: 7 additions & 8 deletions crates/dojo-core/src/model/metadata.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,9 @@ pub struct ResourceMetadata {

#[generate_trait]
pub impl ResourceMetadataImpl of ResourceMetadataTrait {
fn from_values(resource_id: felt252, ref values: Span<felt252>) -> ResourceMetadata {
let metadata_uri = Serde::<ByteArray>::deserialize(ref values);
if metadata_uri.is_none() {
panic!("Model `ResourceMetadata`: metadata_uri deserialization failed.");
}

ResourceMetadata { resource_id, metadata_uri: metadata_uri.unwrap() }
fn from_values(resource_id: felt252, ref values: Span<felt252>) -> Option<ResourceMetadata> {
let metadata_uri = Serde::<ByteArray>::deserialize(ref values)?;
Option::Some(ResourceMetadata { resource_id, metadata_uri })
}
}

Expand All @@ -50,7 +46,10 @@ pub impl ResourceMetadataModel of Model<ResourceMetadata> {
};

let mut values = world.entity(Self::selector(), ModelIndex::Keys(keys), Self::layout());
ResourceMetadataTrait::from_values(*keys.at(0), ref values)
match ResourceMetadataTrait::from_values(*keys.at(0), ref values) {
Option::Some(x) => x,
Option::None => { panic!("Model `ResourceMetadata`: deserialization failed.") }
}
}

fn set_model(self: @ResourceMetadata, world: IWorldDispatcher,) {
Expand Down
2 changes: 1 addition & 1 deletion crates/dojo-core/src/model/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub enum ModelIndex {
pub trait ModelEntity<T> {
fn id(self: @T) -> felt252;
fn values(self: @T) -> Span<felt252>;
fn from_values(entity_id: felt252, ref values: Span<felt252>) -> T;
fn from_values(entity_id: felt252, ref values: Span<felt252>) -> Option<T>;
// Get is always used with the trait path, which results in no ambiguity for the compiler.
fn get(world: IWorldDispatcher, entity_id: felt252) -> T;
// Update and delete can be used directly on the entity, which results in ambiguity.
Expand Down
6 changes: 4 additions & 2 deletions crates/dojo-core/src/tests/model/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,16 @@ fn test_from_values() {
let mut values = [3, 4].span();

let model_entity = ModelEntity::<FooEntity>::from_values(1, ref values);
assert!(model_entity.is_some());
let model_entity = model_entity.unwrap();
assert!(model_entity.__id == 1 && model_entity.v1 == 3 && model_entity.v2 == 4);
}

#[test]
#[should_panic(expected: "ModelEntity `FooEntity`: deserialization failed.")]
fn test_from_values_bad_data() {
let mut values = [3].span();
let _ = ModelEntity::<FooEntity>::from_values(1, ref values);
let res = ModelEntity::<FooEntity>::from_values(1, ref values);
assert!(res.is_none());
}

#[test]
Expand Down
5 changes: 4 additions & 1 deletion crates/dojo-core/src/world/world_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,10 @@ pub mod world {
Model::<ResourceMetadata>::layout()
);

ResourceMetadataTrait::from_values(resource_selector, ref values)
match ResourceMetadataTrait::from_values(resource_selector, ref values) {
Option::Some(x) => x,
Option::None => { panic!("Model `ResourceMetadata`: deserialization failed.") }
}
}

/// Sets the metadata of the resource.
Expand Down
30 changes: 9 additions & 21 deletions crates/dojo-lang/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,28 +282,16 @@ pub fn deserialize_keys_and_values(
///
/// * member: The member to serialize.
pub fn serialize_member_ty(member: &Member, with_self: bool, dest_name: &str) -> RewriteNode {
match member.ty.as_str() {
"felt252" => RewriteNode::Text(format!(
"core::array::ArrayTrait::append(ref {dest_name}, {}{});\n",
if with_self { "*self." } else { "" },
member.name
)),
_ => RewriteNode::Text(format!(
"core::serde::Serde::serialize({}{}, ref {dest_name});\n",
if with_self { "self." } else { "@" },
member.name
)),
}
RewriteNode::Text(format!(
"core::serde::Serde::serialize({}{}, ref {dest_name});\n",
if with_self { "self." } else { "@" },
member.name
))
}

pub fn deserialize_member_ty(member: &Member, input_name: &str) -> RewriteNode {
match member.ty.as_str() {
"felt252" => {
RewriteNode::Text(format!("let {} = {input_name}.pop_front()?;\n", member.name))
}
_ => RewriteNode::Text(format!(
"let {} = core::serde::Serde::<{}>::deserialize(ref {input_name})?;",
member.name, member.ty
)),
}
RewriteNode::Text(format!(
"let {} = core::serde::Serde::<{}>::deserialize(ref {input_name})?;\n",
member.name, member.ty
))
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
kind = "Class"
class_hash = "0x190f42b1a811d9e39dee85ae2129db5a592b4117cb318e6df4737a6033d9dac"
original_class_hash = "0x190f42b1a811d9e39dee85ae2129db5a592b4117cb318e6df4737a6033d9dac"
class_hash = "0x4743bf5aabbbbb43f658bd540ae7a0a2bb7f7ccce057a6cbb74be1ca56e3f57"
original_class_hash = "0x4743bf5aabbbbb43f658bd540ae7a0a2bb7f7ccce057a6cbb74be1ca56e3f57"
abi = "manifests/dev/base/abis/dojo-world.json"
tag = "dojo-world"
manifest_name = "dojo-world"
96 changes: 59 additions & 37 deletions crates/dojo-lang/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use dojo_world::manifest::Member;
use starknet::core::utils::get_selector_from_name;

use crate::data::{
compute_namespace, get_parameters, parse_members, serialize_keys_and_values,
serialize_member_ty, DEFAULT_DATA_VERSION,
compute_namespace, deserialize_keys_and_values, get_parameters, parse_members,
serialize_keys_and_values, serialize_member_ty, DEFAULT_DATA_VERSION,
};
use crate::plugin::{DojoAuxData, Model, DOJO_MODEL_ATTR};

Expand Down Expand Up @@ -70,15 +70,10 @@ pub fn handle_model_struct(
),
};

let mut members_values: Vec<RewriteNode> = vec![];
let mut param_keys: Vec<String> = vec![];
let members = parse_members(db, &struct_ast.members(db).elements(db), &mut diagnostics);

let mut serialized_keys: Vec<RewriteNode> = vec![];
let mut serialized_param_keys: Vec<RewriteNode> = vec![];
let mut serialized_values: Vec<RewriteNode> = vec![];
let mut field_accessors: Vec<RewriteNode> = vec![];
let mut entity_field_accessors: Vec<RewriteNode> = vec![];

let members = parse_members(db, &struct_ast.members(db).elements(db), &mut diagnostics);

serialize_keys_and_values(
&members,
Expand All @@ -104,17 +99,39 @@ pub fn handle_model_struct(
});
}

let mut deserialized_keys: Vec<RewriteNode> = vec![];
let mut deserialized_values: Vec<RewriteNode> = vec![];

deserialize_keys_and_values(
&members,
"keys",
&mut deserialized_keys,
"values",
&mut deserialized_values,
);

let mut member_key_names: Vec<RewriteNode> = vec![];
let mut member_value_names: Vec<RewriteNode> = vec![];
let mut members_values: Vec<RewriteNode> = vec![];
let mut param_keys: Vec<String> = vec![];
let mut serialized_param_keys: Vec<RewriteNode> = vec![];

members.iter().for_each(|member| {
if member.key {
param_keys.push(format!("{}: {}", member.name, member.ty));
serialized_param_keys.push(serialize_member_ty(member, false, "serialized"));
member_key_names.push(RewriteNode::Text(format!("{},\n", member.name.clone())));
} else {
members_values
.push(RewriteNode::Text(format!("pub {}: {},\n", member.name, member.ty)));
member_value_names.push(RewriteNode::Text(format!("{},\n", member.name.clone())));
}
});
let param_keys = param_keys.join(", ");

let mut field_accessors: Vec<RewriteNode> = vec![];
let mut entity_field_accessors: Vec<RewriteNode> = vec![];

members.iter().filter(|m| !m.key).for_each(|member| {
field_accessors.push(generate_field_accessors(
model_name.clone(),
Expand Down Expand Up @@ -165,22 +182,16 @@ pub impl $type_name$StoreImpl of $type_name$Store {
core::poseidon::poseidon_hash_span(serialized.span())
}
fn from_values(ref keys: Span<felt252>, ref values: Span<felt252>) -> $type_name$ {
let mut serialized = core::array::ArrayTrait::new();
serialized.append_span(keys);
serialized.append_span(values);
let mut serialized = core::array::ArrayTrait::span(@serialized);
let entity = core::serde::Serde::<$type_name$>::deserialize(ref serialized);
fn from_values(ref keys: Span<felt252>, ref values: Span<felt252>) -> Option<$type_name$> {
$deserialized_keys$
$deserialized_values$
if core::option::OptionTrait::<$type_name$>::is_none(@entity) {
panic!(
\"Model `$type_name$`: deserialization failed. Ensure the length of the keys tuple \
is matching the number of #[key] fields in the model struct.\"
);
}
core::option::OptionTrait::<$type_name$>::unwrap(entity)
Option::Some(
$type_name$ {
$member_key_names$
$member_value_names$
}
)
}
fn get(world: dojo::world::IWorldDispatcher, $param_keys$) -> $type_name$ {
Expand Down Expand Up @@ -212,18 +223,15 @@ pub impl $type_name$ModelEntityImpl of dojo::model::ModelEntity<$type_name$Entit
core::array::ArrayTrait::span(@serialized)
}
fn from_values(entity_id: felt252, ref values: Span<felt252>) -> $type_name$Entity {
let mut serialized = array![entity_id];
serialized.append_span(values);
let mut serialized = core::array::ArrayTrait::span(@serialized);
fn from_values(entity_id: felt252, ref values: Span<felt252>) -> Option<$type_name$Entity> {
$deserialized_values$
let entity_values = core::serde::Serde::<$type_name$Entity>::deserialize(ref serialized);
if core::option::OptionTrait::<$type_name$Entity>::is_none(@entity_values) {
panic!(
\"ModelEntity `$type_name$Entity`: deserialization failed.\"
);
}
core::option::OptionTrait::<$type_name$Entity>::unwrap(entity_values)
Option::Some(
$type_name$Entity {
__id: entity_id,
$member_value_names$
}
)
}
fn get(world: dojo::world::IWorldDispatcher, entity_id: felt252) -> $type_name$Entity {
Expand All @@ -233,7 +241,12 @@ pub impl $type_name$ModelEntityImpl of dojo::model::ModelEntity<$type_name$Entit
dojo::model::ModelIndex::Id(entity_id),
dojo::model::Model::<$type_name$>::layout()
);
Self::from_values(entity_id, ref values)
match Self::from_values(entity_id, ref values) {
Option::Some(x) => x,
Option::None => {
panic!(\"ModelEntity `$type_name$Entity`: deserialization failed.\")
}
}
}
fn update_entity(self: @$type_name$Entity, world: dojo::world::IWorldDispatcher) {
Expand Down Expand Up @@ -334,7 +347,12 @@ pub impl $type_name$ModelImpl of dojo::model::Model<$type_name$> {
);
let mut _keys = keys;
$type_name$Store::from_values(ref _keys, ref values)
match $type_name$Store::from_values(ref _keys, ref values) {
Option::Some(x) => x,
Option::None => {
panic!(\"Model `$type_name$`: deserialization failed.\")
}
}
}
fn set_model(
Expand Down Expand Up @@ -580,8 +598,12 @@ pub mod $contract_name$ {
&UnorderedHashMap::from([
("contract_name".to_string(), RewriteNode::Text(model_name.to_case(Case::Snake))),
("type_name".to_string(), RewriteNode::Text(model_name)),
("member_key_names".to_string(), RewriteNode::new_modified(member_key_names)),
("member_value_names".to_string(), RewriteNode::new_modified(member_value_names)),
("serialized_keys".to_string(), RewriteNode::new_modified(serialized_keys)),
("serialized_values".to_string(), RewriteNode::new_modified(serialized_values)),
("deserialized_keys".to_string(), RewriteNode::new_modified(deserialized_keys)),
("deserialized_values".to_string(), RewriteNode::new_modified(deserialized_values)),
("model_version".to_string(), model_version),
("model_selector".to_string(), model_selector),
("model_namespace".to_string(), RewriteNode::Text(model_namespace.clone())),
Expand Down
Loading

0 comments on commit 4c8a3c4

Please sign in to comment.