Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model ptr to stuct #2612

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/dojo/core-cairo-test/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ mod tests {
mod event;
}

// mod model {
// mod model;
// }
mod model {
mod model;
}

mod storage {
mod database;
Expand Down
113 changes: 50 additions & 63 deletions crates/dojo/core-cairo-test/src/tests/model/model.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use dojo::model::{Model, ModelValue, ModelStorage, ModelValueStorage, ModelMemberStorage};
use dojo::world::{IWorldDispatcherTrait, WorldStorageTrait, WorldStorage};

use crate::tests::helpers::{deploy_world};
use dojo::model::{Model, ModelValue, ModelStorage, ModelValueStorage};
use dojo::world::WorldStorage;
use dojo_cairo_test::{spawn_test_world, NamespaceDef, TestResource};

#[derive(Copy, Drop, Serde, Debug)]
#[dojo::model]
Expand All @@ -26,6 +25,19 @@ struct Foo2 {
v2: u32
}

fn namespace_def() -> NamespaceDef {
NamespaceDef {
namespace: "dojo_cairo_test", resources: [
TestResource::Model(m_Foo::TEST_CLASS_HASH.try_into().unwrap()),
TestResource::Model(m_Foo2::TEST_CLASS_HASH.try_into().unwrap()),
].span()
}
}

fn spawn_foo_world() -> WorldStorage {
spawn_test_world([namespace_def()].span())
}

#[test]
fn test_model_definition() {
let definition = dojo::model::Model::<Foo>::definition();
Expand Down Expand Up @@ -65,73 +77,61 @@ fn test_from_values_bad_data() {
}

#[test]
fn test_get_and_update_model_value() {
let world = deploy_world();
world.register_model("dojo", foo::TEST_CLASS_HASH.try_into().unwrap());

let mut world = WorldStorageTrait::new(world, "dojo");
fn test_read_and_update_model_value() {
let mut world = spawn_foo_world();

let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
world.write_model(@foo);

let entity_id = foo.entity_id();
let mut model_value: FooValue = world.read_model_value(foo.key());
let mut model_value: FooValue = world.read_value(foo.key());
assert_eq!(model_value.v1, foo.v1);
assert_eq!(model_value.v2, foo.v2);

model_value.v1 = 12;
model_value.v2 = 18;

world.write_model_value_from_id(entity_id, @model_value);
world.write_value_from_id(entity_id, @model_value);

let read_values: FooValue = world.read_model_value(foo.key());
let read_values: FooValue = world.read_value(foo.key());
assert!(read_values.v1 == model_value.v1 && read_values.v2 == model_value.v2);
}

#[test]
fn test_delete_model_value() {
let world = deploy_world();
world.register_model("dojo", foo::TEST_CLASS_HASH.try_into().unwrap());

let mut world = WorldStorageTrait::new(world, "dojo");
let mut world = spawn_foo_world();

let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
world.write_model(@foo);

let entity_id = foo.entity_id();
ModelStorage::<WorldStorage, Foo>::erase_model(ref world, @foo);

let read_values: FooValue = world.read_model_value_from_id(entity_id);
let read_values: FooValue = world.read_value_from_id(entity_id);
assert!(read_values.v1 == 0 && read_values.v2 == 0);
}

#[test]
fn test_get_and_set_field_name() {
let world = deploy_world();
world.register_model("dojo", foo::TEST_CLASS_HASH.try_into().unwrap());

let mut world = WorldStorageTrait::new(world, "dojo");
fn test_read_and_write_field_name() {
let mut world = spawn_foo_world();

let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
world.write_model(@foo);

// Inference fails here, we need something better without too generics
// which also fails.
let v1 = world.read_member(foo.key(), selector!("v1"));
let v1 = world.read_member(foo.ptr(), selector!("v1"));
assert!(foo.v1 == v1);

world.write_member_from_id(foo.entity_id(), selector!("v1"), 42);
world.write_member(foo.ptr(), selector!("v1"), 42);

let v1 = world.read_member_from_id(foo.key(), selector!("v1"));
let v1 = world.read_member(foo.ptr(), selector!("v1"));
assert!(v1 == 42);
}

#[test]
fn test_get_and_set_from_model() {
let world = deploy_world();
world.register_model("dojo", foo::TEST_CLASS_HASH.try_into().unwrap());

let mut world = WorldStorageTrait::new(world, "dojo");
fn test_read_and_write_from_model() {
let mut world = spawn_foo_world();

let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
world.write_model(@foo);
Expand All @@ -143,10 +143,7 @@ fn test_get_and_set_from_model() {

#[test]
fn test_delete_from_model() {
let world = deploy_world();
world.register_model("dojo", foo::TEST_CLASS_HASH.try_into().unwrap());

let mut world = WorldStorageTrait::new(world, "dojo");
let mut world = spawn_foo_world();

let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
world.write_model(@foo);
Expand All @@ -157,41 +154,31 @@ fn test_delete_from_model() {
}

#[test]
fn test_get_and_set_member_from_model() {
let world = deploy_world();
world.register_model("dojo", foo::TEST_CLASS_HASH.try_into().unwrap());

let mut world = WorldStorageTrait::new(world, "dojo");

fn test_model_ptr_from_key() {
let mut world = spawn_foo_world();
let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
let ptr = Model::<Foo>::ptr_from_key(foo.key());
world.write_model(@foo);

let key: (u8, felt252) = foo.key();
let v1: u128 = world.read_member(key, selector!("v1"));

assert!(v1 == 3);

world.write_member(key, selector!("v1"), 42);
let foo: Foo = world.read_model(key);
assert!(foo.v1 == 42);
let v1 = world.read_member(ptr, selector!("v1"));
assert!(foo.v1 == v1);
}

#[test]
fn test_get_and_set_field_name_from_model() {
let world = deploy_world();
world.register_model("dojo", foo::TEST_CLASS_HASH.try_into().unwrap());

let mut world = WorldStorageTrait::new(world, "dojo");

fn test_model_ptr_from_keys() {
let mut world = spawn_foo_world();
let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
let ptr = Model::<Foo>::ptr_from_keys(foo.keys());
world.write_model(@foo);
let v1 = world.read_member(ptr, selector!("v1"));
assert!(foo.v1 == v1);
}

// Currently we don't have automatic field id computation. To be done.
// @remy/@ben.

let v1 = world.read_member((foo.k1, foo.k2), selector!("v1"));
assert!(v1 == 3);

world.write_member((foo.k1, foo.k2), selector!("v1"), 42);
assert!(v1 == 42);
#[test]
fn test_model_ptr_from_entity_id() {
let mut world = spawn_foo_world();
let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
let ptr = Model::<Foo>::ptr_from_id(foo.entity_id());
world.write_model(@foo);
let v1 = world.read_member(ptr, selector!("v1"));
assert!(foo.v1 == v1);
}
6 changes: 2 additions & 4 deletions crates/dojo/core/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub mod model {
pub use definition::{ModelIndex, ModelDefinition, ModelDef};

pub mod model;
pub use model::{Model, KeyParser};
pub use model::{Model, KeyParser, ModelPtr};

pub mod model_value;
pub use model_value::{ModelValue, ModelValueKey};
Expand All @@ -46,9 +46,7 @@ pub mod model {
pub use metadata::ResourceMetadata;

pub mod storage;
pub use storage::{
ModelStorage, ModelStorageTest, ModelValueStorage, ModelValueStorageTest, ModelPtr,
};
pub use storage::{ModelStorage, ModelStorageTest, ModelValueStorage, ModelValueStorageTest,};

#[cfg(target: "test")]
pub use model::{ModelTest};
Expand Down
40 changes: 37 additions & 3 deletions crates/dojo/core/src/model/model.cairo
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
use dojo::{meta::{Layout, introspect::Ty, layout::compute_packed_size}, utils::entity_id_from_keys};

use dojo::{
meta::{Layout, introspect::Ty, layout::compute_packed_size},
utils::{entity_id_from_keys, find_model_field_layout, entity_id_from_key}
};
use super::{ModelDefinition, ModelDef};

/// Trait `KeyParser` defines a trait for parsing keys from a given model.
///
/// A pointer to a model, which can be expressed by an entity id.
#[derive(Copy, Drop, Serde, Debug, PartialEq)]
pub struct ModelPtr<M> {
pub id: felt252,
}

pub trait KeyParser<M, K> {
/// Parses the key from the given model.
fn parse_key(self: @M) -> K;
Expand Down Expand Up @@ -39,6 +47,8 @@ pub trait Model<M> {
fn schema() -> Ty;
/// Returns the memory layout of the model.
fn layout() -> Layout;
/// Returns the layout of a field in the model.
fn field_layout(field_selector: felt252) -> Option<Layout>;
/// Returns the unpacked size of the model. Only applicable for fixed size models.
fn unpacked_size() -> Option<usize>;
/// Returns the packed size of the model. Only applicable for fixed size models.
Expand All @@ -49,6 +59,14 @@ pub trait Model<M> {
fn definition() -> ModelDef;
/// Returns the selector of the model computed for the given namespace hash.
fn selector(namespace_hash: felt252) -> felt252;
/// Returns the pointer to the model from the key.
fn ptr_from_key<K, +Serde<K>, +Drop<K>>(key: K) -> ModelPtr<M>;
/// Returns the pointer to the model from the keys.
fn ptr_from_keys(keys: Span<felt252>) -> ModelPtr<M>;
/// Returns the pointer to the model from the entity id.
fn ptr_from_id(entity_id: felt252) -> ModelPtr<M>;
/// Returns the ptr of the model.
fn ptr(self: @M) -> ModelPtr<M>;
}

pub impl ModelImpl<M, +ModelParser<M>, +ModelDefinition<M>, +Serde<M>> of Model<M> {
Expand Down Expand Up @@ -92,6 +110,10 @@ pub impl ModelImpl<M, +ModelParser<M>, +ModelDefinition<M>, +Serde<M>> of Model<
ModelDefinition::<M>::layout()
}

fn field_layout(field_selector: felt252) -> Option<Layout> {
find_model_field_layout(Self::layout(), field_selector)
}

fn schema() -> Ty {
ModelDefinition::<M>::schema()
}
Expand All @@ -118,6 +140,18 @@ pub impl ModelImpl<M, +ModelParser<M>, +ModelDefinition<M>, +Serde<M>> of Model<
unpacked_size: Self::unpacked_size()
}
}
fn ptr_from_key<K, +Serde<K>, +Drop<K>>(key: K) -> ModelPtr<M> {
ModelPtr { id: entity_id_from_key(@key) }
}
fn ptr_from_keys(keys: Span<felt252>) -> ModelPtr<M> {
ModelPtr { id: entity_id_from_keys(keys) }
}
fn ptr_from_id(entity_id: felt252) -> ModelPtr<M> {
ModelPtr::<M> { id: entity_id }
}
fn ptr(self: @M) -> ModelPtr<M> {
ModelPtr::<M> { id: self.entity_id() }
}
}

/// The `ModelTest` trait.
Expand Down
22 changes: 7 additions & 15 deletions crates/dojo/core/src/model/storage.cairo
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
use dojo::model::model_value::ModelValueKey;
use dojo::{model::{ModelPtr, model_value::ModelValueKey}};

// TODO: define the right interface for member accesses.

/// A pointer to a model, which can be expressed by an entity id.
/// It's different from `ModelIndex` which is used for low level accesses.
#[derive(Copy, Drop, Serde, Debug, PartialEq)]
pub enum ModelPtr<M> {
// The id of the model.
Id: felt252,
// The keys of the model as span.
Keys: Span<felt252>,
}

/// A `ModelStorage` trait that abstracts where the storage is.
///
/// Currently it's only world storage, but this will be useful when we have other
/// storage solutions (micro worlds).
pub trait ModelStorage<S, M> {
/// Sets a model of type `M`.
fn write_model(ref self: S, model: @M);

/// Retrieves a model of type `M` using the provided key of type `K`.
fn read_model<K, +Drop<K>, +Serde<K>>(self: @S, key: K) -> M;

/// Deletes a model of type `M`.
fn erase_model(ref self: S, model: @M);

/// Deletes a model of type `M` using the provided entity id.
/// The ptr is mostly used for type inferrence.
fn erase_model_ptr(ref self: S, ptr: ModelPtr<M>);

/// Retrieves a model of type `M` using the provided entity idref .
fn read_member<T, +Serde<T>>(self: @S, ptr: ModelPtr<M>, field_selector: felt252) -> T;
/// Retrieves a model of type `M` using the provided entity id.
fn write_member<T, +Serde<T>, +Drop<T>>(
ref self: S, ptr: ModelPtr<M>, field_selector: felt252, value: T
);
/// Returns the current namespace hash.
fn namespace_hash(self: @S) -> felt252;
}
Expand Down
Loading