Skip to content

Commit

Permalink
feat: add get_[field_name] and set_[field_name] to models
Browse files Browse the repository at this point in the history
  • Loading branch information
remybar committed Jul 8, 2024
1 parent 52051d4 commit 82a2a16
Show file tree
Hide file tree
Showing 40 changed files with 2,953 additions and 180 deletions.
14 changes: 14 additions & 0 deletions crates/dojo-core/src/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ trait Model<T> {
world: IWorldDispatcher, keys: Span<felt252>, layout: dojo::database::introspect::Layout
);

fn entity_member(
world: IWorldDispatcher,
keys: Span<felt252>,
member_id: felt252,
layout: dojo::database::introspect::Layout
) -> Span<felt252>;

fn set_entity_member(
world: IWorldDispatcher,
keys: Span<felt252>,
member_id: felt252,
values: Span<felt252>,
layout: dojo::database::introspect::Layout
);

/// Returns the name of the model as it was written in Cairo code.
fn name() -> ByteArray;
Expand Down
22 changes: 22 additions & 0 deletions crates/dojo-core/src/resource_metadata.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ impl ResourceMetadataModel of dojo::model::Model<ResourceMetadata> {
dojo::world::IWorldDispatcherTrait::delete_entity(world, Self::selector(), keys, layout);
}

fn entity_member(
world: dojo::world::IWorldDispatcher,
keys: Span<felt252>,
member_id: felt252,
layout: dojo::database::introspect::Layout
) -> Span<felt252> {
dojo::world::IWorldDispatcherTrait::entity_member(
world, Self::selector(), keys, member_id, layout
)
}

fn set_entity_member(
world: dojo::world::IWorldDispatcher,
keys: Span<felt252>,
member_id: felt252,
values: Span<felt252>,
layout: dojo::database::introspect::Layout
) {
dojo::world::IWorldDispatcherTrait::set_entity_member(
world, Self::selector(), keys, member_id, values, layout
);
}

