diff --git a/crates/dojo-lang/src/introspect.rs b/crates/dojo-lang/src/introspect.rs index 5764e1e0c4..5ebccdb891 100644 --- a/crates/dojo-lang/src/introspect.rs +++ b/crates/dojo-lang/src/introspect.rs @@ -275,17 +275,7 @@ fn handle_introspect_internal( } }); - if size_precompute > 0 { - size.push(format!("{}", size_precompute)); - } - - if size.is_empty() { - panic!( - "The model `{}` has only keys, ensure you have at least one field without the #[key] \ - attribute.", - name - ); - } + size.push(format!("{}", size_precompute)); RewriteNode::interpolate_patched( " diff --git a/crates/dojo-lang/src/manifest_test_data/compiler_cairo_v240/Scarb.lock b/crates/dojo-lang/src/manifest_test_data/compiler_cairo_v240/Scarb.lock index e9d8135cc3..b0e7da2d12 100644 --- a/crates/dojo-lang/src/manifest_test_data/compiler_cairo_v240/Scarb.lock +++ b/crates/dojo-lang/src/manifest_test_data/compiler_cairo_v240/Scarb.lock @@ -10,7 +10,7 @@ dependencies = [ [[package]] name = "dojo" -version = "0.4.1" +version = "0.4.2" dependencies = [ "dojo_plugin", ] diff --git a/crates/dojo-lang/src/manifest_test_data/manifest b/crates/dojo-lang/src/manifest_test_data/manifest index db12a2953d..738e0ededb 100644 --- a/crates/dojo-lang/src/manifest_test_data/manifest +++ b/crates/dojo-lang/src/manifest_test_data/manifest @@ -1,5 +1,4 @@ //! > Test generated manifest file - //! > test_runner_name test_manifest_file @@ -1374,7 +1373,7 @@ test_manifest_file { "name": "dojo_examples::models::position", "address": null, - "class_hash": "0x3d9bc8581c7e9b7e234d4443b9959756771f923884d3f829b5ce1a0ae21c88", + "class_hash": "0x4cd20d231b04405a77b184c115dc60637e186504fad7f0929bd76cbd09c10b", "abi": [ { "type": "function", @@ -1542,4 +1541,4 @@ test_manifest_file } ], "models": [] -} +} \ No newline at end of file diff --git a/crates/dojo-lang/src/model.rs b/crates/dojo-lang/src/model.rs index d201b18587..3a42488add 100644 --- a/crates/dojo-lang/src/model.rs +++ b/crates/dojo-lang/src/model.rs @@ -38,7 +38,14 @@ pub fn handle_model_struct( if keys.is_empty() { diagnostics.push(PluginDiagnostic { - message: "Model must define atleast one #[key] attribute".into(), + message: "Model must define at least one #[key] attribute".into(), + stable_ptr: struct_ast.name(db).stable_ptr().untyped(), + }); + } + + if keys.len() == members.len() { + diagnostics.push(PluginDiagnostic { + message: "Model must define at least one member that is not a key".into(), stable_ptr: struct_ast.name(db).stable_ptr().untyped(), }); } diff --git a/crates/dojo-lang/src/plugin_test_data/introspect b/crates/dojo-lang/src/plugin_test_data/introspect index 15fd1b5f07..a76ee7edd1 100644 --- a/crates/dojo-lang/src/plugin_test_data/introspect +++ b/crates/dojo-lang/src/plugin_test_data/introspect @@ -423,7 +423,7 @@ impl GenericStructSerde, +core::traits::Destruct> o impl GenericStructIntrospect> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> usize { - dojo::database::introspect::Introspect::::size() + dojo::database::introspect::Introspect::::size() + 0 } #[inline(always)] diff --git a/crates/dojo-lang/src/plugin_test_data/model b/crates/dojo-lang/src/plugin_test_data/model index f524306fb1..bcaac79822 100644 --- a/crates/dojo-lang/src/plugin_test_data/model +++ b/crates/dojo-lang/src/plugin_test_data/model @@ -43,6 +43,12 @@ struct Roles { role_ids: Array } +#[derive(Model, Serde)] +struct OnlyKeyModel { + #[key] + id: felt252 +} + use starknet::ContractAddress; #[derive(Model, Copy, Drop, Serde)] @@ -520,11 +526,16 @@ mod player { } //! > expected_diagnostics -error: Model must define atleast one #[key] attribute +error: Model must define at least one #[key] attribute --> test_src/lib.cairo:36:8 struct Roles { ^***^ +error: Model must define at least one member that is not a key + --> test_src/lib.cairo:41:8 +struct OnlyKeyModel { + ^**********^ + error: Unsupported attribute. --> test_src/lib.cairo[Position]:73:13 #[starknet::contract] @@ -535,6 +546,11 @@ error: Unsupported attribute. #[starknet::contract] ^*******************^ +error: Unsupported attribute. + --> test_src/lib.cairo[OnlyKeyModel]:68:13 + #[starknet::contract] + ^*******************^ + error: Unsupported attribute. --> test_src/lib.cairo[Player]:77:13 #[starknet::contract] @@ -600,6 +616,36 @@ error: Unsupported attribute. #[external(v0)] ^*************^ +error: Unsupported attribute. + --> test_src/lib.cairo[OnlyKeyModel]:72:17 + #[storage] + ^********^ + +error: Unsupported attribute. + --> test_src/lib.cairo[OnlyKeyModel]:75:17 + #[external(v0)] + ^*************^ + +error: Unsupported attribute. + --> test_src/lib.cairo[OnlyKeyModel]:80:17 + #[external(v0)] + ^*************^ + +error: Unsupported attribute. + --> test_src/lib.cairo[OnlyKeyModel]:85:17 + #[external(v0)] + ^*************^ + +error: Unsupported attribute. + --> test_src/lib.cairo[OnlyKeyModel]:93:17 + #[external(v0)] + ^*************^ + +error: Unsupported attribute. + --> test_src/lib.cairo[OnlyKeyModel]:100:17 + #[external(v0)] + ^*************^ + error: Unsupported attribute. --> test_src/lib.cairo[Player]:81:17 #[storage] @@ -670,6 +716,12 @@ struct Roles { role_ids: Array } +#[derive(Model, Serde)] +struct OnlyKeyModel { + #[key] + id: felt252 +} + use starknet::ContractAddress; #[derive(Model, Copy, Drop, Serde)] @@ -786,7 +838,7 @@ impl PositionSerde of core::serde::Serde:: { impl PositionIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> usize { - dojo::database::introspect::Introspect::::size() + dojo::database::introspect::Introspect::::size() + 0 } #[inline(always)] @@ -905,7 +957,7 @@ impl RolesSerde of core::serde::Serde:: { impl RolesIntrospect<> of dojo::database::introspect::Introspect> { #[inline(always)] fn size() -> usize { - dojo::database::introspect::Introspect::>::size() + dojo::database::introspect::Introspect::>::size() + 0 } #[inline(always)] @@ -971,6 +1023,120 @@ impl RolesIntrospect<> of dojo::database::introspect::Introspect> { dojo::database::introspect::Introspect::::ty() } } +impl OnlyKeyModelSerde of core::serde::Serde:: { + fn serialize(self: @OnlyKeyModel, ref output: core::array::Array) { + core::serde::Serde::serialize(self.id, ref output) + } + fn deserialize(ref serialized: core::array::Span) -> core::option::Option { + core::option::Option::Some(OnlyKeyModel { + id: core::serde::Serde::deserialize(ref serialized)?, + }) + } +} + + impl OnlyKeyModelModel of dojo::model::Model { + #[inline(always)] + fn name(self: @OnlyKeyModel) -> felt252 { + 'OnlyKeyModel' + } + + #[inline(always)] + fn keys(self: @OnlyKeyModel) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + core::array::ArrayTrait::append(ref serialized, *self.id); + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn values(self: @OnlyKeyModel) -> Span { + let mut serialized = core::array::ArrayTrait::new(); + + core::array::ArrayTrait::span(@serialized) + } + + #[inline(always)] + fn layout(self: @OnlyKeyModel) -> Span { + let mut layout = core::array::ArrayTrait::new(); + dojo::database::introspect::Introspect::::layout(ref layout); + core::array::ArrayTrait::span(@layout) + } + + #[inline(always)] + fn packed_size(self: @OnlyKeyModel) -> usize { + let mut layout = self.layout(); + dojo::packing::calculate_packed_size(ref layout) + } + } + + +impl OnlyKeyModelIntrospect<> of dojo::database::introspect::Introspect> { + #[inline(always)] + fn size() -> usize { + 0 + } + + #[inline(always)] + fn layout(ref layout: Array) { + + } + + #[inline(always)] + fn ty() -> dojo::database::introspect::Ty { + dojo::database::introspect::Ty::Struct(dojo::database::introspect::Struct { + name: 'OnlyKeyModel', + attrs: array![].span(), + children: array![dojo::database::introspect::serialize_member(@dojo::database::introspect::Member { + name: 'id', + ty: dojo::database::introspect::Ty::Primitive('felt252'), + attrs: array!['key'].span() + })].span() + }) + } +} + + + #[starknet::interface] + trait IOnlyKeyModel { + fn name(self: @T) -> felt252; + } + + #[starknet::contract] + mod only_key_model { + use super::OnlyKeyModel; + + #[storage] + struct Storage {} + + #[external(v0)] + fn name(self: @ContractState) -> felt252 { + 'OnlyKeyModel' + } + + #[external(v0)] + fn unpacked_size(self: @ContractState) -> usize { + dojo::database::introspect::Introspect::::size() + } + + #[external(v0)] + fn packed_size(self: @ContractState) -> usize { + let mut layout = core::array::ArrayTrait::new(); + dojo::database::introspect::Introspect::::layout(ref layout); + let mut layout_span = layout.span(); + dojo::packing::calculate_packed_size(ref layout_span) + } + + #[external(v0)] + fn layout(self: @ContractState) -> Span { + let mut layout = core::array::ArrayTrait::new(); + dojo::database::introspect::Introspect::::layout(ref layout); + core::array::ArrayTrait::span(@layout) + } + + #[external(v0)] + fn schema(self: @ContractState) -> dojo::database::introspect::Ty { + dojo::database::introspect::Introspect::::ty() + } + } impl PlayerCopy of core::traits::Copy::; impl PlayerDrop of core::traits::Drop::; impl PlayerSerde of core::serde::Serde:: { diff --git a/crates/dojo-world/src/contracts/model_test.rs b/crates/dojo-world/src/contracts/model_test.rs index efab444182..ce03fd3f3a 100644 --- a/crates/dojo-world/src/contracts/model_test.rs +++ b/crates/dojo-world/src/contracts/model_test.rs @@ -63,7 +63,7 @@ async fn test_model() { assert_eq!( position.class_hash(), FieldElement::from_hex_be( - "0x02b233bba9a232a5e891c85eca9f67beedca7a12f9768729ff017bcb62d25c9d" + "0x004cd20d231b04405a77b184c115dc60637e186504fad7f0929bd76cbd09c10b" ) .unwrap() );