#[inline(always)]
fn name() -> ByteArray {
Expand Down
194 changes: 180 additions & 14 deletions crates/dojo-core/src/world.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,41 @@ trait IWorld<T> {
entity_id: felt252,
layout: dojo::database::introspect::Layout
);

fn entity_member(
self: @T,
model_id: felt252,
keys: Span<felt252>,
member_id: felt252,
layout: dojo::database::introspect::Layout
) -> Span<felt252>;

fn set_entity_member(
self: @T,
model_id: felt252,
keys: Span<felt252>,
member_id: felt252,
values: Span<felt252>,
layout: dojo::database::introspect::Layout
);

fn entity_member_by_id(
self: @T,
model_id: felt252,
entity_id: felt252,
member_id: felt252,
layout: dojo::database::introspect::Layout
) -> Span<felt252>;

fn set_entity_member_by_id(
self: @T,
model_id: felt252,
entity_id: felt252,
member_id: felt252,
values: Span<felt252>,
layout: dojo::database::introspect::Layout
);

fn base(self: @T) -> ClassHash;

/// In Dojo, there are 2 levels of authorization: `owner` and `writer`.
Expand Down Expand Up @@ -88,6 +123,8 @@ mod Errors {
const OWNER_ONLY_UPGRADE: felt252 = 'only owner can upgrade';
const OWNER_ONLY_UPDATE: felt252 = 'only owner can update';
const NAMESPACE_ALREADY_REGISTERED: felt252 = 'namespace already registered';
const NO_ACCESS_TO_MEMBERS: felt252 = 'cannot access to model members';
const INVALID_MEMBER_ID: felt252 = 'invalid member id';
const UNEXPECTED_ERROR: felt252 = 'unexpected error';
}

Expand Down Expand Up @@ -883,6 +920,74 @@ mod world {
self._read_model_entity(model_id, entity_id, layout)
}

///
fn entity_member(
self: @ContractState,
model_id: felt252,
keys: Span<felt252>,
member_id: felt252,
layout: dojo::database::introspect::Layout
) -> Span<felt252> {
let entity_id = entity_id_from_keys(keys);
self.entity_member_by_id(model_id, entity_id, member_id, layout)
}

///
fn set_entity_member(
self: @ContractState,
model_id: felt252,
keys: Span<felt252>,
member_id: felt252,
values: Span<felt252>,
layout: dojo::database::introspect::Layout
) {
let entity_id = entity_id_from_keys(keys);
self.set_entity_member_by_id(model_id, entity_id, member_id, values, layout);
}

///
fn entity_member_by_id(
self: @ContractState,
model_id: felt252,
entity_id: felt252,
member_id: felt252,
layout: dojo::database::introspect::Layout
) -> Span<felt252> {
match layout {
dojo::database::introspect::Layout::Struct(field_layouts) => {
let field_layout = SelfTrait::_find_member_layout(field_layouts, member_id);
match field_layout {
Option::Some(l) => self
._read_entity_member(model_id, entity_id, member_id, l),
Option::None => panic_with_felt252(Errors::INVALID_MEMBER_ID)
}
},
_ => panic_with_felt252(Errors::NO_ACCESS_TO_MEMBERS)
}
}

///
fn set_entity_member_by_id(
self: @ContractState,
model_id: felt252,
entity_id: felt252,
member_id: felt252,
values: Span<felt252>,
layout: dojo::database::introspect::Layout
) {
match layout {
dojo::database::introspect::Layout::Struct(field_layouts) => {
let field_layout = SelfTrait::_find_member_layout(field_layouts, member_id);
match field_layout {
Option::Some(l) => self
._write_entity_member(model_id, entity_id, member_id, values, l),
Option::None => panic_with_felt252(Errors::INVALID_MEMBER_ID)
};
},
_ => panic_with_felt252(Errors::NO_ACCESS_TO_MEMBERS)
};
}

/// Gets the base contract class hash.
///
/// # Returns
Expand Down Expand Up @@ -1080,6 +1185,16 @@ mod world {
|| self.is_account_writer(resource_id)
}

///
fn _can_access_model_members(
self: @ContractState, layout: dojo::database::introspect::Layout
) -> bool {
match layout {
dojo::database::introspect::Layout::Struct(_) => true,
_ => false
}
}

/// Write a new entity.
///
/// # Arguments
Expand Down Expand Up @@ -1157,9 +1272,60 @@ mod world {
read_data.span()
}

/// Compute the full field key from parent key and current field key.
fn _field_key(parent_key: felt252, field_key: felt252) -> felt252 {
poseidon::poseidon_hash_span(array![parent_key, field_key].span())
///
fn _read_entity_member(
self: @ContractState,
model_id: felt252,
entity_id: felt252,
member_id: felt252,
layout: dojo::database::introspect::Layout
) -> Span<felt252> {
let mut read_data = ArrayTrait::<felt252>::new();
Self::_read_layout(
model_id, Self::_combine_key(entity_id, member_id), ref read_data, layout
);

read_data.span()
}

///
fn _write_entity_member(
self: @ContractState,
model_id: felt252,
entity_id: felt252,
member_id: felt252,
values: Span<felt252>,
layout: dojo::database::introspect::Layout
) {
let mut offset = 0;
Self::_write_layout(
model_id, Self::_combine_key(entity_id, member_id), values, ref offset, layout
)
}

///
fn _find_member_layout(
field_layouts: Span<FieldLayout>, member_id: felt252
) -> Option<dojo::database::introspect::Layout> {
let mut i = 0;
let layout = loop {
if i >= field_layouts.len() {
break Option::None;
}

let field_layout = *field_layouts.at(i);
if field_layout.selector == member_id {
break Option::Some(field_layout.layout);
}
i += 1;
};

layout
}

/// Combine parent and child keys to build one full key.
fn _combine_key(parent_key: felt252, child_key: felt252) -> felt252 {
poseidon::poseidon_hash_span(array![parent_key, child_key].span())
}

/// Append some values to an array.
Expand Down Expand Up @@ -1280,7 +1446,7 @@ mod world {
if i >= array_len {
break;
}
let key = Self::_field_key(key, i.into());
let key = Self::_combine_key(key, i.into());

Self::_write_layout(model, key, values, ref offset, item_layout);

Expand Down Expand Up @@ -1338,7 +1504,7 @@ mod world {
}

let field_layout = *layout.at(i);
let field_key = Self::_field_key(key, field_layout.selector);
let field_key = Self::_combine_key(key, field_layout.selector);

Self::_write_layout(model, field_key, values, ref offset, field_layout.layout);

Expand Down Expand Up @@ -1368,7 +1534,7 @@ mod world {
}

let field_layout = *layout.at(i);
let key = Self::_field_key(key, i.into());
let key = Self::_combine_key(key, i.into());

Self::_write_layout(model, key, values, ref offset, field_layout);

Expand All @@ -1392,7 +1558,7 @@ mod world {
offset += 1;

// find the corresponding layout and then write the full variant
let variant_data_key = Self::_field_key(key, variant);
let variant_data_key = Self::_combine_key(key, variant);

match Self::_find_variant_layout(variant, variant_layouts) {
Option::Some(layout) => Self::_write_layout(
Expand Down Expand Up @@ -1467,7 +1633,7 @@ mod world {
}

let field_layout = *layout.at(i);
let key = Self::_field_key(key, field_layout.selector);
let key = Self::_combine_key(key, field_layout.selector);

Self::_delete_layout(model, key, field_layout.layout);

Expand All @@ -1489,7 +1655,7 @@ mod world {
}

let field_layout = *layout.at(i);
let key = Self::_field_key(key, i.into());
let key = Self::_combine_key(key, i.into());

Self::_delete_layout(model, key, field_layout);

Expand All @@ -1509,7 +1675,7 @@ mod world {
database::delete(model, key, array![251].span());

// find the corresponding layout and the delete the full variant
let variant_data_key = Self::_field_key(key, variant);
let variant_data_key = Self::_combine_key(key, variant);

match Self::_find_variant_layout(variant, variant_layouts) {
Option::Some(layout) => Self::_delete_layout(model, variant_data_key, layout),
Expand Down Expand Up @@ -1587,7 +1753,7 @@ mod world {
break;
}

let field_key = Self::_field_key(key, i.into());
let field_key = Self::_combine_key(key, i.into());
Self::_read_layout(model, field_key, ref read_data, item_layout);

i += 1;
Expand Down Expand Up @@ -1638,7 +1804,7 @@ mod world {
}

let field_layout = *layout.at(i);
let field_key = Self::_field_key(key, field_layout.selector);
let field_key = Self::_combine_key(key, field_layout.selector);

Self::_read_layout(model, field_key, ref read_data, field_layout.layout);

Expand All @@ -1663,7 +1829,7 @@ mod world {
}

let field_layout = *layout.at(i);
let field_key = Self::_field_key(key, i.into());
let field_key = Self::_combine_key(key, i.into());
Self::_read_layout(model, field_key, ref read_data, field_layout);

i += 1;
Expand All @@ -1686,7 +1852,7 @@ mod world {
read_data.append(variant);

// find the corresponding layout and the read the variant data
let variant_data_key = Self::_field_key(key, variant);
let variant_data_key = Self::_combine_key(key, variant);

match Self::_find_variant_layout(variant, variant_layouts) {
Option::Some(layout) => Self::_read_layout(
Expand Down
4 changes: 2 additions & 2 deletions crates/dojo-lang/src/introspect/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use cairo_lang_syntax::node::ast::{Expr, ItemEnum, ItemStruct, OptionTypeClause,
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::helpers::QueryAttrs;
use cairo_lang_syntax::node::{ids, Terminal, TypedSyntaxNode};
use starknet::core::utils::get_selector_from_name;
use dojo_world::contracts::naming::compute_bytearray_hash;

use super::utils::{
get_array_item_type, get_tuple_item_types, is_array, is_byte_array, is_tuple,
Expand All @@ -27,7 +27,7 @@ pub fn build_field_layouts(
}

let field_name = m.name(db).text(db);
let field_selector = get_selector_from_name(field_name.as_str()).unwrap().to_string();
let field_selector = compute_bytearray_hash(&field_name.to_string()).to_string();
let field_layout = get_layout_from_type_clause(db, diagnostics, &m.type_clause(db));
Some(format!(
"dojo::database::introspect::FieldLayout {{
Expand Down
Loading

0 comments on commit 82a2a16

Please sign in to comment.