Skip to content

fix(core): introduce DojoStore trait to handle some specific storage cases #3051

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

Closed
wants to merge 1 commit into from

Conversation

remybar
Copy link
Collaborator

@remybar remybar commented Feb 19, 2025

Description

In the current Dojo version, Option<T> and custom enums are not correctly stored in the world storage, because the first variant id is 0 and cannot be differenciated from an uninitialized variant.

To fix that, this PR introduces the DojoStore trait.

  • Every data structure implement the DojoStore trait which, by default, encapsulates Serde calls.
  • The implementation of the DojoStore trait for custom enums and Option<T> handles the variant index incrementing and returns Default::default() as default value during deserialization if the variant index is 0. That means, each custom enums must implement the Default trait.

As these changes may break the world storage, we also introduce the DojoLegacyStorage derive attribute. This attribute must be used on models for which we want to keep the current storage behavior. So, by default, model (de)serialization is done throughDojoStore. If DojoLegacyStorage is set, model (de)serialization is done through Serde.

Tests

  • Yes
  • No, because they aren't needed
  • No, because I need help

Added to documentation?

  • README.md
  • Dojo Book
  • No documentation needed

Checklist

  • I've formatted my code (scripts/prettier.sh, scripts/rust_fmt.sh, scripts/cairo_fmt.sh)
  • I've linted my code (scripts/clippy.sh, scripts/docs.sh)
  • I've commented my code
  • I've requested a review after addressing the comments

Summary by CodeRabbit

  • New Features

    • Added support for distinguishing between legacy and new storage systems, including querying legacy storage usage in models and contracts.
    • Introduced a new serialization and deserialization trait (DojoStore) for efficient data handling in world storage.
    • Added derive macros and utilities for legacy storage compatibility and enhanced default value handling for enums and structs.
  • Improvements

    • Enhanced model and event macros to support legacy storage with improved diagnostics and generic handling.
    • Updated formatting and printing functions to reflect storage type and improve data representation, especially for enums.
    • Expanded introspection and layout utilities to support conversion between new and legacy storage formats.
    • Refined storage layout validation and error messages for better diagnostics.
  • Bug Fixes

    • Improved error handling and messages for data layout and serialization edge cases.
  • Tests

    • Added extensive tests covering serialization, deserialization, and storage layouts for primitive, composite, generic, and nested data types.
  • Chores

    • Updated dependencies and configuration files to enhance development experience and compatibility.
    • Removed deprecated module and refactored serialization utilities for clarity and maintainability.

@remybar remybar force-pushed the fix-enum_options_storage branch 3 times, most recently from a4a6f88 to 7026e34 Compare March 3, 2025 21:11
@remybar remybar marked this pull request as ready for review March 4, 2025 20:30
Copy link
Contributor

coderabbitai bot commented Mar 4, 2025

Walkthrough

Ohayo sensei! This update introduces a comprehensive overhaul to Dojo's storage serialization and deserialization, adding the new DojoStore trait for handling model data, alongside legacy storage compatibility. The derive macros and code generation for models and events are refactored to support both legacy and new storage systems. Numerous tests are added to validate storage layouts and serialization logic. The use_legacy_storage method is now available across the stack, from core traits to contract bindings and CLI utilities, allowing dynamic handling of storage formats. Several enums and structs across the codebase now derive or implement Default, and utility modules are introduced or expanded for code reuse and diagnostics.

Changes

File(s) / Path(s) Change Summary
Cargo.toml Updated scarb, scarb-metadata, and scarb-ui dependencies to point to a different GitHub repository and commit.
crates/benches/contracts/src/models/character.cairo Added new game-related model structs and enums representing characters, abilities, stats, weapons, cases, and aliases.
crates/dojo/core-cairo-test/src/lib.cairo Added new test submodules: layout, dojo_store, and layout under tests::meta and tests::storage.
crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo Added use_legacy_storage method returning false to model implementation.
crates/dojo/core-cairo-test/src/tests/helpers/event.cairo,
crates/dojo/core-cairo-test/src/tests/helpers/model.cairo,
crates/dojo/core-cairo-test/src/tests/world/event.cairo,
crates/dojo/core-cairo-test/src/tests/world/model.cairo,
examples/spawn-and-move/src/models.cairo
Added or updated Default derives for enums and marked default variants explicitly.
crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo Added Default derives, new use_legacy_storage method, and updated enum/struct derives for testing helpers.
crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo Renamed generic struct, added generic enum, changed enum variant indexing to 1-based, and added/renamed introspection tests.
crates/dojo/core-cairo-test/src/tests/meta/layout.cairo Added comprehensive unit tests for legacy layout building covering options, enums, tuples, arrays, structs, and nested types.
crates/dojo/core-cairo-test/src/tests/model/model.cairo Added new model enums and structs, tests for legacy and new storage layouts, and serialization/deserialization checks.
crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo Added extensive tests for DojoStore serialization/deserialization of primitives, arrays, options, enums, structs, generics, and complex nested types.
crates/dojo/core-cairo-test/src/tests/storage/layout.cairo Added comprehensive tests for storage layout operations including fixed, tuple, byte array, array, struct, enum layouts, and error handling.
crates/dojo/core-cairo-test/src/tests/world/storage.cairo Updated assertions for option and enum fields in world storage tests.
crates/dojo/core/src/event/component.cairo Wrapped event layout in build_legacy_layout for legacy compatibility, with comments on event storage.
crates/dojo/core/src/lib.cairo Added dojo_store submodule and publicly re-exported ContractAddressDefault and DojoStore.
crates/dojo/core/src/meta/introspect.cairo Changed Option variant selectors from 0/1 to 1/2 to align with new storage indexing.
crates/dojo/core/src/meta/layout.cairo Added build_legacy_layout function to convert new Dojo storage layouts back to legacy format by adjusting variant selectors.
crates/dojo/core/src/model/component.cairo Added use_legacy_storage method to model interface implementation delegating to model definition.
crates/dojo/core/src/model/definition.cairo Added use_legacy_storage method to ModelDefinition trait.
crates/dojo/core/src/model/interface.cairo Added use_legacy_storage method to IModel trait.
crates/dojo/core/src/model/model.cairo Added deserialize and use_legacy_storage methods to model traits; updated deserialization to use ModelParser.
crates/dojo/core/src/model/model_value.cairo Added deserialize method to ModelValueParser trait; updated deserialization logic accordingly.
crates/dojo/core/src/storage/dojo_store.cairo New file: Defined DojoStore trait with serialization/deserialization methods and implementations for primitives, options, arrays, spans, and contract types.
crates/dojo/core/src/storage/layout.cairo Introduced MAX_VARIANT_INDEX constant; improved error handling and diagnostics for enum layouts.
crates/dojo/core/src/world/world_contract.cairo Refined layout compatibility check for resource upgrades to panic only if fixed layouts differ.
crates/dojo/lang/Cargo.toml Added cairo-lang-parser as a dev-dependency.
crates/dojo/lang/src/attribute_macros/element.rs Removed file: Element serialization and member parsing logic moved to utils.rs.
crates/dojo/lang/src/attribute_macros/event.rs Updated imports and serialization logic; added check to reject generic event structs; updated serialization call arguments.
crates/dojo/lang/src/attribute_macros/mod.rs Removed element module from public interface.
crates/dojo/lang/src/attribute_macros/model.rs Disallowed generics in models; refactored member parsing; added legacy storage handling; updated serialization/deserialization code generation.
crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo Added deserialize method to event model parser implementation.
crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo Added use_legacy_storage and deserialize methods; updated deserialization path; added DojoStore impl for model value.
crates/dojo/lang/src/cairo_plugin.rs Added DOJO_LEGACY_STORAGE_DERIVE to recognized derive macros.
crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs New file: Added code generation for DojoStore trait implementations for structs and enums.
crates/dojo/lang/src/derive_macros/introspect/layout.rs Changed enum variant selectors to 1-based; disallowed packing Option<T> types.
crates/dojo/lang/src/derive_macros/introspect/mod.rs Refactored generic handling; added DojoStore code generation; improved modularity and clarity.
crates/dojo/lang/src/derive_macros/introspect/utils.rs Added utility functions is_option and clean_ty for type detection and cleaning.
crates/dojo/lang/src/derive_macros/mod.rs Added DOJO_LEGACY_STORAGE_DERIVE constant.
crates/dojo/lang/src/lib.rs Added utils module and debug_store_expand function for debugging DojoStore code generation.
crates/dojo/lang/src/utils.rs New file: Added utilities for serialization/deserialization, member parsing, tuple destructuring, and diagnostics.
crates/dojo/world/src/contracts/abigen/model.rs Added use_legacy_storage method to contract and reader bindings.
crates/dojo/world/src/contracts/model.rs Added async use_legacy_storage method to ModelReader trait and implementation.
crates/sozo/ops/src/model.rs Added use_legacy_storage parameter to formatting functions; updated logic to handle legacy vs new storage when printing enums and layouts.
examples/spawn-and-move/dojo_dev.toml Updated world_address value.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Model (Contract)
    participant DojoStore
    participant LegacyStore
    participant CLI/SDK

    User->>CLI/SDK: Query or update model data
    CLI/SDK->>Model: Call use_legacy_storage()
    Model-->>CLI/SDK: Returns true (legacy) or false (new)
    CLI/SDK->>DojoStore: (if false) Serialize/deserialize using DojoStore
    CLI/SDK->>LegacyStore: (if true) Serialize/deserialize using legacy logic
    DojoStore-->>CLI/SDK: Data in new format
    LegacyStore-->>CLI/SDK: Data in legacy format
    CLI/SDK-->>User: Presents/updates formatted data
Loading

Possibly related PRs

  • feat: batch entities #2633: The main PR updates the Cargo.toml dependency sources for scarb and related crates to a different Git repository and commit, while the retrieved PR modifies Cargo.toml dependencies for scarb and scarb-ui to point to a different branch in the dojoengine repository; both involve changes to the same dependencies in Cargo.toml but differ in repository URLs and versions, indicating they are related changes to dependency references.

Suggested labels

contributor

Suggested reviewers

  • glihm

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (14)
crates/dojo/lang/src/derive_macros/mod.rs (1)

24-24: Consider adding proper documentation for the new derive attribute.

While the constant itself is clear, adding documentation about its purpose and usage would help users understand when and why to use the DojoLegacyStorage derive attribute.

 pub const DOJO_LEGACY_STORAGE_DERIVE: &str = "DojoLegacyStorage";
+
+/// The DojoLegacyStorage derive allows models to retain the current Serde-based storage behavior
+/// instead of using the new DojoStore trait. This is useful for maintaining compatibility with
+/// existing world storage during the transition period.
crates/dojo/core/src/storage/layout.cairo (1)

192-192: Enhanced error message with variant details

The improved error message includes the variant value, which will make debugging much easier when an invalid variant is encountered.

crates/dojo/lang/src/lib.rs (1)

44-53: Excellent debugging function for DojoStore implementation

Ohayo! The new debug_store_expand function looks good and follows the same pattern as the existing debug_expand function. This will be helpful for debugging the DojoStore trait implementation when needed.

A small suggestion: consider adding a parameter for the verbosity level to control the output format, which could help when debugging complex storage operations.

-pub fn debug_store_expand(element_name: &str, code: &str) {
+pub fn debug_store_expand(element_name: &str, code: &str, verbosity: Option<u8>) {
    if std::env::var("DOJO_STORE_EXPAND").is_ok() {
+       let verbosity_level = verbosity.unwrap_or(1);
+       if verbosity_level > 1 {
            println!(
                "\n*> EXPAND {} <*\n>>>>>>>>>>>>>>>>>>>>>>>>>>>\n{}\n<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
                element_name, code
            );
+       } else {
+           println!("*> EXPAND {}: {} bytes of code", element_name, code.len());
+       }
    }
}
crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (1)

73-77: Default implementation for AnotherEnum required by DojoStore

Ohayo, sensei! Similar to MyEnum, this change adds the Default trait to AnotherEnum and marks X as the default variant. This is necessary for the new DojoStore trait implementation.

Consider documenting in the enum definition why Default is required, to make it clearer for future developers.

 #[derive(Introspect, Copy, Drop, Serde, Default)]
+/// AnotherEnum implements Default to work with DojoStore,
+/// which uses variant 0 to represent uninitialized values
 enum AnotherEnum {
     #[default]
     X: u8,
 }
crates/dojo/lang/src/attribute_macros/model.rs (1)

79-80: Ohayo sensei, good use of separate vectors for deserialized values.
Separating tracked arrays can improve readability. If performance becomes a concern, consider using specialized data structures or in-place transformations.

crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (2)

17-20: Ohayo sensei, collecting struct member aliases.
Storing member details in vectors is workable. If you run into ordering or duplication issues, you may consider a small dedicated struct or map for more robust handling.


55-59: Ohayo sensei, efficient string joins.
Combining the joined segments outside the loop is good for clarity. Watch out for accidental whitespace or commas that break the final structure.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (1)

151-153: Critical change for the DojoStore implementation, sensei!

This modification to increment the variant index is the core change that addresses the PR objective - solving the problem where variant id 0 cannot be distinguished from an uninitialized variant. Now variants start from 1, which is a fundamental change to support the new storage mechanism.

This change ensures that when deserializing, a variant index of 0 can be properly interpreted as uninitialized, triggering the return of the default value. Excellent architectural decision!

crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (2)

33-35: Essential addition of use_legacy_storage method, sensei!

This method is crucial for the PR objective - it allows models to indicate whether they should use the legacy storage behavior or the new DojoStore trait. This enables backward compatibility via the DojoLegacyStorage derive attribute mentioned in the PR description.

This toggle mechanism provides an elegant migration path for existing models while allowing new ones to benefit from the improved storage handling.


88-96: Excellent implementation of ValueDojoStore!

This is a core part of the PR - implementing the DojoStore trait for model values with both serialization and deserialization support. The template correctly includes placeholders for the serialization and deserialization logic.

The separation of serialization and model logic through this trait is a good design pattern that allows for specialized handling of storage edge cases like Option<T> and custom enums.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (2)

31-197: Ohayo sensei: Consider a table-driven approach to reduce repetitive tests.
This test function checks many primitive types in the same pattern. Using a table-driven approach or macro-based testing could eliminate duplicated code and make maintenance easier.


192-192: Ohayo sensei: Fix mismatch in the error message.
The error message references i128 instead of ByteArray. Here’s a suggested fix:

-    assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<i128> serialization failed");
+    assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<ByteArray> serialization failed");
crates/dojo/core/src/storage/dojo_store.cairo (2)

66-76: Ohayo sensei! Watch out for recursion in large arrays.
Recursively invoking serialize_array_helper could cause excessive stack usage for large arrays. An iterative approach might be more robust if large arrays are expected.


78-86: Ohayo sensei! Consider iterating in deserialize_array_helper.
Similarly, deserializing large arrays with recursion can hit stack limits in extreme cases. Switching to an iterative method may improve reliability.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 429f238 and 8b48963.

⛔ Files ignored due to path filters (4)
  • Cargo.lock is excluded by !**/*.lock
  • crates/dojo/core-cairo-test/Scarb.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (48)
  • Cargo.toml (1 hunks)
  • crates/benches/contracts/src/models/character.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/lib.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (4 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/meta/introspect.cairo (1 hunks)
  • crates/dojo/core/src/meta/layout.cairo (1 hunks)
  • crates/dojo/core/src/model/component.cairo (1 hunks)
  • crates/dojo/core/src/model/definition.cairo (1 hunks)
  • crates/dojo/core/src/model/interface.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (4 hunks)
  • crates/dojo/core/src/model/model_value.cairo (2 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/layout.cairo (8 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (1 hunks)
  • crates/dojo/lang/Cargo.toml (1 hunks)
  • crates/dojo/lang/src/attribute_macros/element.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/mod.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3 hunks)
  • crates/dojo/lang/src/cairo_plugin.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs (3 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/mod.rs (1 hunks)
  • crates/dojo/lang/src/lib.rs (2 hunks)
  • crates/dojo/lang/src/utils.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/model.rs (2 hunks)
  • crates/dojo/world/src/contracts/model.rs (2 hunks)
  • crates/sozo/ops/src/model.rs (8 hunks)
  • crates/torii/sqlite/src/model.rs (1 hunks)
  • crates/torii/types-test/src/models.cairo (1 hunks)
  • examples/spawn-and-move/dojo_dev.toml (1 hunks)
  • examples/spawn-and-move/src/models.cairo (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/dojo/lang/src/attribute_macros/mod.rs
  • crates/dojo/lang/src/attribute_macros/element.rs
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (141)
examples/spawn-and-move/dojo_dev.toml (1)

71-71:

✅ Verification successful

Verify that the world address has been updated in all necessary places

Ohayo, sensei! The world address has been updated to a new value. This change appears to be related to the addition of the DojoStore trait for handling Option<T> and custom enum storage.


🏁 Script executed:

#!/bin/bash
# Ensure the world address is consistent across the codebase
rg "0x3aacf8c2da6370f1ba7f331094dff29e209d40a98ac94f20c66c34128177799" --type toml

Length of output: 204


Ohayo, sensei!

I’ve verified that the updated world address:

world_address = "0x3aacf8c2da6370f1ba7f331094dff29e209d40a98ac94f20c66c34128177799"

appears only in examples/spawn-and-move/dojo_dev.toml at line 71. No additional occurrences were found across the TOML files, so it looks like the update is correctly localized. This change aligns with the recent additions related to the DojoStore trait.

Keep up the great work!

crates/dojo/core/src/model/interface.cairo (1)

10-10: Clean interface addition for the new storage trait system

Ohayo, sensei! This new use_legacy_storage method is a good addition to the IModel trait. It provides a clean way for models to specify whether they should use the legacy Serde-based storage or the new DojoStore trait that properly handles Option<T> and custom enums.

The method signature follows the conventions of other methods in this trait, taking a reference to self and returning a boolean.

crates/dojo/core/src/model/definition.cairo (1)

23-23: Nice addition to model definition for storage behavior control

Ohayo, sensei! The use_legacy_storage method is an excellent addition to the ModelDefinition trait. This static method allows models to declare at the type level whether they should use legacy storage or the new DojoStore trait system.

This pairs well with the similar method added to the IModel trait and enables the DojoLegacyStorage derive attribute mentioned in the PR objectives.

crates/dojo/lang/Cargo.toml (1)

31-31:

❓ Verification inconclusive

Added parser dependency for attribute processing

Ohayo, sensei! Adding the Cairo language parser as a development dependency makes sense for implementing the DojoLegacyStorage derive attribute. This will allow the code to properly parse and process the attribute during the compilation phase.


🏁 Script executed:

#!/bin/bash
# Check if the cairo-lang-parser is being used in the implementation of the DojoLegacyStorage attribute
rg -A 3 -B 3 "cairo-lang-parser" --type rust

Length of output: 44


Ohayo, sensei! The addition of the cairo-lang-parser.workspace = true line in crates/dojo/lang/Cargo.toml looks like it should support the attribute processing for DojoLegacyStorage. However, our automated search—limited to Rust source files—didn't show any direct usage of cairo-lang-parser. This may be because the dependency is only referenced in the TOML configuration or used indirectly during macro expansion.

  • Please ensure that the derive attribute implementation indeed leverages the Cairo parser during compilation.
  • Consider reviewing the macro expansion or testing the build output to confirm that the attribute processing behaves as expected.
  • If necessary, update your search scope (or perform a manual check) to include TOML files or any indirect references.
crates/dojo/lang/src/derive_macros/introspect/utils.rs (1)

42-44: Ohayo! Clean, functional addition for Option type detection.

The is_option function follows the same pattern as the existing type detection functions, making it a natural extension to the type introspection capabilities. This will be valuable for handling Option types differently during serialization/deserialization.

crates/dojo/core/src/model/component.cairo (1)

40-42: Ohayo! Solid implementation of the storage strategy selector.

This method nicely delegates to the Model implementation, creating a consistent interface for determining whether to use legacy storage. This allows components to transparently handle different storage approaches without knowing the implementation details.

crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1)

64-66: Ohayo sensei! Testing the non-legacy storage path with a hardcoded return.

The implementation always returns false, ensuring this test model uses the new DojoStore approach. This is appropriate for testing the new storage mechanism.

Do you plan to add test cases that explicitly test both true and false return values to ensure both storage paths are covered? This would help verify the correctness of both legacy and new storage mechanisms.

crates/dojo/core/src/lib.cairo (1)

71-72:

❓ Verification inconclusive

Ohayo! Important module export for the new storage functionality.

This change exposes the new DojoStore trait and ContractAddressDefault type, making them available for use throughout the codebase. This is essential for the implementation of the solution described in the PR objectives.


🏁 Script executed:

#!/bin/bash
# Let's check if there are implementations of DojoStore for Option types
# and custom enums in the codebase

echo "Checking for DojoStore implementations for Option types:"
rg -A 10 "impl DojoStore<Option<" crates/

echo -e "\nChecking for Default implementations for custom enums:"
rg -A 5 "#\[derive\(.*Default.*\)\].*enum" crates/

Length of output: 344


Ohayo sensei! Please verify the new storage exports manually.

The module export for the new storage functionality in crates/dojo/core/src/lib.cairo (lines 71–72) appears to be correctly exposing the DojoStore trait and ContractAddressDefault type, aligning with the PR’s objectives. However, our automated search for implementations (such as DojoStore implementations for Option types and default implementations for custom enums) returned no output—which may be due to the absence of such patterns or might require manual verification.

  • File Checked: crates/dojo/core/src/lib.cairo
  • Exported Items:
    • pub mod dojo_store;
    • pub use dojo_store::{ContractAddressDefault, DojoStore};

Please manually verify that the absence of additional related implementations is expected.

crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1)

133-134: Ohayo! The test assertions now correctly verify the default behavior for uninitialized variants.

The changes to these assertions reflect the core improvement introduced by the DojoStore trait, which solves the problem with variant id 0 being indistinguishable from uninitialized values. Now the test correctly verifies that:

  • EnumOne::Two(0) is returned as the default value for an uninitialized enum
  • Option::None is returned for an uninitialized Option

This aligns perfectly with the PR objectives, sensei!

crates/dojo/core-cairo-test/src/lib.cairo (2)

20-20: Ohayo! Great addition of the layout test module, sensei.

This new test module will help ensure the layout functionality works correctly with the DojoStore trait implementations. The structure of your test organization is clean and logical.


33-33: Excellent addition of tests for the new DojoStore functionality!

The new dojo_store test module is essential for verifying that the trait handles Option and custom enums correctly, especially regarding the variant id 0 issue mentioned in the PR objectives.

crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1)

18-21: Ohayo, sensei! Special case handling for event serialization looks good.

The new deserialize method correctly specifies that events always use Serde since they're never stored in world storage. This is a good distinction to make and aligns with the PR objectives of introducing DojoStore for model storage while allowing legacy behavior where appropriate.

crates/dojo/lang/src/derive_macros/mod.rs (1)

24-24: Perfect implementation of the DojoLegacyStorage derive attribute, sensei!

This constant introduces the derive attribute mentioned in the PR objectives, allowing models to retain the current storage behavior while the new DojoStore trait becomes the default. This provides a smooth migration path for existing code.

crates/dojo/core/src/model/model_value.cairo (2)

7-8: Effective API addition for handling DojoStore serialization

Ohayo, sensei! This new method in the ModelValueParser trait is a well-structured approach for implementing custom deserialization logic. It provides the necessary abstraction layer to handle Option and enum variants differently from regular Serde deserialization.


36-37: Appropriate implementation switch from Serde to ModelValueParser

This change correctly implements the PR objectives by redirecting deserialization from Serde to the new ModelValueParser trait, enabling custom handling for enum variants with 0 indices.

examples/spawn-and-move/src/models.cairo (1)

3-5: Default trait addition properly addresses enum variant issue

Ohayo, sensei! This change correctly implements the #[default] attribute on the None variant, which addresses the core problem described in the PR objectives. Now, the Direction enum can properly distinguish between an uninitialized variant and an explicitly set None variant.

crates/dojo/core/src/world/world_contract.cairo (1)

1156-1163: Valid protection for fixed layout resources

Ohayo, sensei! This additional validation is critical for ensuring that resources with fixed layouts maintain consistent storage patterns during upgrades. The check prevents potential data corruption by rejecting layout changes that would break existing storage.

crates/dojo/core/src/storage/layout.cairo (6)

10-11: Good refactoring with MAX_VARIANT_INDEX constant

Ohayo, sensei! Extracting this value into a named constant improves code maintainability and readability by centralizing the maximum variant index value that was previously hardcoded in multiple locations.


179-179: Consistent application of MAX_VARIANT_INDEX

The assertion properly uses the new constant instead of a hardcoded value, improving consistency and maintainability.


304-304: Consistent application of MAX_VARIANT_INDEX

The assertion correctly uses the newly defined constant, ensuring consistent validation across the codebase.


314-314: Better error reporting for variant layout issues

Including the variant value in the error message is a helpful improvement for debugging.


468-468: Consistent boundary check with MAX_VARIANT_INDEX

The validation is properly using the new constant, maintaining consistency with other assertions.


477-486: Crucial handling of uninitialized variants

Ohayo, sensei! This is the core implementation that addresses the PR objective. The special handling for variant 0 is essential for the DojoStore trait to properly distinguish between uninitialized variants and explicitly set variants. The comments clearly explain the rationale behind this behavior.

crates/torii/sqlite/src/model.rs (1)

117-120:

❓ Verification inconclusive

Ohayo! New method adds needed legacy storage support.

The new use_legacy_storage method with a placeholder implementation will help distinguish between models using the new DojoStore trait and those using legacy storage behavior.

However, sensei, there's a TODO comment that should be addressed. Let's check what other implementations of this method look like:


🏁 Script executed:

#!/bin/bash
# Search for other implementations of use_legacy_storage
rg "fn use_legacy_storage" --type cairo -A 5

Length of output: 76


Ohayo sensei! It looks like the initial search command used the wrong file type flag, which is why you saw the “unrecognized file type: cairo” error. Let's re-run the search using the proper flag for Rust files to check for other implementations of use_legacy_storage.

Please run this updated script and manually verify the results. Once you’ve confirmed whether there are existing implementations that need to be aligned with or if the placeholder can remain until further work is done, feel free to address or remove the TODO comment accordingly.


🏁 Script executed:

#!/bin/bash
# Search for other implementations of use_legacy_storage in Rust files
rg "fn use_legacy_storage" -t rust -A 5

Length of output: 2700


Ohayo sensei! The new use_legacy_storage method in crates/torii/sqlite/src/model.rs is correctly added as a placeholder to differentiate legacy storage from models using the new DojoStore trait. That said, I noticed the lingering TODO comment. Our search shows that similar methods exist in:

  • crates/dojo/world/src/contracts/abigen/model.rs – where the function returns an FCall related to legacy storage.
  • crates/dojo/world/src/contracts/model.rs – where an async trait method is defined and implemented similarly.

These examples imply that the legacy storage support might eventually need a more robust implementation. Please either implement the intended functionality in line with these examples or remove the TODO comment to avoid confusion.

crates/dojo/core-cairo-test/src/tests/world/event.cairo (2)

48-51: Excellent addition of Default trait and default variant, sensei!

Adding the Default trait to MyEnum and marking X as the default variant solves the problem with custom enums where variant id 0 couldn't be distinguished from an uninitialized variant.


64-67: Well done adding Default to AnotherEnum too!

Ohayo! This follows the same pattern as the previous enum, ensuring consistent behavior across all custom enums. The #[default] attribute on variant X ensures it will be used when initializing with Default::default().

crates/torii/types-test/src/models.cairo (1)

74-77: Perfect implementation of Default for Depth enum, sensei!

Adding the Default trait and marking Zero as the default variant is an excellent choice for the Depth enum - the name "Zero" naturally suggests it should be the default value, which improves code readability.

crates/dojo/world/src/contracts/model.rs (2)

61-61: Ohayo! Good trait addition for model reader interface.

Adding the use_legacy_storage method to the ModelReader trait ensures consistency across all implementations and supports the PR objective of handling different storage behaviors.


209-211: Clean implementation of use_legacy_storage method, sensei!

The implementation properly delegates to the underlying model reader's method, maintaining consistent behavior throughout the codebase.

crates/dojo/core/src/meta/layout.cairo (2)

52-58: Well-documented function implementation!

Ohayo, sensei! This is a clear and thorough docstring that explains the purpose of the function, the problem it's addressing, and the context around variant indexing changes from 0 to 1.


59-109: Well-structured implementation of the legacy layout builder!

The implementation recursively processes different layout types correctly:

  • For enums: Decrements the selector (variant index) by 1 to convert from new format (1-based) to legacy format (0-based)
  • For structs: Preserves selectors but recursively processes child layouts
  • For tuples and arrays: Recursively processes their layouts
  • For other types: Returns them unchanged

This function enables backward compatibility while allowing the new storage system to work correctly with Option and custom enums.

crates/dojo/world/src/contracts/abigen/model.rs (2)

671-682: Clean implementation of legacy storage checker for ModelContract!

Ohayo, sensei! This implementation follows the established pattern of the other methods in this class. It creates a function call to the use_legacy_storage entry point and properly returns an FCall object with a boolean result type.


800-811: Consistent implementation of legacy storage checker for ModelContractReader!

This mirrors the implementation for ModelContract, ensuring consistent behavior across both contract interfaces. Good job maintaining code symmetry!

crates/dojo/core/src/meta/introspect.cairo (1)

599-603: Important variant indexing change to support new storage system!

Ohayo, sensei! This change is a critical part of the DojoStore trait implementation. By changing the Option's variant indices from 0/1 to 1/2, we ensure we can distinguish between uninitialized variants (which would be 0) and actual variant values.

The selector for Some is now 1 (previously 0), and the selector for None is now 2 (previously 1). This change works together with the build_legacy_layout function to enable backward compatibility.

crates/benches/contracts/src/models/character.cairo (2)

39-44: Added Default trait and default variant for Weapon enum!

Ohayo, sensei! Adding the Default trait and specifying #[default] for the DualWield variant ensures we have a valid default value when reading an uninitialized Weapon. This works with the new DojoStore trait to properly deserialize enum values.


46-50: Added Default trait to Sword struct!

Adding the Default trait to the Sword struct ensures we can instantiate default values for its fields when needed. This complements the default implementation for Weapon and supports the new storage management system.

crates/dojo/lang/src/lib.rs (1)

14-14: New public module added for utilities

Ohayo, sensei! The addition of a new public module utils seems to be related to the DojoStore implementation, which is at the heart of this PR. Make sure that this module is properly documented and contains all necessary utility functions for working with DojoStore.

crates/dojo/lang/src/derive_macros/introspect/layout.rs (3)

10-11: Added utility function for Option type detection

Ohayo, sensei! The addition of is_option to the imported utilities is necessary for the new Option handling in the DojoStore trait. This seems well-integrated with the existing utility functions.


380-387: Proper error handling for Option type in packed layouts

Ohayo, sensei! This addition correctly handles the case when Option is used in a packed layout. Since Option types can't be packed (due to their variable size nature), it's important to provide a clear error message to developers.

The diagnostic message is clear and indicates exactly what the issue is, which is excellent.


78-81:

❓ Verification inconclusive

Critical change for variant indexing to support DojoStore trait

Ohayo! This is a fundamental change that addresses the core issue mentioned in the PR objectives. By incrementing the variant index to start from 1 instead of 0, you enable the use of 0 to represent uninitialized variants, which solves the problem with Option and custom enums.

The comment explains the rationale clearly, which is excellent for future maintainers.

Let's verify that all custom enums affected by this change also implement Default trait:


🏁 Script executed:

#!/bin/bash
# Find all enum definitions in the codebase and check if they have Default implementation
echo "Checking for enums that might be affected by the variant index change..."

# Find all enums
rg --type cairo -A 2 "enum\s+\w+\s*\{" --glob "*.cairo" | grep -v "impl.*Default"

echo "These enums might need to implement Default to work properly with DojoStore"

Length of output: 429


Ohayo sensei! Variant indexing change confirmed as a critical design update.

The update—incrementing the variant index by 1 so that 0 can denote an uninitialized variant—is an essential fix for the DojoStore trait and addresses the Option and custom enum issues nicely. However, our automated search for enums in *.cairo files flagged an issue (“unrecognized file type: cairo”), which means we couldn’t conclusively verify that all custom enums have a Default implementation.

Action items:

  • Revisit the enum scan: Adjust the RG search command (e.g., remove the --type cairo flag or define a custom file type for Cairo) to properly locate and verify enums.
  • Manual review: Alternatively, manually check that custom enums in the codebase properly implement the Default trait to ensure compatibility with the DojoStore logic.
crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (1)

58-62: Default implementation for MyEnum required by DojoStore

Ohayo! This change is necessary to support the new DojoStore trait. By adding the Default trait and marking variant X as default, you're ensuring that MyEnum can be properly deserialized when encountering variant 0 (uninitialized).

This aligns perfectly with the PR objectives that mention custom enums need to implement Default.

crates/dojo/core/src/model/model.cairo (4)

53-55: Key addition of deserialize method to ModelParser trait

Ohayo, sensei! This new method is a crucial part of the DojoStore implementation. It allows for custom deserialization logic that handles Option and custom enums correctly, by returning Default::default() when encountering variant 0.

The method signature is clean and follows Cairo conventions.


84-87: Essential method to detect legacy storage usage

Ohayo! The use_legacy_storage method is a key part of the backward compatibility strategy mentioned in the PR objectives. It allows models to specify whether they should use the legacy storage behavior or the new DojoStore behavior.

This is well-documented and the method name clearly conveys its purpose.


154-157: Implementation of use_legacy_storage method

Ohayo! This implementation delegates to the ModelDefinition trait, which is a clean approach that keeps the responsibility for determining the storage type with the model definition.

This method is crucial for the backward compatibility mentioned in the PR objectives, allowing models marked with DojoLegacyStorage to maintain their current behavior.


135-136:

❓ Verification inconclusive

Updated deserialization logic to use ModelParser

Ohayo, sensei! This change is essential to make use of the new custom deserialization logic provided by ModelParser instead of the standard Serde deserialization.

This one-line change has significant implications, as it's the switch point between the old and new deserialization approaches.

Let's verify that all places that might use this deserialization are properly handled:


🏁 Script executed:

#!/bin/bash
# Find places where deserialization might be used
echo "Checking for places that might be affected by the deserialization change..."

# Look for any references to deserialization
rg "deserialize" --type cairo --glob "*.cairo" | grep -v "ModelParser"

echo "These places might need to be updated to use ModelParser::deserialize instead of Serde::deserialize"

Length of output: 468


Ohayo, sensei!
It looks like our original verification script didn’t quite do the trick—the use of --type cairo isn’t supported by ripgrep and returned the "unrecognized file type: cairo" error. This means we might not be seeing all references to deserialization across our Cairo files.

To be sure all affected areas are handled, please update the verification command to use file globbing instead. For example, try running:

rg -g "*.cairo" "deserialize" | grep -v "ModelParser"

This adjusted command will properly search through all .cairo files for any instances of "deserialize" that haven’t been updated to use ModelParser::deserialize. If any such references are found, they’ll likely need to be updated accordingly.

crates/dojo/lang/src/cairo_plugin.rs (2)

15-17: Add DOJO_LEGACY_STORAGE_DERIVE to import properly

Ohayo, sensei! Good job adding the DOJO_LEGACY_STORAGE_DERIVE to the imports. This is a necessary addition to support the new derive macro for legacy storage handling, enabling the distinction between models using the new DojoStore trait and those that need to maintain backward compatibility.


114-119: Well-organized derive macro declaration

Nice work updating the declared_derives method to include the new DOJO_LEGACY_STORAGE_DERIVE. This ensures the Cairo plugin recognizes and processes this derive macro correctly during compilation. The vector organization is clean and maintainable.

crates/dojo/core-cairo-test/src/tests/model/model.cairo (6)

65-71: Well-structured enum with default implementation

Ohayo sensei! I like how you've implemented MyEnum with appropriate traits including Default and marked the Z variant as #[default]. This is a key part of addressing the storage issue mentioned in the PR objectives, where the first variant (id 0) can't be distinguished from uninitialized values.


73-81: Good implementation of legacy storage model

The LegacyModel with the DojoLegacyStorage attribute demonstrates the backward compatibility aspect of your solution. The structure appropriately uses both simple types and complex types like tuples, Option<T>, and custom enums that were problematic in the original storage system.


83-91: Clean DojoStore model implementation

This model correctly omits the DojoLegacyStorage attribute, which means it will use the new DojoStore trait by default. Using the same field structure as LegacyModel will make it easy to compare the two approaches in testing.


101-102: Properly registered new models in namespace definition

Good job updating the namespace definition to include both new models. This ensures they're properly recognized as resources within the test environment.


375-459: Comprehensive test for legacy model behavior

Excellent test implementation! The test validates both the layout definition and serialization/deserialization process for the legacy model. Your assertions clearly show the expected behavior where variant IDs start at 0, which is the core issue being addressed in this PR.


462-542: Thorough testing of DojoStore model with adjusted variant IDs

Ohayo sensei! This test nicely demonstrates the key difference in the new storage system - the variant IDs start at 1 (not 0) in the serialized values, allowing proper distinction between initialized and uninitialized variants. The expected layout assertion shows the structural changes needed to support this approach.

crates/dojo/lang/src/attribute_macros/event.rs (2)

24-24: Import path reorganization

Good adjustment to import from crate::utils instead of super::element. This suggests a beneficial reorganization of utility functions to improve code structure.


66-72: Updated serialization function call with legacy flag

Ohayo sensei! You've correctly updated the serialize_keys_and_values call to include the new boolean parameter (set to true). This indicates that events will use the legacy storage behavior by default, which maintains backward compatibility while allowing the new storage system to be used where needed.

crates/dojo/core-cairo-test/src/tests/world/model.cairo (2)

59-62: Added Default trait and default variant to MyEnum

Ohayo sensei! Good job adding the Default trait to MyEnum and marking the X variant as #[default]. This ensures that any uninitialized MyEnum values will have a well-defined state, which is essential for the new storage system to work correctly.


75-78: Added Default implementation to AnotherEnum

Smart addition of the Default trait to AnotherEnum with the X variant marked as #[default]. This consistent approach across all enums in the codebase ensures that the new storage system handles all enum types uniformly.

Cargo.toml (1)

213-225:

✅ Verification successful

Ohayo sensei, please verify the new Scarb references!
Switching to a different GitHub fork and commit can sometimes introduce unexpected code changes. It's great that you've commented out the old references. Ensure this commit remains stable in the long run.

You can run this script to confirm that commit “89125e3” exists and is accessible in the new fork:


🏁 Script executed:

#!/bin/bash
# Description: Verify the referenced commit from remybar/scarb
gh api repos/remybar/scarb/commits/89125e3

Length of output: 4447


Ohayo sensei, the new Scarb commit "89125e3" has been verified!
Based on the API output, the commit from remybar/scarb is valid and accessible. The updated GitHub references in Cargo.toml (lines 213-225) correctly replace the old fork, and the commented-out references provide a useful fallback. Everything looks stable and in order—please continue monitoring for any unexpected changes in future updates.

crates/dojo/lang/src/attribute_macros/model.rs (7)

13-13: Ohayo sensei, confirming the new Terminal import usage.
The Terminal import might be handy for reading or parsing tokens. Double-check if any additional lints or macros are needed to handle Terminal AST interactions thoroughly.


20-23: Ohayo sensei, nice reorganization of derive macros!
Consolidating these DOJO_*_DERIVE constants in one place clarifies how they interrelate. This change helps avoid repeated macro checks.


67-69: Ohayo sensei, ensuring the legacy storage flag logic.
Adding use_legacy_storage based on the DOJO_LEGACY_STORAGE_DERIVE attribute is straightforward. Just be sure any fallback paths also behave consistently.


88-168: Ohayo sensei, the extended parsing logic for keys and values looks neat!
Your new approach systematically enforces that all #[key] fields come first, which is a valuable check. Keep an eye on error-handling if the struct design changes frequently.


218-230: Ohayo sensei, aligning model deserialization with the correct path.
Using dojo::storage::DojoStore vs. core::serde::Serde is a nice toggle for legacy or modern approaches. Consider adding thorough tests across both paths for unexpected edge cases.


231-239: Ohayo sensei, consistent approach with build_legacy_layout.
Applying build_legacy_layout is a simple way to maintain backward compatibility. Double-check that the derived layout is correct for nested types or multi-level references.


262-265: Ohayo sensei, final injection of the use_legacy_storage parameter is straightforward.
This signals the rest of the patch that old storage semantics might apply. Ensure all derived expansions handle this flag thoughtfully.

crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (5)

1-4: Ohayo sensei, new imports for Dojo store building.
Everything looks solid. Just make sure these nodes (like OptionTypeClause) handle advanced syntax cases without issues.


5-9: Ohayo sensei, good use of specialized utility functions.
Breaking down serialization into serialize_primitive_member_ty and serialize_tuple_member_ty fosters clarity. This approach should simplify future expansions.


10-16: Ohayo sensei, struct store builder signature.
Your function parameters neatly separate the AST from generic substitutions. This design choice eases code reuse and potential extension for advanced struct patterns.


21-53: Ohayo sensei, systematic loop for serializing/deserializing each member.
This approach is quite modular. Keep in mind that advanced nested types or generics might need more special handling if they appear in the future.


77-175: Ohayo sensei, your enum Dojo store logic covers variant indexing well!
Incrementing the variant index to distinguish uninitialized from the first variant is clever. Always confirm that the default variant actually matches index 0 in your design.

crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (4)

39-45: Ohayo! Good addition of the Default trait for the enum.

The addition of the Default trait and marking the Two variant as #[default] is excellent for the new DojoStore trait implementation. This helps resolve the issue with variant id 0 by providing an explicit default variant.


101-103: Implementation aligns with DojoStore trait requirements, sensei!

The use_legacy_storage method correctly returns false to indicate this model will use the new DojoStore trait for serialization/deserialization rather than legacy storage.


136-140: Great addition of Default trait to Sword struct.

This change enables the Sword struct to work properly with the new DojoStore trait serialization mechanics by providing default values when needed.


186-191: Well implemented Default trait for Weapon enum!

Properly adding the Default trait and marking DualWield as the #[default] variant ensures this enum will work correctly with the new storage system when deserialized from an uninitialized state.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (5)

5-8: Ohayo! Good implementation of Serde and Default trait for Base.

Adding the Serde and Default traits to the Base struct ensures compatibility with the new DojoStore trait's serialization requirements.


58-64: Default trait implementation for EnumNoData looks perfect, sensei!

Adding the Default trait and marking One as the #[default] variant ensures this enum will work correctly with the new DojoStore serialization system.


122-126: Great addition of GenericEnum with Default support!

The new GenericEnum<T> with Default implementation and explicit #[default] variant will work well with the DojoStore trait's handling of generics.


170-190: Well-updated test for the renamed GenericStruct!

The test has been properly updated to reflect the rename from Generic to GenericStruct and verifies the correct layout and type information.


193-213: Excellent new test for GenericEnum, sensei!

This new test thoroughly validates the introspection capabilities for the newly added GenericEnum<T> type, ensuring it works correctly with the layout and serialization systems.

crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3)

13-19: Ohayo! Good addition of Serde trait to FooBaseEvent.

Adding the Serde trait to FooBaseEvent ensures it can be properly serialized and deserialized with the new DojoStore system.


66-70: Well done implementing Default for MyEnum, sensei!

Adding the Default trait and marking X as the #[default] variant ensures this enum will work correctly with the DojoStore trait's deserialization logic when handling uninitialized variant indexes.


81-85: Excellent Default implementation for AnotherEnum!

Similar to MyEnum, this addition of the Default trait with X as the #[default] variant prepares AnotherEnum for the new storage system.

crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3)

29-30: Ohayo! Good modification to use model_layout variable.

This change allows for more flexible layout handling, potentially supporting both the legacy layout and the new layout format depending on configuration.


59-61: Well implemented deserialize method for ModelParser!

Adding a proper deserialization method that delegates to the DojoStore trait's implementation is essential for the new storage system to work with custom enums and Option<T>.


75-77: Great addition of deserialize for ModelValueParser, sensei!

Similar to the ModelParser implementation, this provides the necessary deserialization capabilities for model values in the new storage system.

crates/dojo/lang/src/derive_macros/introspect/mod.rs (11)

10-14: Ohayo sensei: Nice approach adding the new modules and imports
No issues spotted. This correctly introduces itertools, debug_store_expand, and the dojo_store module.


29-44: Ohayo sensei: Great use of generic type handling
Building generic types and impls for both introspection and Dojo store looks consistent. Just be sure to handle edge cases if gen_types is empty.


58-67: Ohayo sensei: Good job building the struct Dojo store
Leveraging dojo_store::build_struct_dojo_store and logging with debug_store_expand is a neat approach for debugging.


68-76: Ohayo sensei: Clean usage of generate_introspect
Passing the newly built dojo_store to generate_introspect nicely centralizes final output.


87-102: Ohayo sensei: Similar robust design for enums
Reusing the same generic building logic for enum introspection and store generation keeps the code DRY.


130-137: Ohayo sensei: Using dojo_store for enum
Consistently constructing the enum Dojo store is good. Ensure Default is implemented where needed for custom enums.


139-149: Ohayo sensei: Great final introspection call
Similar to structs, you integrate the new store with the introspection function elegantly.


161-161: Ohayo sensei: Additional parameter for dojo_store
Including dojo_store in the signature clarifies the data flow for final introspect generation.


191-191: Ohayo sensei: Properly mapping dojo_store in UnorderedHashMap
Integrating the store text into the interpolation is consistent with the existing pattern.


198-221: Ohayo sensei: build_generic_types function
Your function neatly filters and collects only type parameters, keeping the logic straightforward.


223-244: Ohayo sensei: build_generic_impls is well structured
Generating the partial impls for each generic type is a neat approach. Just ensure string join logic with Itertools covers all generics properly.

crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (17)

11-52: Ohayo sensei: test_fixed_layout
Comprehensive checks for reading/writing defaults, offsets, and deletions. No issues found.


54-102: Ohayo sensei: test_tuple_layout
Nicely tests tuple handling with multiple fields, including offsets and deletions. Looks correct.


104-148: Ohayo sensei: test_byte_array_layout
Good coverage. Verifies reading/writing of byte arrays, offset mechanics, and delete steps thoroughly.


149-164: Ohayo sensei: test_read_byte_array_layout_invalid_array_length
Panic test is properly used to catch erroneous array length. Good negative testing coverage.


165-175: Ohayo sensei: test_byte_array_layout_bad_input_length
Again, correct usage of a panic test for invalid input length. Great validation.


176-186: Ohayo sensei: test_byte_array_layout_bad_array_length_value
Ensuring we catch out-of-range lengths fortifies reliability. Nicely done.


187-197: Ohayo sensei: test_byte_array_layout_bad_array_length
Checks for mismatched or incomplete data lengths, ensuring robust error handling.


198-240: Ohayo sensei: test_array_layout
Solid approach for verifying normal array writing, reading, offsets, and deletion.


242-259: Ohayo sensei: test_read_array_layout_bad_array_length
Proper negative test ensuring we handle an overflow in array length.


261-273: Ohayo sensei: test_array_layout_bad_values_length
Good that you confirm mismatched data length triggers an appropriate panic.


274-286: Ohayo sensei: test_array_layout_bad_array_length
Another negative path confirming large invalid length is properly rejected.


287-333: Ohayo sensei: test_struct_layout
Excellent coverage of struct read/write logic, default values, and offset usage.


335-382: Ohayo sensei: test_enum_layout
Thorough checks of variant writing, reading, and deletion. Nicely verifies default variant.


383-403: Ohayo sensei: test_read_enum_layout_invalid_variant_value
Panic test ensures we reject out-of-range variant values. Valid approach.


404-423: Ohayo sensei: test_read_enum_layout_unexisting_variant_value
Another negative scenario confirmed. Good coverage of invalid variants.


425-441: Ohayo sensei: test_enum_layout_invalid_variant_value
Reinforces checks for out-of-range variants when writing, ensuring robust error handling.


442-458: Ohayo sensei: test_enum_layout_unexisting_variant
Ensures that any attempt to write a non-existent variant triggers a panic. Implementation is thorough.

crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (7)

4-19: Ohayo sensei: test_build_legacy_layout_option
Verifies that Option layout transforms correctly, mapping Some/None properly.


21-44: Ohayo sensei: test_build_legacy_layout_simple_enum
Checks re-indexing of variants from 1..4 down to 0..3. Handy test to ensure correct legacy offset.


46-71: Ohayo sensei: test_build_legacy_layout_tuple
Great demonstration of nested Option in a tuple. Confirms correct field-level transformations.


73-95: Ohayo sensei: test_build_legacy_layout_array
Ensures arrays of Option map properly in the legacy layout, revalidating the internal enum structure.


97-129: Ohayo sensei: test_build_legacy_layout_struct
Covers struct fields with Option, verifying correct reindexing of Some/None.


131-194: Ohayo sensei: test_build_legacy_layout_nested_enum
Comprehensive check for multi-level nesting of arrays and tuples inside an enum.


196-260: Ohayo sensei: test_build_legacy_layout_nested_struct
Solid final verification for nested arrays, tuples, and Option fields in a struct.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (3)

3-9: Ohayo sensei: Enum definition looks tidy and concise!
No obvious pitfalls or syntactical issues. The variant types are clearly defined.


199-218: Ohayo sensei: Dynamic arrays test looks great!
The logic cleanly validates both the successful deserialization of an array with elements and the Option::None scenario.


243-260: Ohayo sensei: Enum variant testing is thorough.
The checks for valid variant indices, default variant, and out-of-range behavior appear correct.

crates/sozo/ops/src/model.rs (4)

69-75: Ohayo sensei: Verify edge cases in fallback logic for legacy storage.
When use_legacy_storage is missing, defaulting to true is convenient but be sure that any unexpected error does not mask genuine issues.


126-133: Ohayo sensei: Consistent fallback usage for missing use_legacy_storage.
This mirrored approach to the fallback logic is coherent with the earlier block, ensuring consistent default behavior.


397-410: Ohayo sensei: Layout printing improvements look good.
Displaying legacy storage : <bool> is clear and helps quickly identify models using the older storage mechanism.


440-584: Ohayo sensei: Added use_legacy_storage parameter across formatter functions is consistent.
These changes unify how legacy or new storage formats are applied to arrays, tuples, enums, and fields.

crates/dojo/lang/src/utils.rs (9)

1-21: Ohayo sensei: Initial imports and get_serialization_path are solid.
Deferring to DojoStore or Serde based on the use_serde flag is straightforward and well-organized.


22-46: Ohayo sensei: Double-check the compute_unique_hash for collisions.
While the logic is correct for combining name and member data, consider verifying potential collisions in large codebases or add extra domain separation if needed.


48-90: Ohayo sensei: Model key parsing is neat.
Good job enforcing that keys are declared before non-keys. This helps keep data structures consistent.


92-108: Ohayo sensei: serialize_keys_and_values methodical approach is straightforward.
Partitioning key and non-key serialization clarifies the flow for distinct sets of fields.


110-126: Ohayo sensei: deserialize_keys_and_values mirrors serialization properly.
This symmetry ensures that key fields and value fields align correctly before reconstruction.


128-177: Ohayo sensei: Tuple member destructuring is well-handled.
The solution elegantly tracks nested tuples and produces consistent output representations.


179-231: Ohayo sensei: serialize_member_ty & associated functions are consistent.
Grouping primitive vs. tuple serialization logic is clear and maintainable.


233-283: Ohayo sensei: deserialize_member_ty & associated functions appear robust.
The fallback to deserialize_primitive_member_ty or deserialize_tuple_member_ty ensures correct handling of nested and complex types.


285-349: Ohayo sensei: Tests comprehensively validate tuple destructuring.
These test cases adequately cover varied tuple shapes and data types, reinforcing correctness.

crates/dojo/core/src/storage/dojo_store.cairo (5)

1-5: Ohayo sensei! Nice use of a zero address as the default.
This implementation aligns well with typical default usage for a contract address, returning a “zero” contract address.


7-20: Ohayo sensei! Consider clarifying error handling for serialization and deserialization.
Currently, this trait just defers to Serde. If a deserialization error occurs, the returned Option<T> may not be sufficient to handle edge cases in a more robust manner. You may want to provide or reference a custom error approach to distinguish between uninitialized data and a genuine deserialization error.


22-39: Ohayo sensei! These direct trait implementations look solid.
They ensure consistent default behavior for each primitive and special type with minimal overhead.


40-64: Ohayo sensei! Verify the collision risk of treating 0 as uninitialized.
When the variant index is 0, the code yields Option::Some(Default::default()), which may collide with a genuine Some(Default::default()). If that collision is acceptable for your use case, great, but it might be worth documenting or reconsidering the variant numbering.


89-102: Ohayo sensei! Ensure array length is valid for deserialization.
The length is read from a felt, which might be negative or invalid if data is corrupted. You may wish to add a check before proceeding with remaining - 1.

Here is a possible safeguard:

 fn deserialize_array_helper<T, +Serde<T>, +DojoStore<T>, +Drop<T>>(
     ref serialized: Span<felt252>, mut curr_output: Array<T>, remaining: felt252,
 ) -> Option<Array<T>> {
+    // Early check for invalid lengths
+    if remaining < 0 {
+        return Option::None;
+    }
     if remaining == 0 {
         return Option::Some(curr_output);
     }
     ...
 }

@glihm
Copy link
Collaborator

glihm commented Mar 14, 2025

@remybar do we want to wait the proc macro or I can move forward reviewing this next week?

@remybar
Copy link
Collaborator Author

remybar commented Mar 15, 2025

@remybar do we want to wait the proc macro or I can move forward reviewing this next week?

No need to wait for proc macro as we don't know exactly when the new proc macro API will be officially available.
We can wait for an answer about the DojoStore "bug" with generic structs/enums or, if we have no answer this week, we can fix that in another PR and just temporary don't support them. I guess generic structs/enums are not used a lot at the moment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (1)

217-238: Consider additional checks in build_generic_impls
Ohayo sensei, the code is good as is, but you might consider detecting if no generics are provided and skipping the base impls to avoid trailing commas.

crates/dojo/lang/src/derive_macros/introspect/mod.rs (2)

192-215: Minor note on build_generic_types
If a user has constraints like T: SomeTrait, it might require extra merges in your code. For advanced usage, consider capturing trait bounds within the introspection.


217-238: build_generic_impls logic
The approach of formatting generics inline is effective. However, watch out for potential confusion if multiple lines of generics are needed. A multiline approach might be more readable.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1)

11-29: Structures and Enums for Complex Types
The synergy between S, SComplex, and EComplex is well-structured. The combination of nested tuples and options is tested robustly.

You might consider splitting SComplex into smaller sub-structs for clarity, especially if more fields are added in the future.

crates/dojo/core/src/storage/dojo_store.cairo (3)

1-6: Ohayo! ContractAddressDefault implementation might be temporary.

The comment on line 1 suggests this implementation might not be necessary from Cairo 2.11. Consider adding a more descriptive comment about why this default implementation is needed now and when it can be removed in the future.


44-68: Clever handling of Option variants!

This implementation directly addresses the PR objective of fixing the issue where variant id 0 can't be distinguished from an uninitialized variant. By using 1 for Some and 2 for None, you avoid conflicts with 0.

However, I notice that in the deserialize method, when encountering a 0 value, you return Default::default(). It would be helpful to add a comment explaining this special case for future maintainers.

fn deserialize(ref values: Span<felt252>) -> Option<Option<T>> {
    if let Option::Some(x) = values.pop_front() {
        return match *x {
+           // Handle case where value is 0 (uninitialized) by returning default
            0 => Option::Some(Default::default()),
            1 => Option::Some(DojoStore::<T>::deserialize(ref values)),
            2 => Option::Some(Option::None),
            _ => Option::None,
        };
    }

92-105: Good implementation for Array.

This implementation properly handles arrays by first serializing the length and then each element. The use of the helper functions keeps the code DRY.

One suggestion: consider adding error handling for the case where the length value is negative or unreasonably large, which could happen if corrupted data is passed. This could prevent potential issues when deserializing.

fn deserialize(ref values: Span<felt252>) -> Option<Array<T>> {
    let length = *values.pop_front()?;
+   // Safety check to prevent excessive memory allocation attempts
+   if length < 0 || length > 1000000 {
+       return Option::None;
+   }
    let mut arr = array![];
    deserialize_array_helper(ref values, arr, length)
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between e19dc4d and caf5bed.

📒 Files selected for processing (4)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
crates/dojo/lang/src/derive_macros/introspect/mod.rs (2)
crates/dojo/lang/src/lib.rs (1) (1)
  • debug_store_expand (46-53)
crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (2) (2)
  • build_struct_dojo_store (10-75)
  • build_enum_dojo_store (77-175)
🔇 Additional comments (17)
crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (5)

5-8: Ohayo sensei! Good addition of Default derivation
This ensures there's a well-defined default variant for your struct, which is especially useful for the new DojoStore approach.


58-64: Ohayo sensei! Great usage of the #[default] attribute
Flagging the first variant in EnumNoData as default aligns well with the shift from 0-based to 1-based indexing.


151-156: Watch out for variant indexing in _enum
Switching indexing to start from 1 is consistent with the approach to distinguish uninitialized (0). Just be sure it doesn't cause unexpected off-by-one logic in dependent code.


170-190: Your test for GenericStruct looks thorough
The assert_eq! checks confirm that the size and layout are as expected. Nicely done, sensei!


192-212: Kudos on the newly added test_generic_enum_introspect
The test effectively validates the variant indexing starting from 1 and ensures that the default variant is correct.

crates/dojo/lang/src/derive_macros/introspect/mod.rs (4)

10-10: Ohayo sensei! The itertools import
Bringing in itertools is a good call for more concise joining operations. Approved!


63-64: debug_store_expand usage
Nice debugging helper to reveal expansions for Dojo store. This can be helpful for troubleshooting compile-time expansions.


90-97: Ensuring Default is included for your enums
You include core::traits::Default<{enum_name_with_generics}> in the additional impls. This is critical for bridging uninitialized states. Great addition, sensei.


124-132: Smart approach with build_enum_dojo_store
Constructing the DojoStore impl for an enum hinges on a well-defined Default. This design choice ensures correct fallback for variant=0.

Could you confirm that all existing enums, including user-defined ones in other files, have implemented Default?

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (4)

3-9: Ohayo sensei! Good usage of #[default] in enum E
Defining the default variant as A ensures safe fallback if the variant is 0. No issues spotted.


55-221: Comprehensive tests for primitive DojoStore
This block thoroughly covers numeric types, bool, addresses, and byte arrays. It's an excellent reference for future expansions.


223-263: Kudos on dynamic array and option coverage
The test cases meticulously verify serialization and deserialization for arrays and Option<T>. Great job ensuring edge cases (like empty spans) are handled.


319-385: Generic struct & enum dojo store tests
Verifying for GenericStruct<u32> and GenericEnum<u32> is crucial. This ensures the trait expansions handle generics gracefully.

crates/dojo/core/src/storage/dojo_store.cairo (4)

8-12: Looking good, sensei! Clean trait definition.

The DojoStore trait is well-defined with clear method signatures for serialization and deserialization. This aligns perfectly with the PR objective of introducing a trait to handle storage edge cases.


14-24: Great default implementation using Serde.

The default implementation nicely encapsulates Serde calls as mentioned in the PR objectives. This provides a clean fallback for types that don't need special handling.


26-42: Comprehensive primitive type implementations, nice work!

You've covered all the necessary primitive types with the default Serde-based implementation. This ensures consistent behavior across the common data types.


70-90: Well-designed array helper functions, sensei!

These recursive helper functions for serializing and deserializing arrays are clean and efficient. The early return in deserialize_array_helper when remaining is 0 is a nice optimization.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
Cargo.toml (2)

214-216: Deprecating old scarb dependencies.
Ohayo sensei! The old scarb dependency lines that reference the dojoengine repository are now commented out as expected. Ensure you keep these comments for historical context or remove them if they’re no longer needed for clarity.


222-224: Local path alternative configuration.
Ohayo sensei! The commented-out local path configurations offer a neat alternative for local development setups. Ensure that these remain well-documented so that future contributors know when and how to activate this option if necessary.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between caf5bed and da08431.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • Cargo.toml (1 hunks)
🔇 Additional comments (1)
Cargo.toml (1)

218-221: Introducing new scarb dependencies.
Ohayo sensei! The new dependency definitions for scarb, scarb-metadata, and scarb-ui now point to the remybar fork with the commit hash a634d0103395efde82331336eb76c77eac937966. This update aligns with the PR objectives. Please verify that this commit hash corresponds to a stable, tested revision to avoid any surprises down the line.

@remybar remybar force-pushed the fix-enum_options_storage branch from da08431 to f2e1a84 Compare March 21, 2025 22:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
crates/dojo/core/src/storage/dojo_store.cairo (1)

82-106: 🛠️ Refactor suggestion

Ohayo sensei! Guard against invalid array length values.

When deserializing arrays (lines 100-104), we rely on a length from the serialized data without validating if it’s negative or excessively large. Consider validating the length to avoid potential panics or out-of-bounds issues.

 fn deserialize(ref values: Span<felt252>) -> Option<Array<T>> {
     let length = *values.pop_front()?;
+    // Basic check to avoid negative or unexpectedly large lengths
+    if length < 0 || length > 999_999 {
+       // Adjust threshold as needed
+       return Option::None;
+    }

     let mut arr = array![];
     deserialize_array_helper(ref values, arr, length)
 }
🧹 Nitpick comments (9)
crates/dojo/lang/src/utils.rs (3)

110-126: Ohayo sensei! Check for potential missing/extra fields.
The deserialize_keys_and_values approach nicely mirrors the serialization counterpart. However, consider verifying that the count of deserialized items matches the expected schema size.


128-177: Ohayo sensei! Tuple destructuring looks robust.
You handle nested tuples recursively, which is clean. The fallback to unimplemented! for unsupported expressions is acceptable for early development, but could be improved to provide descriptive errors or fallback logic.


258-283: Ohayo sensei! Consider unify vs. repeated calls.
The repeated pattern of calling destructure_tuple_member and then enumerating the items is consistent. It might be beneficial to unify certain aspects of tuple type handling across serialization and deserialization in a future refactor for maintainability.

crates/dojo/lang/src/derive_macros/introspect/layout.rs (1)

380-387: Consider providing more specific error message for Option types.

The error handling for Option types correctly prevents them from being packed, which is necessary since they need special handling with DojoStore.

However, the error message could be more specific about why Option types can't be packed, making debugging easier for developers.

-            message: format!("{item_type} cannot be packed."),
+            message: format!("{item_type} cannot be packed as it requires special handling by DojoStore for variant management."),
crates/dojo/lang/src/derive_macros/introspect/mod.rs (1)

63-73: Ohayo sensei: Using debug_store_expand
Debugging expansions can be immensely helpful when verifying macro outputs. Logging the generated store code for debugging is a practical technique—nice usage of environment toggling to keep logs optional.

crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (1)

85-87: Ohayo sensei: $model_type$DojoStore documentation
Your note clarifies how store implementations are derived for the main model struct. It's always good to keep references visible for future maintainers.

crates/dojo/core/src/storage/dojo_store.cairo (3)

8-12: Ohayo sensei! Validate error handling strategy for serialization/deserialization.

Currently, the trait does not return any error information if deserialization fails beyond returning Option::None. Consider using a more robust error-reporting approach so that calling code can differentiate between a legitimate "no data" scenario and an error in data parsing.


44-68: Ohayo sensei! Handle requirement for T: Default.

Lines 59-60 rely on Default::default() if the first popped felt is 0. If T doesn’t implement Default, this might fail at compile time. You may wish to add compile-time checks or doc comments to reinforce that T must implement Default, preventing confusion for end users.


70-81: Ohayo sensei! Consider iterative approach for array serialization.

The recursive function serialize_array_helper is elegant but might lead to deep recursion if the array is large. An iterative approach could improve stack usage, especially in resource-constrained or large-scale scenarios.

 fn serialize_array_helper<T, +DojoStore<T>, +Drop<T>>(
     mut input: Span<T>, ref output: Array<felt252>,
 ) {
-    match input.pop_front() {
-        Option::Some(value) => {
-            DojoStore::serialize(value, ref output);
-            serialize_array_helper(input, ref output);
-        },
-        Option::None => {},
-    }
+    loop {
+        let item = input.pop_front();
+        match item {
+            Option::Some(value) => DojoStore::serialize(value, ref output),
+            Option::None => break,
+        };
+    }
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between da08431 and f2e1a84.

⛔ Files ignored due to path filters (3)
  • Cargo.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (48)
  • Cargo.toml (1 hunks)
  • crates/benches/contracts/src/models/character.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/lib.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (4 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/meta/introspect.cairo (1 hunks)
  • crates/dojo/core/src/meta/layout.cairo (1 hunks)
  • crates/dojo/core/src/model/component.cairo (1 hunks)
  • crates/dojo/core/src/model/definition.cairo (1 hunks)
  • crates/dojo/core/src/model/interface.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (4 hunks)
  • crates/dojo/core/src/model/model_value.cairo (2 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/layout.cairo (8 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (1 hunks)
  • crates/dojo/lang/Cargo.toml (1 hunks)
  • crates/dojo/lang/src/attribute_macros/element.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/mod.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3 hunks)
  • crates/dojo/lang/src/cairo_plugin.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs (3 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/mod.rs (1 hunks)
  • crates/dojo/lang/src/lib.rs (2 hunks)
  • crates/dojo/lang/src/utils.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/model.rs (2 hunks)
  • crates/dojo/world/src/contracts/model.rs (2 hunks)
  • crates/sozo/ops/src/model.rs (8 hunks)
  • crates/torii/sqlite/src/model.rs (1 hunks)
  • crates/torii/types-test/src/models.cairo (1 hunks)
  • examples/spawn-and-move/dojo_dev.toml (1 hunks)
  • examples/spawn-and-move/src/models.cairo (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/dojo/lang/src/attribute_macros/mod.rs
  • crates/dojo/lang/src/attribute_macros/element.rs
🚧 Files skipped from review as they are similar to previous changes (30)
  • examples/spawn-and-move/src/models.cairo
  • crates/dojo/core/src/model/definition.cairo
  • crates/dojo/core/src/model/component.cairo
  • crates/dojo/core/src/model/interface.cairo
  • crates/dojo/core-cairo-test/src/lib.cairo
  • crates/dojo/lang/Cargo.toml
  • crates/dojo/lang/src/derive_macros/mod.rs
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs
  • crates/dojo/core/src/lib.cairo
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo
  • crates/dojo/core/src/storage/layout.cairo
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/dojo/core/src/world/world_contract.cairo
  • crates/torii/sqlite/src/model.rs
  • examples/spawn-and-move/dojo_dev.toml
  • crates/dojo/world/src/contracts/abigen/model.rs
  • crates/dojo/core/src/meta/introspect.cairo
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo
  • crates/dojo/lang/src/attribute_macros/event.rs
  • crates/dojo/core/src/model/model_value.cairo
  • crates/dojo/lang/src/lib.rs
  • crates/benches/contracts/src/models/character.cairo
  • Cargo.toml
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo
  • crates/dojo/lang/src/cairo_plugin.rs
  • crates/torii/types-test/src/models.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo
  • crates/sozo/ops/src/model.rs
🧰 Additional context used
🧬 Code Definitions (4)
crates/dojo/lang/src/derive_macros/introspect/layout.rs (1)
crates/dojo/lang/src/derive_macros/introspect/utils.rs (4) (4)
  • is_array (34-36)
  • is_byte_array (30-32)
  • is_option (42-44)
  • is_tuple (38-40)
crates/dojo/world/src/contracts/model.rs (2)
crates/dojo/world/src/contracts/abigen/model.rs (2) (2)
  • use_legacy_storage (673-682)
  • use_legacy_storage (802-811)
crates/torii/sqlite/src/model.rs (1) (1)
  • use_legacy_storage (117-120)
crates/dojo/lang/src/derive_macros/introspect/mod.rs (2)
crates/dojo/lang/src/lib.rs (1) (1)
  • debug_store_expand (46-53)
crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (2) (2)
  • build_struct_dojo_store (10-75)
  • build_enum_dojo_store (77-175)
crates/dojo/lang/src/utils.rs (1)
crates/dojo/lang/src/attribute_macros/event.rs (2) (2)
  • members (90-99)
  • members (101-104)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (91)
crates/dojo/lang/src/utils.rs (10)

13-20: Ohayo sensei! Good use of conditional paths.
This function cleanly switches between Serde and DojoStore. The inline attribute is a nice touch for performance.


22-46: Ohayo sensei! Verify handling of variant collisions.
The logic for hashing element names and members is straightforward. However, consider whether a more explicit method to detect collisions (e.g., consistent logging or diagnostics) might be needed to warn users of conflicting hashes in large projects. Otherwise, this is well-structured.

Would you like a script to scan the codebase for repeated calls to compute_unique_hash on different sets of members?


48-90: Ohayo sensei! Enforce the “keys-before-values” rule carefully.
The function enforces that all key fields appear before non-key fields. This is great. Just be sure any later merging or code generation processes are consistent with this assumption (e.g., potential dynamic object creation).


92-108: Ohayo sensei! Validate key vs. values usage.
The approach for separating keys and values is well-organized. Ensure that no code path attempts to serialize members that are not recognized as keys or values (e.g., attributes other than "key").


179-192: Ohayo sensei! Consistent naming for “with_self” usage.
This function introduces a with_self parameter that is well-explained. Ensure that the naming is consistently used in related helper functions for clarity.


194-205: Ohayo sensei! Straightforward approach for serializing primitives.
The usage of the chosen serialization path is concise. No obvious issues found.


207-231: Ohayo sensei! Named destructuring with central reuse.
You nicely reuse destructure_tuple_member here. Be mindful of performance if there are extremely large nested tuples—though this is likely not a concern for typical usage.


233-247: Ohayo sensei! Straightforward bridging from types to the correct path.
This function focuses well on bridging between tuple-based and primitive-based handling. The logic is consistent with the rest of the file.


249-256: Ohayo sensei! Return clarity of function usage.
The code to retrieve the appropriate path and deserialize is correct. Including the ? operator for error propagation is a good practice for readability.


285-350: Ohayo sensei! Test coverage is impressive for tuple scenarios.
The collection of use cases for nested tuples is thorough. Confirm that other, more complex custom types are similarly tested.

crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1)

18-21: LGTM! Clean implementation for event deserialization.

Ohayo, sensei! This implementation correctly adds deserialization support for events using Serde, which aligns with the intent that event data isn't stored in world storage (so it doesn't need the DojoStore trait handling). The comment clearly explains the reasoning.

crates/dojo/core/src/meta/layout.cairo (1)

52-110: Excellent implementation for supporting legacy storage layouts!

Ohayo, sensei! This function does exactly what we need for maintaining backward compatibility with legacy Dojo models. The function correctly handles all layout types:

  1. For Enum layouts: Properly decrements the selector by 1 to match legacy numbering
  2. For Struct layouts: Preserves selectors while recursively processing nested layouts
  3. For Tuple/Array layouts: Correctly processes nested contents

The clear documentation explains the reasoning perfectly - legacy variants started from 0, while the new DojoStore system starts from 1 to distinguish uninitialized variants.

crates/dojo/world/src/contracts/model.rs (2)

61-61: Interface addition for legacy storage detection.

Ohayo, sensei! This trait method addition aligns perfectly with the PR's goal of allowing models to opt in or out of the new DojoStore trait functionality.


209-211: Clean implementation of the legacy storage detection.

This implementation correctly delegates to the model_reader's use_legacy_storage method. The pattern matches other methods in this class like layout(), packed_size(), etc.

crates/dojo/lang/src/derive_macros/introspect/layout.rs (2)

10-10: Good dependency addition.

Ohayo! Adding the import for is_option is necessary to support the new Option type validation below.


78-80: Good change for the DojoStore variant handling.

Ohayo, sensei! This change correctly increments the enum variant selectors to start from 1 instead of 0, allowing 0 to represent uninitialized variants in the new DojoStore system. The comment clearly explains the reasoning.

crates/dojo/core/src/model/model.cairo (4)

53-54: Ohayo! New method to deserialize model data

The addition of deserialize to the ModelParser trait is a solid design choice and aligns with the PR objectives. This centralizes model deserialization logic, which will be critical for handling Option<T> and custom enums correctly.


84-86: Clean implementation of storage mode indicator

The use_legacy_storage method provides a clear way to determine whether a model uses legacy or new storage behavior. This boolean flag will be essential for maintaining backward compatibility while enabling the new storage system.


135-135: Delegating to the specialized parser, excellent!

Updating from_serialized to use the new ModelParser::<M>::deserialize method ensures consistent deserialization behavior throughout the codebase. This is a key part of implementing the DojoStore trait functionality.


154-156: Delegation to ModelDefinition is the right approach

The implementation properly delegates to ModelDefinition::<M>::use_legacy_storage(), maintaining separation of concerns and ensuring consistent behavior across the system.

crates/dojo/core-cairo-test/src/tests/model/model.cairo (6)

65-71: Clean enum definition with Default implementation

The MyEnum implementation with a #[default] attribute on the Z variant is excellent, sensei! This addresses the core issue described in the PR objective where the first variant ID (0) cannot be distinguished from an uninitialized variant.


73-81: Well-structured LegacyModel with DojoLegacyStorage attribute

This model will use the legacy storage system as indicated by the DojoLegacyStorage derive. It provides a solid example for testing how legacy models should be defined and handled.


83-91: DojoStoreModel properly configured for new storage system

By omitting the DojoLegacyStorage attribute, this model will use the new storage system with the DojoStore trait. Good job creating a contrasting example to test both storage approaches!


101-102: Test models correctly registered in namespace

Adding the new models to the namespace definition ensures they'll be properly recognized during testing. This is essential for comprehensive test coverage.


375-460: Comprehensive test for legacy model implementation

Ohayo! This test thoroughly validates the legacy storage model behavior by:

  1. Verifying the expected layout for legacy storage
  2. Confirming the distinction between ModelDefinition layout and Introspect layout
  3. Testing serialization and deserialization with appropriate variant indexing

The test successfully verifies that variant IDs remain at 0, preserving backward compatibility.


462-543: Excellent verification of DojoStore model implementation

This test confirms the new storage system behavior by:

  1. Verifying the layout matches with indices starting from 1
  2. Testing serialization with incremented variant indices (1, 2, 3 instead of 0, 1, 2)
  3. Validating deserialization works with the new indexing

The test effectively demonstrates the solution to the original problem, where variant ID 0 can now be distinguished from an uninitialized variant.

crates/dojo/lang/src/attribute_macros/model.rs (6)

21-21: Adding DojoLegacyStorage derive constant

Introducing DOJO_LEGACY_STORAGE_DERIVE as a constant maintains consistency with other derive attributes. Good practice, sensei!


67-69: Detecting legacy storage from derive attributes

This logic correctly identifies models using legacy storage by checking for the DojoLegacyStorage derive attribute. This flag drives the subsequent code generation paths.


89-119: Improved member parsing with validation

Ohayo! The refactoring of model member parsing provides several benefits:

  1. Better validation of key members ordering
  2. Clear error diagnostics for incorrect ordering
  3. Logical structuring using the parsing_keys flag

This maintains the important constraint that key members must come before non-key members, which is essential for correct model structure.


124-142: Storage-aware serialization and deserialization

Passing the use_legacy_storage flag to serialization and deserialization functions ensures the correct behavior is used based on the model's storage mode. This is a key part of implementing the dual storage approach.


225-239: Smart conditional code generation based on storage mode

The conditional generation of model deserialize path and layout is elegant:

  • For legacy storage: Uses core::serde::Serde and build_legacy_layout
  • For new storage: Uses dojo::storage::DojoStore and direct introspection layout

This approach maintains backward compatibility while enabling the new storage system.


264-265: Exposing legacy storage flag to generated code

Including the use_legacy_storage boolean in the code interpolation ensures that the generated model implementation will have access to this critical information at runtime.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (5)

5-11: Adding Serde and Default derives to existing types

Ohayo, sensei! Adding Serde and Default to these structs and enums is an excellent enhancement. This ensures they can be properly serialized/deserialized and have default values, which is essential for the new storage system to work with them.

Also applies to: 16-35, 40-53, 58-110


117-120: Renamed Generic to GenericStruct with Serde support

The rename improves clarity and the addition of Serde ensures the generic struct can work with the new storage system. Good improvement!


122-126: New GenericEnum with Default implementation

Adding GenericEnum<T> with the Default trait provides a great test case for generic enum types in the storage system. The #[default] attribute on variant A ensures it has a proper default value.


151-156: Critical change to variant indexing

This is a key change that directly addresses the PR objective! By incrementing variant indices to start from 1 instead of 0, you solve the problem of distinguishing an uninitialized variant (0) from the first actual variant.


170-212: Updated tests for generic types

The tests have been properly updated to:

  1. Test the renamed GenericStruct<T>
  2. Add verification for the new GenericEnum<T>
  3. Confirm correct variant indexing in the layouts

These tests ensure that the generic types work correctly with the new storage system.

crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (4)

1-9: Ohayo sensei: Imports are clear and well-chosen
These imports from Cairo's syntax and the utility module directly support struct/enum AST processing. Overall, they're neatly organized and relevant to the Dojo store logic.


10-75: Ohayo sensei: build_struct_dojo_store logic looks solid
This function cleanly differentiates tuple-type members from primitive members, generating appropriate serialization and deserialization code segments. The iteration over struct members is straightforward and well-structured. Good job ensuring each primitive vs. tuple scenario is handled distinctly.


77-145: Ohayo sensei: build_enum_dojo_store covers variants comprehensively
Your approach to matching tuple variants, empty variants, and other data variants is well-organized. The code generation for each variant, including the variant index increment, is neatly encapsulated. This ensures each variant is distinct, satisfying the PR objective of handling uninitialized variant 0.


167-171: Ohayo sensei: Fallback to default & potential array underflow
This line implements 0 => Option::Some(Default::<{name}<{generic_types}>>::default()). It ensures a safe fallback for uninitialized variants, matching the PR's stated approach. However, please remain cautious about potential array underflows if values is unexpectedly empty when popping the variant.

crates/dojo/lang/src/derive_macros/introspect/mod.rs (8)

10-14: Ohayo sensei: Adding the itertools dependency
Pulling in itertools::Itertools is a neat choice for combining strings and arrays elegantly. This keeps the code concise where you join multiple generics or trait bounds.


28-38: Ohayo sensei: Handling generics in handle_introspect_struct
The logic that builds generic type lists and corresponding trait impl strings is well-organized. Combining +dojo::meta::introspect::Introspect ensures we always have introspection on the struct’s generics.


55-61: Ohayo sensei: Generating the Dojo store
Calling build_struct_dojo_store here aligns with your approach to unify introspection and store logic in one pass. This is a clean solution that makes the final generated code more powerful.


85-96: Ohayo sensei: handle_introspect_enum generics
Extending the base impls with both DojoStore and Serde ensures your complex enums can be reliably serialized. The additional default bound is fitting for fallback logic.


124-131: Ohayo sensei: Building the Dojo store for enums
build_enum_dojo_store usage here is consistent with handle_introspect_struct. This symmetrical design in your macros fosters clarity and maintainability.


133-143: Ohayo sensei: Debugging and final introspection
The repeated pattern of debug expansion followed by the core introspection generation keeps your code flow consistent. This makes the final expansions predictable.


155-186: Ohayo sensei: Merging Dojo store logic in introspect rewrite
Injecting $dojo_store$ into the same rewrite node that handles size, layout, and type is a clever approach that keeps all codegen consistent. This single final output merges store logic with meta introspection.


192-237: Ohayo sensei: build_generic_types & build_generic_impls
These builder functions streamline the composable generation of trait bounds. Leveraging Itertools for transformations and joins is elegantly succinct. This sets a good foundation for scaling more advanced generics.

crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (5)

29-29: Ohayo sensei: $model_layout$ reference
Replacing direct introspection calls with an injected $model_layout$ variable provides greater flexibility to customize or override layout logic while still integrating with the rest of your macros.


32-35: Ohayo sensei: Introducing use_legacy_storage()
This function toggle is in line with the PR’s approach to maintain pre-existing storage behavior. Marking legacy usage explicitly will help keep new and old usage patterns separate.


59-61: Ohayo sensei: Model deserialize for $model_type$
This new function delegates to $model_deserialize_path$ cleanly, aligning with how you handle custom macros for model serialization. It's straightforward, which should simplify refactoring or extension later.


75-77: Ohayo sensei: Model value deserialize
Mirroring the same approach used in $model_type$, you maintain consistency in the deserialization path for the $model_type$Value. This consistency will ease debugging.


88-96: Ohayo sensei: $model_type$ValueDojoStore
Defining serialize and deserialize for the $model_type$Value explicitly is a great complement to storing the model itself, ensuring that values can be processed separately. You handle the arrays for serialization thoroughly, including the final assembly into $deserialized_modelvalue$.

crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (17)

11-52: Ohayo sensei! Thorough coverage in test_fixed_layout.
The step-by-step writes, reads, and deletes confirm every stage works as intended. Consider extracting common logic for writing/reading default values into a helper if you find yourself duplicating these steps frequently.


54-102: Ohayo sensei! Great job with test_tuple_layout.
The multi-section offset handling is clear, and the negative tests are well-defined by separate functions.


105-147: Ohayo sensei! Good approach in test_byte_array_layout.
Reading uninitialized data as [0,0,0] and verifying offsets is excellent for ensuring robust coverage.


150-164: Ohayo sensei! test_read_byte_array_layout_invalid_array_length is well-defined.
Verifying the panic path helps ensure misconfigured lengths don't slip through.


166-175: Ohayo sensei! The test_byte_array_layout_bad_input_length scenario is handled thoroughly.
Great usage of #[should_panic] for negative tests.


177-186: Ohayo sensei! test_byte_array_layout_bad_array_length_value effectively checks overflow conditions.
This helps confirm robust bounds checking.


188-197: Ohayo sensei! test_byte_array_layout_bad_array_length handles invalid lengths gracefully.
Another excellent negative test to prevent inconsistent data.


199-241: Ohayo sensei! test_array_layout shows consistent patterns for array reads and writes.
The offset-based approach is well-demonstrated and likely to be stable in production.


243-259: Ohayo sensei! test_read_array_layout_bad_array_length confirms array length invariants under error conditions.
It’s good to see panic scenarios tested early.


261-273: Ohayo sensei! test_array_layout_bad_values_length is a neat negative test.
Short and clear. No issues spotted.


275-286: Ohayo sensei! test_array_layout_bad_array_length once again ensures we handle invalid array lengths safely.
Consistent with other negative tests.


288-334: Ohayo sensei! test_struct_layout is well-organized and verifies default reads, writes, and deletions.
All fields appear logically sound.


336-382: Ohayo sensei! test_enum_layout thoroughly checks default variant handling and removal.
The structure is cohesive, and no concerns jump out.


384-403: Ohayo sensei! test_read_enum_layout_invalid_variant_value properly verifies variant bounds.
This ensures the system won't interpret out-of-range values.


405-424: Ohayo sensei! test_read_enum_layout_unexisting_variant_value is another solid check.
Covers an important edge case for enumerations.


426-441: Ohayo sensei! test_enum_layout_invalid_variant_value confirms the panic scenario for out-of-range variants.
Well tested.


443-458: Ohayo sensei! test_enum_layout_unexisting_variant also ensures out-of-bounds variant selection triggers a panic.
This final test completes the negative coverage.

crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (7)

4-19: Ohayo sensei! test_build_legacy_layout_option is clear and spot-on.
Mapping Option<T> to an enum layout for ‘Some’ and ‘None’ is well validated.


21-44: Ohayo sensei! test_build_legacy_layout_simple_enum elegantly checks variant index shifting.
Ensures that old variant 1..4 maps to new 0..3. Good approach.


46-71: Ohayo sensei! test_build_legacy_layout_tuple confirms nested layout transformations.
It’s great to see the Option nested inside the tuple.


73-95: Ohayo sensei! test_build_legacy_layout_array ensures arrays of Option<T> handle variant increments properly.
All array logic is validated nicely.


97-129: Ohayo sensei! test_build_legacy_layout_struct thoroughly covers assembling fields with Option<T>.
No issues, excellent clarity in verifying the final struct layout.


131-194: Ohayo sensei! test_build_legacy_layout_nested_enum is solid.
You’re verifying multiple nesting levels with arrays and tuples in the same enum, which is very comprehensive.


196-260: Ohayo sensei! test_build_legacy_layout_nested_struct is a robust test for multi-layered structures.
Handling arrays and tuples inside a struct is well validated.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (11)

55-221: Ohayo sensei! test_dojo_store_primitives demonstrates thorough coverage of numeric and address types.
All baseline types appear correctly serialized and deserialized. Nicely done.


223-242: Ohayo sensei! test_dojo_store_dynamic_arrays robustly checks array serialization lengths, including edge cases for Option::None.
Well-structured approach to confirm correctness.


244-263: Ohayo sensei! test_dojo_store_option elegantly handles Some/None states with distinct indexes.
This is consistent with the modifications for legacy or new variant indexing.


265-284: Ohayo sensei! test_dojo_store_enums verifies each enum variant’s index offset.
Keen fallback scenario for out-of-range variants returning Option::None.


286-301: Ohayo sensei! test_dojo_store_structs is straightforward.
Ensures fields are serialized in order, with coverage for partial or empty inputs.


303-317: Ohayo sensei! test_dojo_store_tuples uses nested tuples in E::C.
Neat usage to confirm multi-value arrangement.


319-334: Ohayo sensei! test_dojo_store_generic_struct proves that DojoStore can handle generic fields.
No issues spotted.


336-351: Ohayo sensei! test_dojo_store_generic_enum demonstrates consistent enum indexing for generic payloads.
The approach is polished.


355-369: Ohayo sensei! test_dojo_store_use_generic_struct ensures a struct with generics remains consistent during serialization.
Great extension of the coverage.


371-386: Ohayo sensei! test_dojo_store_use_generic_enum is also well-handled.
Confirms that references to GenericEnum become stable across transitions.


388-443: Ohayo sensei! test_mix thoroughly stress-tests nested structures with arrays and options.
This is an excellent integration test to verify everything holds together.

crates/dojo/core/src/storage/dojo_store.cairo (2)

1-6: Ohayo sensei! Consider verifying necessity of the custom default implementation.

While this impl safely provides a default for ContractAddress, it might be redundant if newer versions of Cairo (e.g., ≥2.11) automatically derive defaults for this type.

Would you like to confirm if the default implementation is still needed for your specific compiler version or if we can remove it to reduce maintenance overhead?


14-24: Ohayo sensei! Confirm Serde-based default’s coverage of all scenarios.

This block relies on Serde for all types. If certain types require custom (de)serialization logic (e.g., additional checks or transformations), we might consider specialized trait implementations instead of the default.

@remybar remybar force-pushed the fix-enum_options_storage branch from d5f46fb to d20c0e0 Compare March 25, 2025 19:35
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
crates/dojo/core-cairo-test/src/tests/world/model.cairo (1)

75-77: Consider adding PartialEq trait to AnotherEnum for consistency

Ohayo! I noticed that you've added both Default and PartialEq traits to MyEnum, but only Default to AnotherEnum. For consistency across your enum types, consider adding PartialEq to AnotherEnum as well, unless there's a specific reason not to include it, sensei.

-#[derive(Introspect, Copy, Drop, Serde, Default)]
+#[derive(Introspect, Copy, Drop, Serde, Default, PartialEq)]
enum AnotherEnum {
    #[default]
    X: bool,
}
crates/dojo/lang/src/utils.rs (1)

285-349: Good test coverage for tuple destructuring

Ohayo, sensei! The tests thoroughly validate the destructure_tuple_member function with various tuple structures.

Consider adding tests for the other key functions like serialization and deserialization to ensure their correctness across different scenarios, particularly for edge cases in the new DojoStore implementation.

crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1)

1-458: Consider extracting common test patterns.

Ohayo sensei! While the tests are comprehensive and well-structured, there's significant repetition in the testing patterns. Consider extracting common testing sequences into helper functions to reduce code duplication and make the tests more maintainable.

+// Helper function for basic layout test pattern
+fn test_layout_operations<T>(
+    layout_type: &str,
+    layout: T,
+    read_fn: fn(felt252, felt252, ref Array<felt252>, T),
+    write_fn: fn(felt252, felt252, Span<felt252>, ref usize, T),
+    delete_fn: fn(felt252, felt252, T),
+    test_values: Array<felt252>
+) {
+    const MODEL_KEY: felt252 = 1;
+    const KEY: felt252 = 2;
+
+    // Read uninitialized data
+    let mut read_data = array![];
+    read_fn(MODEL_KEY, KEY, ref read_data, layout);
+    
+    // Then write and read back
+    // ... rest of common testing pattern
+}
crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1)

1-260: Consider adding explanatory comments about variant mapping.

Ohayo sensei! While the tests are comprehensive, they would benefit from more explanatory comments about why variant indices are shifted (from 1-based to 0-based). This would make the tests more self-documenting and help future developers understand the reasoning behind the legacy layout transformations.

+// In the new DojoStore implementation, enum variants are numbered starting from 1,
+// with 0 reserved for Default handling. However, in legacy storage, variants started
+// from 0. The build_legacy_layout function transforms the layout accordingly.
crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (2)

216-220: Ohayo sensei, there's a mismatch in the error messages for ByteArray tests.
They currently mention i128 instead of ByteArray, which may cause confusion when reading test failures.

Use this diff to correct the messages:

-assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<i128> serialization failed");
+assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<ByteArray> serialization failed");

-assert_eq!(res, Option::Some("hello"), "DojoStore<i128> deserialization failed");
+assert_eq!(res, Option::Some("hello"), "DojoStore<ByteArray> deserialization failed");

55-443: Ohayo sensei, consider DRY-ing up repeated serialization/deserialization code in tests.
Many test blocks follow a similar pattern. You could refactor by extracting a helper function or macro to eliminate duplication and make the file more maintainable, especially as new types are added in the future.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between d5f46fb and 3e4fe96.

⛔ Files ignored due to path filters (3)
  • Cargo.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (49)
  • Cargo.toml (1 hunks)
  • crates/benches/contracts/src/models/character.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/lib.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (4 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1 hunks)
  • crates/dojo/core/src/event/component.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/meta/introspect.cairo (1 hunks)
  • crates/dojo/core/src/meta/layout.cairo (1 hunks)
  • crates/dojo/core/src/model/component.cairo (1 hunks)
  • crates/dojo/core/src/model/definition.cairo (1 hunks)
  • crates/dojo/core/src/model/interface.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (4 hunks)
  • crates/dojo/core/src/model/model_value.cairo (2 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/layout.cairo (8 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (1 hunks)
  • crates/dojo/lang/Cargo.toml (1 hunks)
  • crates/dojo/lang/src/attribute_macros/element.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/mod.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3 hunks)
  • crates/dojo/lang/src/cairo_plugin.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs (3 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/mod.rs (1 hunks)
  • crates/dojo/lang/src/lib.rs (2 hunks)
  • crates/dojo/lang/src/utils.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/model.rs (2 hunks)
  • crates/dojo/world/src/contracts/model.rs (2 hunks)
  • crates/sozo/ops/src/model.rs (8 hunks)
  • crates/torii/sqlite/src/model.rs (1 hunks)
  • crates/torii/types-test/src/models.cairo (1 hunks)
  • examples/spawn-and-move/dojo_dev.toml (1 hunks)
  • examples/spawn-and-move/src/models.cairo (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/dojo/lang/src/attribute_macros/mod.rs
  • crates/dojo/lang/src/attribute_macros/element.rs
🚧 Files skipped from review as they are similar to previous changes (32)
  • crates/dojo/core/src/model/definition.cairo
  • crates/dojo/core/src/model/interface.cairo
  • crates/dojo/lang/Cargo.toml
  • crates/dojo/lang/src/derive_macros/mod.rs
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo
  • crates/dojo/core/src/lib.cairo
  • crates/torii/sqlite/src/model.rs
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs
  • crates/dojo/core/src/world/world_contract.cairo
  • crates/dojo/core-cairo-test/src/lib.cairo
  • crates/dojo/core/src/model/component.cairo
  • examples/spawn-and-move/src/models.cairo
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo
  • crates/torii/types-test/src/models.cairo
  • crates/dojo/core/src/event/component.cairo
  • crates/dojo/lang/src/lib.rs
  • crates/dojo/core/src/meta/introspect.cairo
  • examples/spawn-and-move/dojo_dev.toml
  • crates/dojo/core/src/storage/layout.cairo
  • crates/benches/contracts/src/models/character.cairo
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs
  • crates/dojo/lang/src/cairo_plugin.rs
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo
  • Cargo.toml
  • crates/dojo/lang/src/attribute_macros/event.rs
  • crates/sozo/ops/src/model.rs
  • crates/dojo/core/src/storage/dojo_store.cairo
🧰 Additional context used
🧬 Code Definitions (4)
crates/dojo/lang/src/derive_macros/introspect/layout.rs (1)
crates/dojo/lang/src/derive_macros/introspect/utils.rs (6)
  • get_array_item_type (46-52)
  • get_tuple_item_types (56-96)
  • is_array (34-36)
  • is_byte_array (30-32)
  • is_option (42-44)
  • is_tuple (38-40)
crates/dojo/world/src/contracts/model.rs (2)
crates/dojo/world/src/contracts/abigen/model.rs (2)
  • use_legacy_storage (673-682)
  • use_legacy_storage (802-811)
crates/torii/sqlite/src/model.rs (1)
  • use_legacy_storage (117-120)
crates/dojo/lang/src/derive_macros/introspect/mod.rs (2)
crates/dojo/lang/src/lib.rs (1)
  • debug_store_expand (46-53)
crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (2)
  • build_struct_dojo_store (10-75)
  • build_enum_dojo_store (77-175)
crates/dojo/lang/src/utils.rs (1)
crates/dojo/lang/src/attribute_macros/event.rs (2)
  • members (90-99)
  • members (101-104)
🔇 Additional comments (78)
crates/dojo/core-cairo-test/src/tests/world/model.cairo (1)

59-61: Ohayo! Good implementation of Default trait for enums.

The addition of the Default trait and marking the X variant as default is an excellent solution for the storage issue with custom enums. This will allow proper distinction between uninitialized variants and actual variant 0, which is crucial for the DojoStore trait implementation you're introducing, sensei.

crates/dojo/lang/src/utils.rs (13)

1-11: Ohayo! Clean imports with good organization

The imports are well-organized, properly separating Cairo language, syntax, and Dojo-specific dependencies. The use of the poseidon hash for cryptographic purposes is appropriate for generating unique identifiers.


13-20: Well-designed serialization path selection function, sensei!

This function cleanly implements the core mechanism that powers the PR's objective of supporting both Serde and DojoStore serialization paths. The inline attribute is appropriate for this simple but frequently called function.


22-46: Solid implementation of the hash computation

The unique hash computation is well-documented and correctly implemented. It uses poseidon_hash_many to create a deterministic identifier based on element properties and member information, which is essential for ensuring uniqueness in element contracts.


48-90: Nice enforcement of key member order, sensei!

The member parsing logic correctly enforces that key members are defined before non-key members, with appropriate error handling. The function maintains a boolean flag for tracking the parsing state and provides helpful error messages.


92-108: Elegant separation of serialization concerns

This function effectively separates members into keys and values based on attributes, which aligns with the PR's objective of handling specific storage cases.


110-126: Clean parallel implementation for deserialization

The deserialization function mirrors the serialization approach, maintaining consistency in the codebase. Good job maintaining symmetry between these related functions.


128-177: Excellent tuple handling with clear documentation

The tuple destructuring function is well-documented with examples that make its purpose and behavior clear. The implementation correctly handles nested tuples and maintains the index count across recursion.


179-192: Good pattern matching for member type serialization

This function cleanly dispatches to the appropriate serialization method based on the member type.


194-205: Clear primitive type serialization

The function correctly handles the serialization path selection based on the use_serde flag, supporting the PR's objective of mixed storage approaches.


207-231: Thoughtful handling of tuple serialization

The function has a special case for empty tuples and properly destructures complex tuple structures. The code generates formatted serialization code that will work correctly for nested tuples.


233-247: Well-structured deserialization dispatch

This function appropriately dispatches between tuple and primitive deserialization methods.


249-256: Concise primitive type deserialization

The function correctly handles the deserialization path selection and generates appropriate code for primitive types.


258-283: Comprehensive tuple deserialization

The function handles empty tuples gracefully and correctly destructures complex tuple structures for deserialization.

crates/dojo/core/src/model/model_value.cairo (2)

7-8: Ohayo, sensei! New deserialize method added for greater model value flexibility.

This new method enhances the ModelValueParser trait by providing custom deserialization capabilities, which is crucial for handling the difference between legacy and new storage models. It complements the existing serialize_values method, creating a more robust serialization/deserialization interface.


37-37: Implementation correctly updated to use new deserialize method!

The change from Serde::<V>::deserialize to ModelValueParser::<V>::deserialize aligns with the introduction of the DojoStore trait, enabling the special handling needed for Option and custom enums. This leverages the customized deserialization logic that addresses the variant identification issue.

crates/dojo/core/src/meta/layout.cairo (1)

52-110: Ohayo! Excellent implementation of legacy support for variant indices.

The build_legacy_layout function properly transforms the new storage layout (where variants start from 1) to the legacy format (where variants start from 0) by decrementing enum selectors. The recursive approach correctly handles nested layouts, ensuring complete transformation across complex data structures.

The implementation properly handles different layout types:

  • For Enum layouts: decrements selectors by 1
  • For Struct layouts: preserves selectors
  • For Tuple and Array layouts: recursively processes contained layouts
  • For other layouts: returns them unchanged

This enables seamless backward compatibility with existing Dojo models.

crates/dojo/world/src/contracts/abigen/model.rs (2)

671-682: Clean implementation of use_legacy_storage method for ModelContract!

The method properly constructs a function call to the contract's use_legacy_storage entry point, following the established pattern of other methods in this class. This enables checking whether a model uses legacy storage, which is crucial for the proper (de)serialization of models.


800-811: Consistent implementation for ModelContractReader, nice work sensei!

This implementation mirrors the one in ModelContract, providing the same functionality for read-only contract interactions. The consistent approach across both classes ensures that legacy storage checking is available regardless of how the contracts are accessed.

crates/dojo/world/src/contracts/model.rs (2)

61-61: Good addition to the ModelReader trait interface!

Adding the use_legacy_storage method to the trait ensures that all implementations will provide this functionality, maintaining a consistent interface for model interactions regardless of the underlying storage model.


209-211: Clean implementation of use_legacy_storage in ModelRPCReader!

The implementation correctly delegates to the model_reader's method and wraps the result in an Ok variant, following the established pattern of other methods in this class. This enables the ModelRPCReader to check legacy storage status without duplicating logic.

crates/dojo/lang/src/derive_macros/introspect/layout.rs (3)

10-10: The new import enhances type detection capabilities.

The addition of is_option to the imports list aligns with the new functionality to properly handle Option types within the layout system.


78-80: Ohayo, variants now start from 1 instead of 0!

This critical change ensures that variant 0 can be used as an uninitialized state, allowing the system to differentiate between default and explicitly set values. This addresses the core issue mentioned in the PR objectives.


380-386: Added proper error handling for packed Option types, nice work sensei!

This new conditional branch prevents users from attempting to pack Option types, which would lead to runtime errors. The clear error message helps developers understand the limitation.

crates/dojo/core/src/model/model.cairo (4)

53-54: New deserialization method enhances the ModelParser trait.

This addition provides a standardized way to convert raw data into model structs, supporting the new DojoStore trait functionality.


84-86: Added storage type detection capability, sugoi!

This method allows the system to determine whether a model should use legacy or new storage mechanisms, providing the flexibility mentioned in the PR objectives.


135-135: Updated to use the new deserialization approach.

The implementation now calls ModelParser::<M>::deserialize instead of directly using Serde, enabling the custom serialization logic needed for the DojoStore trait.


154-156: Implemented the storage type detection method.

This implementation delegates to ModelDefinition::<M>::use_legacy_storage(), ensuring consistent behavior across the model system.

crates/dojo/core-cairo-test/src/tests/model/model.cairo (6)

65-71: Ohayo! New test enum with Default implementation.

The enum properly implements the Default trait with Z as the default variant, satisfying the requirements for custom enums mentioned in the PR objectives.


73-81: LegacyModel with DojoLegacyStorage attribute for backwards compatibility.

This model uses the DojoLegacyStorage attribute to maintain compatibility with existing world storage as described in the PR objectives.


83-91: New DojoStoreModel using the enhanced storage system.

This model will use the new DojoStore trait for serialization/deserialization, allowing comparison with the legacy approach.


101-102: Added new models to the namespace, nice work sensei!

Both the legacy and new storage models are properly registered in the namespace definition, allowing them to be recognized as resources.


375-460: Comprehensive test for legacy model behavior.

This test verifies that LegacyModel:

  1. Produces the expected layout with variant IDs starting from 0
  2. Correctly serializes/deserializes values using the legacy approach
  3. Maintains backward compatibility as intended

The detailed assertions help validate that the legacy storage behavior is preserved.


462-543: Thorough test for the new DojoStore model functionality.

This test confirms that DojoStoreModel:

  1. Produces layouts with variant IDs starting from 1 (not 0)
  2. Correctly handles Option types and custom enums
  3. Properly serializes/deserializes with the new approach

The assertions verify that the serialized values include the incremented variant IDs as expected.

crates/dojo/lang/src/attribute_macros/model.rs (6)

21-21: Added DojoLegacyStorage derive attribute constant.

This constant defines the attribute name used to identify models that should use legacy storage behavior.


67-69: Detect legacy storage flag from derive attributes.

The code now checks for the presence of DojoLegacyStorage among the derive attributes to determine the storage approach.


89-119: Improved member parsing with key ordering validation, nice work sensei!

This refactored approach:

  1. Processes struct members sequentially
  2. Verifies that key members are defined before non-key members
  3. Provides clear error messages when the ordering is incorrect
  4. Tracks the parsing state to enforce the structure

The validation ensures models follow best practices for key/value organization.


124-142: Serialization/deserialization now considers storage type.

The serialization and deserialization code generation now passes the use_legacy_storage flag to ensure the appropriate approach is used based on the model's attributes.


219-240: Storage-aware path selection and layout generation.

The code now:

  1. Uses Serde for legacy models and DojoStore for new models
  2. Applies build_legacy_layout for legacy models to ensure compatibility
  3. Uses the introspected layout directly for new models

This conditional approach enables the seamless transition described in the PR objectives.


263-265: Added use_legacy_storage flag to the template context.

This ensures the generated code can access the storage type information during model implementation.

crates/dojo/lang/src/derive_macros/introspect/mod.rs (12)

10-10: Ohayo! Good addition of Itertools crate.

The itertools crate is a nice addition here, sensei. Using Itertools::join is much cleaner than manually building strings with loops.


12-14: New modules organization looks perfect!

Nice structure with the additional debug helper and new dojo_store module, sensei. This cleanly separates the DojoStore generation logic from the rest of the introspection code.


29-38: Much improved generic type handling!

Ohayo! This is a significant improvement to the generic type handling, sensei. Splitting the types from implementations makes the code more focused and easier to maintain. The separation of concerns between inspect_gen_impls and dojo_store_gen_impls is excellent!


55-61: Excellent DojoStore implementation for structs.

The dojo_store::build_struct_dojo_store call properly generates the DojoStore trait implementation for structs, which aligns perfectly with the PR objective of introducing the DojoStore trait to handle special storage cases.


63-63: Smart debugging approach.

Ohayo sensei! Using environment variables for conditional debug output is an excellent practice. This will help tremendously when troubleshooting implementation issues without cluttering normal output.


65-73: Updated introspect generation properly includes DojoStore.

The updated call to generate_introspect now includes the DojoStore implementation, which is essential for the new functionality to work correctly.


85-97: Important enhancement for enum handling.

Ohayo! The special handling for enums is particularly important, sensei. The additional requirements (Default trait) match what was described in the PR objectives for handling enum variants where the first variant ID is 0.


125-131: DojoStore implementation for enums looks good.

Similar to the struct implementation, this properly generates the DojoStore trait for enums with the correct parameters.


133-133: Helpful debug output for enums.

Same helpful debugging approach as for structs, which will make troubleshooting easier.


155-155: Clean integration of DojoStore into introspection output.

The addition of the DojoStore parameter and its integration into the output template is clean and non-disruptive to the existing introspection functionality.

Also applies to: 176-176, 185-185


192-215: Good refactoring of the generic types function.

The build_generic_types function has been streamlined to focus solely on extracting and returning the types, which makes it more reusable and clearer in its purpose.


217-237: Excellent new implementation builder function.

Ohayo, sensei! This new build_generic_impls function is well-designed. It handles both default implementations and additional implementations, which provides the flexibility needed for different trait requirements. The empty check at line 232 is an important edge case handling.

crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (6)

29-29: Good switch to using model layout variable.

Ohayo! The change to use $model_layout$ instead of directly calling the introspect function allows for more flexibility in how layouts are determined, supporting the new legacy storage functionality.


32-36: Key addition for legacy storage support.

This new function use_legacy_storage is central to the PR's objective, sensei. It allows models to opt into using legacy storage behavior, which is exactly what was described in the PR summary.


59-61: Good deserialization implementation for models.

Adding the deserialize method to the ModelParser implementation properly delegates to the appropriate deserializer path, providing a clean integration point for the DojoStore functionality.


75-77: Consistent deserialization for model values.

Similar to the model parser, this provides deserialization for model values, ensuring consistency in the API.


85-87: Helpful comment about DojoStore implementation.

Ohayo sensei! This comment clarifies how the DojoStore implementation for the model itself is handled, which is useful for understanding the overall implementation approach.


88-96: Well-structured DojoStore implementation for model values.

The implementation of ValueDojoStore provides both serialization and deserialization methods that align with the DojoStore trait requirements. This completes the storage handling for model values.

crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (11)

1-10: Good imports setup for the test file.

Ohayo sensei! The imports are well-organized, bringing in all the necessary layout functions and constants needed for the tests.


11-52: Comprehensive fixed layout tests.

This test function thoroughly verifies fixed layout functionality, covering initialization, reading/writing, offset handling, and cleanup. The assertions with descriptive messages are particularly helpful.


54-102: Well-structured tuple layout tests.

Similar to the fixed layout tests, these tuple layout tests are comprehensive and well-structured, ensuring the functionality works as expected.


104-147: Thorough byte array layout tests.

Ohayo! The byte array tests properly verify string-like data structures, sensei. Good job handling the specific encoding format with explicit values.


149-163: Good negative test for invalid array length.

This test properly verifies that the system detects and handles invalid array lengths with appropriate error messages.


165-196: Comprehensive error condition tests for byte arrays.

These tests verify different error conditions for byte arrays, ensuring robust error handling.


198-240: Well-designed array layout tests.

The array layout tests cover all the necessary functionality including default values, reading/writing, and cleanup.


242-285: Good error handling tests for arrays.

These tests verify that the array operations properly handle error conditions such as invalid lengths.


287-333: Thorough struct layout tests.

Ohayo, sensei! These struct layout tests properly verify the functionality for structures with named fields.


335-381: Comprehensive enum layout tests.

The enum layout tests properly verify variant handling, which is particularly important given the PR's focus on properly handling enum variants.


383-457: Important error handling tests for enums.

These tests verify that the enum functions properly handle error conditions such as invalid variant values, which is critical for the functionality described in the PR.

crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (8)

1-3: Good imports setup for legacy layout tests.

Ohayo! The imports bring in the necessary components for testing the legacy layout functionality.


4-19: Important test for Option legacy layout.

This test is particularly important given the PR objectives of handling Option<T> storage cases. It verifies that the legacy layout is correctly generated with the variant indices adjusted as expected.


21-44: Critical enum layout regression test.

Great test for simple enums, sensei! This verifies that the variant indices are properly adjusted in the legacy layout, which is central to the PR's objective.


46-71: Good test for tuple with nested Option.

This test verifies that the legacy layout handling works correctly for tuples containing Option<T>, ensuring nested structures are properly converted.


73-95: Well-designed array test with Option elements.

Ohayo! This test verifies arrays containing Option elements, ensuring the legacy layout is correctly generated for container types.


97-129: Comprehensive struct layout test.

This test covers structs with Option fields, verifying that legacy layout generation works correctly for complex data structures.


131-194: Excellent test for nested enum structures.

This complex test verifies that deeply nested structures with enums, arrays, and tuples are correctly handled by the legacy layout generation.


196-260: Thorough test for nested struct structures.

Similar to the nested enum test, this verifies that complex nested structures with structs are correctly handled.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1)

3-9: Ohayo sensei, excellent use of default variants in your enum.
These definitions look consistent with the DojoStore approach, and the default variant appears well-suited for seamless initialization.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
Cargo.toml (2)

224-230: Dependency Source Reassignment: Validate the New Repository

Ohayo, sensei! You’ve successfully switched the scarb, scarb-metadata, and scarb-ui dependencies from the dojoengine repo to the remybar/scarb repository with commit 6279a8e. Please verify that the new repo and commit provide all the required features and that they’re compatible with the rest of the codebase.


232-234: Local Path Dependencies: Enhance Developer Guidance

Ohayo, sensei! The commented-out local path dependencies are a nice touch for local development troubleshooting. Consider adding a brief note or comment to inform contributors when they might switch to these local paths for testing or debugging.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 3e4fe96 and e6f4ef6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • Cargo.toml (1 hunks)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (1)

5-5: Ohayo! New import added for ContractAddressDefault

This import seems to support the DojoStore trait introduced in this PR, though it's not directly used within this file. Consider adding a comment to clarify its purpose or removing it if truly unused.

crates/dojo/lang/src/attribute_macros/model.rs (1)

67-68: Consider adding a documentation comment for use_legacy_storage.

Adding a comment that explains the purpose and effect of this flag would improve code readability and maintenance.

+// Flag indicating whether this model should use legacy storage (Serde)
+// instead of the new DojoStore trait
 let use_legacy_storage =
     derive_attr_names.contains(&DOJO_LEGACY_STORAGE_DERIVE.to_string());
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 69d6316 and e334d24.

⛔ Files ignored due to path filters (1)
  • crates/dojo/lang/Scarb.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • crates/benches/contracts/src/models/character.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (6 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (6 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (2 hunks)
  • crates/dojo/core/src/event/component.cairo (1 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/benches/contracts/src/models/character.cairo
  • crates/dojo/core/src/storage/dojo_store.cairo
🧰 Additional context used
🪛 GitHub Actions: ci
crates/dojo/lang/src/attribute_macros/model.rs

[error] 20-20: Rust formatting check failed. Please run 'rustfmt' to format the code.


[error] 141-141: Rust formatting check failed. Please run 'rustfmt' to format the code.

crates/dojo/core/src/event/component.cairo

[error] 27-27: Formatting check failed. Run 'scarb fmt' to fix code style issues in this file.

🔇 Additional comments (29)
crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5)

39-45: Nicely implemented Default for EnumOne, sensei!

Adding the Default trait and explicitly marking the Two variant as #[default] aligns perfectly with the PR objectives. This ensures proper handling of enum variants when using the new DojoStore trait, addressing the issue where variant id 0 can't be distinguished from uninitialized state.


56-63: Excellent demonstration of DojoLegacyStorage attribute, sensei!

This new struct provides a perfect test case for comparing the behavior between legacy storage and the new DojoStore trait. It contains both a custom enum (EnumOne) and an Option, which are exactly the types this PR aims to address.


110-112: Implementation of use_legacy_storage looks good!

The default return value of false ensures models will use the new DojoStore trait by default, which aligns with the PR objectives. This provides a clean way for models to opt-out of the new storage behavior when needed.


145-146: Default trait added to Sword struct - nice!

Adding the Default trait to Sword ensures it can be properly handled by the new DojoStore trait. This is essential when Sword is used within other structures or enums.


195-198: Perfect implementation of Default for Weapon enum!

Adding Default trait and marking DualWield as #[default] ensures proper serialization/deserialization behavior with the new DojoStore trait. This pattern is consistent with the EnumOne implementation earlier in the file.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (15)

5-5: Adding Default trait to Base - nice work sensei!

This change supports the PR's objective of requiring each custom struct to implement the Default trait for proper integration with DojoStore.


40-40: Vec3 now has Default implementation - excellent!

Adding the Default trait to this packed struct ensures it works correctly with the new DojoStore serialization system.


58-61: EnumNoData now properly implements Default with marked default variant!

This change addresses the core issue in the PR by ensuring the enum has a Default implementation and explicitly marking the first variant as default, which solves the problem of distinguishing variant 0 from uninitialized.


66-69: Default implementation for EnumWithSameData looks good!

Adding Default and marking the first variant explicitly helps solve the storage issue described in the PR objectives.


74-77: EnumWithSameTupleData Default implementation is correct!

This follows the same pattern as the other enum modifications, properly implementing the Default trait and marking the first variant.


82-85: EnumWithVariousData Default implementation looks perfect!

The Default trait and default variant are correctly implemented here, consistent with the other enum changes.


91-94: EnumPacked now has Default - aligned with PR objectives!

Ensuring packed enums also implement Default is important for consistent behavior with the new DojoStore trait.


98-101: EnumInnerPacked Default implementation looks great sensei!

This maintains consistency with the other enum implementations, ensuring proper behavior with DojoStore.


105-108: EnumInnerNotPacked Default implementation is correct!

This completes the set of enum modifications to ensure they all work correctly with the new storage approach.


118-120: Renamed Generic to GenericStruct for clarity!

This renaming improves code readability by differentiating between generic structs and enums.


122-126: Added GenericEnum with Default - excellent addition sensei!

This new generic enum with Default implementation ensures that generics also work correctly with the DojoStore trait, addressing a potential gap in the implementation.


151-152: Critical change to enum variant indexing!

This is a key implementation detail for the PR objectives - changing variant indexing to start from 1 instead of 0 allows distinguishing between uninitialized variants and the first variant.


155-156: Updated match cases to use the new variant indexing!

This change ensures the enum handling logic matches the new variant indexing scheme where variants start from 1.


170-190: Updated test for renamed GenericStruct - looks great!

The test has been properly updated to reflect the struct name change and ensures that generic structs continue to work as expected.


192-212: New test for GenericEnum provides good coverage!

This test ensures that the new GenericEnum works correctly with the introspection system, maintaining proper test coverage for the codebase.

crates/dojo/lang/src/attribute_macros/model.rs (9)

13-13: Ohayo! New Terminal import added for improved AST handling.

The addition of Terminal from cairo_lang_syntax::node enhances your AST manipulation capabilities, which is essential for the new DojoStore trait implementation.


20-22: Nice work adding the DOJO_LEGACY_STORAGE_DERIVE constant, sensei!

This constant enables the backward compatibility mechanism discussed in the PR objectives, allowing models to retain their current storage behavior when needed.

🧰 Tools
🪛 GitHub Actions: ci

[error] 20-20: Rust formatting check failed. Please run 'rustfmt' to format the code.


23-23: Well-organized utility imports for storage handling.

The added utility functions properly encapsulate the serialization logic needed for the new DojoStore trait implementation.


61-69: Elegant implementation of legacy storage detection!

This code correctly identifies whether a model should use legacy storage based on the presence of the DojoLegacyStorage derive attribute.


79-80: Excellent addition of deserialized_values collection.

This new collection enables proper handling of deserialization based on the storage strategy, which is key to resolving the issue with variant id 0.


86-117: Great enhancement to member parsing logic, sensei!

The refactoring to use iterators and the addition of key-first ordering enforcement improves code organization and prevents potential errors. The diagnostics provide clear error messages when the ordering rule is violated.


122-128: Nice integration of legacy storage flag in serialization.

The use_legacy_storage parameter is now properly passed to the serialization function, ensuring consistency with the chosen storage approach.


214-232: Clean implementation of conditional model layout generation!

This code elegantly handles the different paths for model layout based on whether legacy storage is used, supporting the PR's objective of maintaining compatibility with existing world storage.


240-257: Comprehensive template interpolation with new storage parameters.

All necessary variables for the code template are now included, ensuring that the generated code correctly implements the chosen storage strategy.

@remybar remybar force-pushed the fix-enum_options_storage branch from e334d24 to 64fae38 Compare March 27, 2025 10:38
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/dojo/lang/src/attribute_macros/model.rs (1)

13-13: Ohayo sensei! Unused import detected.

The Terminal import from cairo_lang_syntax::node appears to be unused in this file. Unless it's required by one of the functions called in this file, consider removing it to keep imports clean.

-use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode};
+use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between e334d24 and 64fae38.

⛔ Files ignored due to path filters (3)
  • crates/dojo/lang/Scarb.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (7)
  • crates/benches/contracts/src/models/character.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (6 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (6 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (2 hunks)
  • crates/dojo/core/src/event/component.cairo (1 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/dojo/core/src/event/component.cairo
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo
  • crates/benches/contracts/src/models/character.cairo
  • crates/dojo/core/src/storage/dojo_store.cairo
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: fmt
🔇 Additional comments (12)
crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (6)

5-5: Ohayo! Good addition for supporting default values for ContractAddress types.

Adding the import for ContractAddressDefault enables default value support for ContractAddress which aligns with the DojoStore trait's functionality to handle default values during deserialization.


39-45: Looking good, sensei! Nice default trait implementation for enums.

The addition of the Default trait derivation for EnumOne and explicitly marking the Two variant as #[default] is excellent. This supports the PR objective of providing proper default values for custom enums, which is necessary for the DojoStore serialization/deserialization mechanism to handle the variant index correctly.


56-63: Excellent strategy for backward compatibility, sensei!

The introduction of WithOptionAndEnumsLegacy with the DojoLegacyStorage attribute demonstrates how legacy storage behavior can be maintained while moving to the new serialization mechanism. This allows for testing both approaches and ensures smooth transition for existing code.


110-112: Perfectly implementing the new storage interface, sugoi!

The use_legacy_storage method returns false, indicating this model will use the new DojoStore trait rather than legacy serialization. This aligns with the PR objective of providing a mechanism to distinguish between legacy and new storage behaviors.


145-149: Default trait for structured types - nice work, sensei!

Adding the Default trait derivation to the Sword struct enables proper initialization of default values, which is necessary for the DojoStore serialization/deserialization mechanism.


195-200: Great default implementation for the Weapon enum!

Similar to the changes for EnumOne, adding the Default trait to Weapon and explicitly marking DualWield as the default variant supports the proper serialization/deserialization with the new DojoStore trait. This ensures there's a clear default when deserializing an uninitialized variant.

crates/dojo/lang/src/attribute_macros/model.rs (6)

19-21: Great addition of legacy storage support, sensei!

The inclusion of DOJO_LEGACY_STORAGE_DERIVE constant alongside other derive attributes is a good approach for implementing the legacy storage feature.


67-68: Clean implementation of the legacy storage detection, sensei!

The code elegantly determines if legacy storage should be used by checking for the presence of the DOJO_LEGACY_STORAGE_DERIVE attribute. This approach ensures backward compatibility while allowing newer code to use the improved storage mechanism.


86-117: Excellent implementation of member parsing with key ordering enforcement!

Ohayo, sensei! Your approach of using an iterator over member AST nodes improves the code structure while enforcing the important rule that key members must be defined before non-key members. The error diagnostics for incorrect ordering are clear and helpful.


122-141: Well-implemented serialization with legacy storage support!

The code correctly passes the use_legacy_storage flag to serialization and deserialization functions, ensuring consistent behavior regardless of the storage strategy. This implementation aligns perfectly with the PR's objective of addressing special storage cases.


216-233: Clean implementation of conditional model layout, sensei!

The conditional handling of model layout based on the legacy storage flag is well-structured. Using the build_legacy_layout function for legacy storage while directly using the introspection layout for the new approach is a clean solution.


254-257: Good addition to the interpolation parameters!

Adding the use_legacy_storage flag to the interpolation parameters makes it available in the generated code, ensuring consistency throughout the model implementation.

@remybar remybar force-pushed the fix-enum_options_storage branch from 64fae38 to ad3d846 Compare April 30, 2025 11:08
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
crates/dojo/lang/src/derive_macros/introspect/mod.rs (1)

158-160: 🛠️ Refactor suggestion

⚠️ Potential issue

Avoid empty generic angle-brackets – causes invalid Cairo syntax

If the inspected struct/enum is not generic, the placeholders $generics$ and $generics_types$ become empty strings, yielding code such as
impl FooIntrospect<> of … and Foo<>, which is illegal Cairo and aborts compilation.

Consider guarding the brackets:

-impl $name$Introspect<$generics$> of dojo::meta::introspect::Introspect<$name$<$generics_types$>> {
+impl $name$Introspect$(
+    if $generics$ == "" { "" } else { format!("<{generics}>") }
+) of dojo::meta::introspect::Introspect<
+    $name$$(
+        if $generics_types$ == "" { "" } else { format!("<{generics_types}>") }
+    >
+> {

Or, more simply, build impl_generics / ty_generics strings beforehand and interpolate only when non-empty.
Failing to fix this will break every non-generic model.

♻️ Duplicate comments (1)
crates/dojo/lang/src/utils.rs (1)

158-163: Graceful handling for unsupported tuple elements still missing

The unimplemented! panic for unsupported expressions inside tuples remains unchanged from the previous review.
This will crash the macro expansion phase and halt compilation rather than emitting a diagnostic.

Please downgrade to a diagnostic push (severity = Error) and generate placeholder code so other models can still compile.

🧹 Nitpick comments (4)
crates/dojo/lang/src/utils.rs (1)

14-20: Micro-optimisation: Reuse the serialization path string

get_serialization_path allocates a new String every call.
As this helper is invoked for every member inside serialize_* / deserialize_*, the extra heap traffic adds up during macro expansion.

A static const SERDE_PATH: &str = "core::serde::Serde"; and
const DOJO_PATH: &str = "dojo::storage::DojoStore"; (or returning &'static str) eliminates the allocations with zero ergonomic cost.

crates/dojo/core/src/storage/dojo_store.cairo (3)

1-6: Ohayo! ContractAddressDefault implementation might be temporary.

The comment on line 1 suggests that this implementation might not be necessary in Cairo 2.11+. Consider adding a more specific TODO or a tracking issue reference to ensure this is revisited when Cairo 2.11 is released.


52-52: Variant ID for None is 2 instead of the conventional 0.

In most Rust/Cairo implementations, None typically uses variant ID 0. Here it's using 2, which is intentional based on the PR description to avoid confusion with uninitialized values. This choice should be documented more clearly for future maintainers.

-            Option::None => { serialized.append(2); },
+            // Using variant ID 2 for None (instead of conventional 0)
+            // to distinguish from uninitialized storage values
+            Option::None => { serialized.append(2); },

8-118: Ohayo, sensei! Consider adding documentation for custom enum handling.

While the implementation for Option<T> is provided, the PR description also mentions handling custom enums. Consider adding documentation about how custom enums are expected to be handled (possibly through derive macros or custom implementations).

Also, it would be helpful to add examples in the documentation showing how to use DojoStore for custom types and how it integrates with the DojoLegacyStorage attribute.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 64fae38 and ad3d846.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (44)
  • Cargo.toml (1 hunks)
  • crates/benches/contracts/src/models/character.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/lib.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (4 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1 hunks)
  • crates/dojo/core/src/event/component.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/meta/introspect.cairo (1 hunks)
  • crates/dojo/core/src/meta/layout.cairo (1 hunks)
  • crates/dojo/core/src/model/component.cairo (1 hunks)
  • crates/dojo/core/src/model/definition.cairo (1 hunks)
  • crates/dojo/core/src/model/interface.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (4 hunks)
  • crates/dojo/core/src/model/model_value.cairo (2 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/layout.cairo (8 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (1 hunks)
  • crates/dojo/lang/Cargo.toml (1 hunks)
  • crates/dojo/lang/src/attribute_macros/element.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/mod.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3 hunks)
  • crates/dojo/lang/src/cairo_plugin.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs (3 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/mod.rs (1 hunks)
  • crates/dojo/lang/src/lib.rs (2 hunks)
  • crates/dojo/lang/src/utils.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/model.rs (2 hunks)
  • crates/dojo/world/src/contracts/model.rs (2 hunks)
💤 Files with no reviewable changes (2)
  • crates/dojo/lang/src/attribute_macros/mod.rs
  • crates/dojo/lang/src/attribute_macros/element.rs
✅ Files skipped from review due to trivial changes (2)
  • crates/dojo/core-cairo-test/src/lib.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo
🚧 Files skipped from review as they are similar to previous changes (32)
  • crates/dojo/lang/Cargo.toml
  • crates/dojo/core/src/model/component.cairo
  • crates/dojo/core/src/model/interface.cairo
  • crates/dojo/core/src/model/definition.cairo
  • crates/dojo/core/src/lib.cairo
  • crates/dojo/core/src/model/model_value.cairo
  • crates/dojo/core/src/meta/introspect.cairo
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/dojo/core/src/meta/layout.cairo
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo
  • crates/dojo/lang/src/lib.rs
  • crates/dojo/lang/src/derive_macros/mod.rs
  • crates/dojo/core/src/world/world_contract.cairo
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs
  • crates/dojo/core/src/event/component.cairo
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo
  • crates/dojo/lang/src/attribute_macros/event.rs
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo
  • crates/dojo/core/src/storage/layout.cairo
  • crates/dojo/core/src/model/model.cairo
  • crates/dojo/world/src/contracts/model.rs
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs
  • crates/dojo/lang/src/cairo_plugin.rs
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo
  • crates/dojo/world/src/contracts/abigen/model.rs
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo
  • Cargo.toml
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo
🧰 Additional context used
🧬 Code Graph Analysis (1)
crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (2)
crates/dojo/lang/src/derive_macros/introspect/utils.rs (1)
  • clean_ty (66-68)
crates/dojo/lang/src/utils.rs (5)
  • deserialize_primitive_member_ty (249-256)
  • deserialize_tuple_member_ty (258-283)
  • serialize_primitive_member_ty (194-205)
  • serialize_tuple_member_ty (207-231)
  • expr (143-165)
🔇 Additional comments (29)
crates/dojo/core-cairo-test/src/tests/model/model.cairo (7)

75-80: Ohayo sensei! Good use of Default trait with properly marked variant.

The EnumWithCommentOnLastVariant enum correctly derives Default with the #[default] attribute on the X variant. This is essential for the new DojoStore trait to handle enums properly. The comment on the last variant is a nice edge case test for the parsing logic.


82-88: Well-structured test enum with nested option type!

This enum includes an Option<u32> variant which is perfect for testing nested enum serialization with the new DojoStore trait. The #[default] attribute on the Z variant ensures proper default implementation.


90-99: Great test model with legacy storage annotation!

The LegacyModel appropriately derives DojoLegacyStorage and includes a diverse set of field types (tuple, option, enum), making it an excellent test case for verifying legacy serialization behavior.


100-108: Perfect companion model for storage comparison testing!

The DojoStoreModel mirrors LegacyModel but without the legacy storage annotation, creating an ideal pair for direct comparison between the two storage implementations. This will help verify the behavior differences clearly.


118-119: Resources correctly registered for testing.

The namespace definition is properly updated to include both new test models as resources, enabling them to be used in the world storage tests.


392-477: Comprehensive test for legacy model storage!

This test thoroughly validates the legacy storage implementation, checking:

  1. The layout differences between ModelDefinition::layout() and Introspect::layout()
  2. Serialization of keys and values with legacy variant indices (starting at 0)
  3. Successful deserialization back to the original model

The validation of layout differences is particularly important to ensure compatibility with existing storage.


479-559: Excellent test for new DojoStore model!

This parallel test confirms the new storage implementation works correctly, verifying:

  1. The layout consistency between ModelDefinition::layout() and Introspect::layout()
  2. Serialization with new variant indices (starting at 1 instead of 0)
  3. Successful round-trip deserialization

The test clearly demonstrates the key difference between storage formats: variant indices start at 1 in the new format, solving the uninitialized variant issue described in the PR objectives.

crates/dojo/lang/src/attribute_macros/model.rs (5)

61-69: Ohayo! Smart approach to early derive detection and flag setting.

The code now extracts derive attributes early and sets a use_legacy_storage flag based on the presence of DojoLegacyStorage. This is a clean approach that allows the subsequent code to make storage-related decisions during code generation.


88-119: Better member ordering with clear error messages!

This change enforces that key members must be defined before non-key members, providing clear error diagnostics when this constraint is violated. The approach using the parsing_keys flag is elegant and doesn't halt parsing after the first error, allowing for multiple diagnostics to be collected.


124-143: Serialization code now handles both storage modes correctly.

The code now passes the use_legacy_storage flag to both serialization and deserialization helper functions, ensuring consistent handling across the model. The addition of deserialized value nodes is crucial for the round-trip serialization process.


219-240: Excellent conditional path selection for layout and deserialization!

This is a key implementation that:

  1. Selects between core::serde::Serde and dojo::storage::DojoStore based on the storage mode
  2. For legacy storage, wraps the introspection layout with build_legacy_layout
  3. For new storage, uses the introspection layout directly

This allows seamless compatibility with both storage approaches while moving forward with the improved variant handling.


263-265: Storage flag properly passed to template.

The use_legacy_storage flag is correctly added to the interpolation parameters, ensuring the model patch template can generate the appropriate code based on the storage mode.

crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (2)

11-81: Ohayo sensei! Well-implemented struct serialization code generator!

The build_struct_dojo_store function generates a complete implementation of the DojoStore trait for structs:

  1. Properly handles both primitive and tuple members with specialized code
  2. Generates serialization code using the appropriate helpers
  3. Constructs deserialization code that builds the struct from its members
  4. Supports generic types correctly

The implementation is clean and modular, leveraging the utility functions for serialization and deserialization.


83-185: Excellent enum serialization with variant offset handling!

The build_enum_dojo_store function addresses the core problem described in the PR:

  1. Increments variant index by 1 (line 96) to avoid using 0 as a variant index
  2. Adds special handling for variant 0 in deserialization to return Default::default() (line 178)
  3. Handles empty, tuple, and primitive variants with appropriate code generation
  4. Returns None for unknown variant indices for proper error handling

This implementation solves the issue with uninitialized enum variants by ensuring all serialized variants use indices starting at 1, with 0 reserved for uninitialized/default values.

crates/benches/contracts/src/models/character.cairo (4)

1-14: Ohayo sensei! Good model structure with proper key field.

The Character model is well-structured with:

  1. Appropriate use of #[dojo::model] attribute
  2. Correct key field using ContractAddress
  3. Nice mix of primitive and complex nested types

This will make a good benchmark case for testing the storage system's handling of complex models.


16-37: Well-designed supporting structs for nested model testing.

The Abilities and Stats structs provide good test cases for nested serialization within models. They have appropriate derives (Introspect, Copy, Drop) and contain a variety of field types to exercise the serialization system.


39-50: Excellent default implementation for enum and nested struct.

Both Weapon enum and Sword struct correctly derive Default, which is essential for the new DojoStore trait to handle uninitialized variants properly. The #[default] attribute on the DualWield variant ensures the correct default implementation.


52-67: Good additional model definitions for benchmark diversity.

The Case and Alias models provide additional test cases with different structures:

  1. Both correctly use #[dojo::model] with appropriate key fields
  2. Case includes a nested Sword struct, testing complex field serialization
  3. Both use simple primitive fields alongside the complex ones

These will help ensure the benchmark covers diverse model patterns.

crates/dojo/lang/src/derive_macros/introspect/mod.rs (1)

218-237: Potential double comma / spacing in generic impl list

build_generic_impls produces strings like "T, +Trait1<T>, +Trait2<T>" for each T and then joins them with ", ".
When additional_impls are appended the result can become
"T, +Trait<T>, Extra, Extra2" → notice the missing T before the extra items.

Verify that resulting where-like lists match your intended grammar or build gen_impls from a single iterator of homogeneous entries instead of mixing two phases.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (2)

55-128: Ohayo sensei – impressive coverage!

The primitive round-trip tests are exhaustive and clearly validate the DojoStore contract for each scalar type. No issues spotted here.


389-442: Complex nested structure test is 💯

Great job stress-testing nested generics, options, arrays and enums in a single assertion. This will guard against a whole class of regressions.

crates/dojo/core/src/storage/dojo_store.cairo (8)

8-12: Looks great, sensei! Core trait design is clean and focused.

The DojoStore trait provides a clear, minimal interface for serialization and deserialization of types for world storage. The Option return type for deserialize properly handles potential deserialization failures.


14-24: Nice implementation of the default DojoStore using Serde!

This default implementation elegantly reuses existing Serde implementations, which reduces duplication and maintains consistency with existing serialization logic.


26-42: All essential primitive and StarkNet types covered, good work sensei!

The comprehensive list of implementations ensures that all commonly used types have proper DojoStore support out of the box.


44-68: Key implementation addressing Option variant ID issue.

This custom implementation for Option<T> solves the core problem mentioned in the PR description by using variant IDs 1 and 2 instead of 0 and 1, allowing distinction between uninitialized (0) and actual variants.

The deserialize method's handling of variant ID 0 returning Default::default() matches the PR objectives perfectly.


59-59: Default handling for uninitialized variant IDs.

This is the key part of the solution - when encountering variant ID 0 (uninitialized storage), it returns the default value instead of failing, which aligns perfectly with the PR objectives. Great implementation, sensei!


70-90: Well-structured array helper functions.

These recursive helper functions provide clean, reusable logic for array serialization and deserialization. The recursion is properly terminated and handles error cases well.


92-105: Proper DojoStore implementation for arrays.

This implementation correctly serializes the array length first, followed by each element, and handles deserialization failures appropriately with the ? operator.


107-118: DojoStore implementation for Span matches Array implementation.

The Span implementation follows the same pattern as the Array implementation, which is good for consistency. The double-wrapping with Option on line 116 looks correct for handling potential deserialization failures.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Cargo.toml (1)

161-163: Clean up commented Git dependencies

Ohayo sensei, these commented-out Git entries for scarb, scarb-metadata, and scarb-ui clutter the manifest and may confuse contributors. If they’re no longer needed, please remove them or add a note explaining their purpose.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between fbb3e1c and 934b3c1.

⛔ Files ignored due to path filters (1)
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (5)
  • Cargo.toml (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/dojo/lang/src/attribute_macros/event.rs
  • crates/dojo/lang/src/attribute_macros/model.rs
🔇 Additional comments (24)
crates/dojo/lang/src/derive_macros/introspect/mod.rs (12)

10-12: Good additions for debugging and functionality!

Ohayo sensei! The use of Itertools will be helpful for joining collections, and the debug utility will aid in diagnostics during development.


14-14: New DojoStore module looks good!

This module will encapsulate the DojoStore trait implementation logic, keeping the codebase well-organized.


27-35: Clean separation of generic type handling!

The extraction of generic types and separate implementation constraints for introspection and DojoStore is well-structured. This will make the code more maintainable and easier to extend in the future.


52-61: Great modular approach to building DojoStore for structs!

The separate function call to build DojoStore implementation keeps the code clean, and the debug output will be valuable during development.


62-70: Nice integration of DojoStore into introspect generation!

The modified function signature ensures that both introspection and storage traits are generated cohesively.


82-94: Well-structured generic handling for enums!

Ohayo sensei! I particularly like how you're handling the enum name with generics and the additional trait constraints for Serde and Default. This ensures that enums will properly implement the necessary traits for the DojoStore functionality.


122-131: Well-implemented DojoStore for enums!

The enum DojoStore implementation follows the same clean pattern as for structs. The debug logging will help with troubleshooting during development.


132-140: Good integration of DojoStore for enums!

The updated generate_introspect function call properly includes the DojoStore implementation in the generated code.


173-173: Clean template integration!

Adding the DojoStore to the template with a simple placeholder ensures clean code generation.


182-182: Good interpolation of the DojoStore code!

The RewriteNode map correctly includes the DojoStore implementation.


189-212: Clean refactoring of generic type handling!

The extracted build_generic_types function is well-implemented and focused on a single responsibility.


214-234: Excellent helper function for building generic impl constraints!

The build_generic_impls function is well-designed with a clean separation of concerns. It handles both base implementations and additional constraints elegantly.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (12)

5-8: Addition of Default trait is appropriate!

Ohayo sensei! Adding the Default trait to Base struct aligns with the new DojoStore requirements, allowing proper handling of uninitialized values.


58-64: Good addition of Default for EnumNoData and default variant!

The #[default] attribute on the first variant ensures proper default value handling for the enum.


66-72: Similar pattern applied consistently for EnumWithSameData!

Maintaining consistency with Default trait and default variant annotation.


74-80: Consistent pattern for EnumWithSameTupleData!

The Default trait and default variant continue to be applied consistently.


82-88: Good handling for EnumWithVariousData!

Default trait and default variant are properly applied.


91-96: EnumPacked with Default looks good!

Consistent pattern application continues.


117-120: Good rename from Generic to GenericStruct!

Ohayo sensei! The rename to GenericStruct makes the purpose clearer and aligns with the new GenericEnum type.


122-126: Nice addition of GenericEnum!

Adding a generic enum with proper Default implementation complements the GenericStruct and provides better test coverage.


151-153: Critical change to enum variant indexing!

The shift from zero-based to one-based indexing for enum variants is a key change that addresses the core issue in the PR. This reserves zero for uninitialized variants, allowing proper distinction in storage.


155-156: Updated variant usage in match pattern!

The updated field creation correctly uses the new variant index.


170-190: Good update to the generic struct test!

The renamed test accurately verifies the introspection of GenericStruct with comprehensive assertions for size, layout, and type.


192-212: Excellent new test for GenericEnum!

Ohayo sensei! This new test comprehensively verifies the introspection functionality for GenericEnum, ensuring that the size, layout, and type introspection work correctly.

@remybar remybar force-pushed the fix-enum_options_storage branch 2 times, most recently from 5786d1f to ea46867 Compare May 1, 2025 09:42
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
crates/dojo/lang/src/derive_macros/introspect/mod.rs (1)

154-175: ⚠️ Potential issue

Ohayo sensei – generated impls break for non-generic types

generate_introspect hard-codes angle-brackets:

impl $name$Introspect<$generics$> of ... <$name$<$generics_types$>>

When the inspected type has no generics, $generics$ and $generics_types$ are empty strings, leaving dangling <> which is illegal Cairo syntax and fails compilation for every non-generic struct/enum.

A minimal fix is to include the brackets inside the placeholders:

-"impl $name$Introspect<$generics$> of dojo::meta::introspect::Introspect<$name$<$generics_types$>>"
+"impl $name$Introspect$generics$ of dojo::meta::introspect::Introspect<$name$$generics_types$>"

And populate

let generics = if generic_impls.is_empty() { "".into() } else { format!("<{generic_impls}>") };
let generics_types = if generic_types.is_empty() { "".into() } else { format!("<{}>", generic_types.join(", ")) };

before interpolation.

♻️ Duplicate comments (1)
crates/dojo/lang/src/utils.rs (1)

158-163: Ohayo sensei – unimplemented! will blow up at runtime

We already discussed this in an earlier round: panicking on unsupported tuple expressions will abort macro expansion and confuse users. Please replace the unimplemented! with a diagnostic + graceful fallback exactly as suggested before.

🧹 Nitpick comments (8)
crates/dojo/lang/src/utils.rs (3)

13-20: Ohayo sensei – avoid heap allocations in get_serialization_path()

The function returns a newly-allocated String every time, yet it only ever returns one of two static literals. Returning &'static str (or even making the function const) removes the allocation and enables further compile-time optimisations.

-#[inline(always)]
-pub fn get_serialization_path(use_serde: bool) -> String {
-    if use_serde {
-        "core::serde::Serde".to_string()
-    } else {
-        "dojo::storage::DojoStore".to_string()
-    }
-}
+#[inline(always)]
+pub const fn get_serialization_path(use_serde: bool) -> &'static str {
+    if use_serde {
+        "core::serde::Serde"
+    } else {
+        "dojo::storage::DojoStore"
+    }
+}

This is zero-cost and keeps the call-sites unchanged (string literals coerce to &'static str).


167-173: Trailing comma logic may mis-place commas

comma is empty only when start == 1, but this is also true for nested tuples that recursed with start = 1. This can produce output like "(e1,e2,)," (note the comma after the closing )), which later string-parsers treat as an extra field. Consider deriving the delimiter from elements.len() or appending , inside the join operation instead.


194-205: Uniform generic notation for serialize / deserialize

deserialize_primitive_member_ty emits

DojoStore::<T>::deserialize(...)

whereas serialize_primitive_member_ty emits

DojoStore::serialize(value, ...)

For Serde the symmetric <T> is already required; for DojoStore it is optional but keeping the two consistent prevents surprises and aligns with the deserialize side:

-    format!(
-        "{path}::serialize({}{member_name}, ref serialized);\n",
-        if with_self { "self." } else { "" },
-    )
+    format!(
+        "{path}::<{member_ty}>::serialize({}{member_name}, ref serialized);\n",
+        if with_self { "self." } else { "" },
+        member_ty = member_name  // you already have the concrete type when `with_self` is false
+    )

(You'll need to thread member_ty into the caller.)

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (1)

170-177: Ohayo sensei – prefer assert_eq! for clearer failure messages

Most tests use assert!(a == b). Switching to assert_eq!(a, b) prints both sides on failure and avoids redundant unwrap() calls:

-let size = Introspect::<GenericStruct<u32>>::size();
-assert!(size.is_some());
-assert!(size.unwrap() == 1);
+assert_eq!(Introspect::<GenericStruct<u32>>::size(), Option::Some(1));

This makes the suite more readable and yields richer diagnostics when a test breaks.

crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (2)

198-233: Consider extracting shared helpers for array-length driven cases

The three write/read/delete cycles here are almost identical to the earlier fixed/tuple tests (only the writer/reader pairs differ). A tiny helper such as fn roundtrip(write, read) would slash ~60 duplicated lines and keep future tests DRY.


242-260: Nit: two almost-identical panic messages – unify spelling to avoid brittle assertions

'invalid array length' vs 'Invalid values length' (capital “I”). If the underlying implementation ever changes its exact wording, half the tests will fail while the other half pass, hiding regressions. Recommend asserting on a shared constant or at least keeping the wording consistent across tests.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (2)

212-220: Copy-paste typo in ByteArray assertions

The failure messages still read “DojoStore …” although the test targets ByteArray. This can mislead newcomers skimming CI logs.

-    assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<i128> serialization failed");
+    assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<ByteArray> serialization failed");
@@
-    assert_eq!(res, Option::Some("hello"), "DojoStore<i128> deserialization failed");
+    assert_eq!(res, Option::Some("hello"), "DojoStore<ByteArray> deserialization failed");

436-441: Impressive stress test, but consider property-based fuzzing for long-term safety

The deeply nested EComplex round-trip is fantastic, yet still a single concrete sample. Leveraging proptest-style fuzzing (once available for Cairo) would give higher confidence that any arbitrary permutation of the same pattern survives serialise/​deserialise cycles.

🛑 Comments failed to post (1)
crates/dojo/lang/src/derive_macros/introspect/mod.rs (1)

215-235: 🛠️ Refactor suggestion

Generic bound builder produces unparsable parameter list

build_generic_impls currently returns a flat string like

"T, +dojo::meta::introspect::Introspect<T>, +dojo::storage::DojoStore<T>, U, +…"

which is inserted inside the <> of the type name. This mixes parameter names (T) with bounds (+dojo::…) without the : separator Cairo expects, giving T, +Trait<T> instead of T: Trait<T>.

Consider emitting "T: Trait1<T> + Trait2<T>" per param and then joining them:

for g in gen_types {
-   format!("{g}, {bounds}")
+   format!("{g}: {}", bounds.join(" + "))
}

where bounds is the mapped list of base_impls. This will align with Cairo’s generic syntax and fix compile failures on every generic type.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents (early access)
In crates/dojo/lang/src/derive_macros/introspect/mod.rs around lines 215 to 235, the function build_generic_impls currently produces a string mixing generic parameter names and trait bounds without the required ':' separator, resulting in invalid syntax like "T, +Trait<T>". To fix this, modify the function to emit each generic parameter followed by a colon and its trait bounds joined with '+', for example "T: Trait1<T> + Trait2<T>", then join these per-parameter strings with commas. This will produce a valid generic parameter list for Cairo.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
crates/dojo/lang/src/utils.rs (1)

158-163: Avoid unimplemented! panics – surface a diagnostic instead

Ohayo sensei! Falling back to unimplemented! here will explode at macro-expansion time and abort compilation, giving the user no actionable hint. We already raised this in an earlier round, so consider piping the unsupported expression into PluginDiagnostic (similar to the tuple-key ordering check) and return a harmless placeholder tuple fragment instead.

🧹 Nitpick comments (4)
crates/dojo/lang/src/utils.rs (1)

13-20: Return a &'static str to skip heap allocation

get_serialization_path() rebuilds the same two strings every call. Converting it to return a &'static str avoids repeated allocations and simplifies call-sites:

-pub fn get_serialization_path(use_serde: bool) -> String {
-    if use_serde { "core::serde::Serde".to_string() } 
-    else { "dojo::storage::DojoStore".to_string() }
+pub fn get_serialization_path(use_serde: bool) -> &'static str {
+    if use_serde { "core::serde::Serde" } else { "dojo::storage::DojoStore" }
 }

Since the value is immediately interpolated into a format! call, lifetimes stay trivial.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (1)

118-121: Derive Default for GenericStruct as well for symmetry

GenericEnum derives Default, but its sibling struct does not. Although DojoStore does not strictly require it for structs, having both generic helpers implement Default offers a consistent public surface and eases test authoring.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (2)

214-220: Assertion message copy-paste typo

Ohayo sensei! The failure string still mentions i128 instead of ByteArray, which could mislead debugging:

-assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<i128> serialization failed");
+assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<ByteArray> serialization failed");
...
-assert_eq!(res, Option::Some("hello"), "DojoStore<i128> deserialization failed");
+assert_eq!(res, Option::Some("hello"), "DojoStore<ByteArray> deserialization failed");

55-173: Consider table-driven tests to cut down boilerplate

The primitive round-trip assertions are excellent but very verbose. A small helper macro (e.g. assert_dojo_roundtrip!(T, value, serialized...)) would slash ~120 lines, keep intent crystal clear, and make future additions painless.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 5786d1f and ea46867.

⛔ Files ignored due to path filters (3)
  • Cargo.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (47)
  • Cargo.toml (1 hunks)
  • crates/benches/contracts/src/models/character.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/lib.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (4 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1 hunks)
  • crates/dojo/core/src/event/component.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/meta/introspect.cairo (1 hunks)
  • crates/dojo/core/src/meta/layout.cairo (1 hunks)
  • crates/dojo/core/src/model/component.cairo (1 hunks)
  • crates/dojo/core/src/model/definition.cairo (1 hunks)
  • crates/dojo/core/src/model/interface.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (4 hunks)
  • crates/dojo/core/src/model/model_value.cairo (2 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/layout.cairo (8 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (1 hunks)
  • crates/dojo/lang/Cargo.toml (1 hunks)
  • crates/dojo/lang/src/attribute_macros/element.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/mod.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3 hunks)
  • crates/dojo/lang/src/cairo_plugin.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs (3 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/mod.rs (1 hunks)
  • crates/dojo/lang/src/lib.rs (2 hunks)
  • crates/dojo/lang/src/utils.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/model.rs (2 hunks)
  • crates/dojo/world/src/contracts/model.rs (2 hunks)
  • crates/sozo/ops/src/model.rs (8 hunks)
  • examples/spawn-and-move/dojo_dev.toml (1 hunks)
  • examples/spawn-and-move/src/models.cairo (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/dojo/lang/src/attribute_macros/mod.rs
  • crates/dojo/lang/src/attribute_macros/element.rs
✅ Files skipped from review due to trivial changes (3)
  • crates/dojo/core-cairo-test/src/lib.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs
🚧 Files skipped from review as they are similar to previous changes (39)
  • crates/dojo/core/src/model/component.cairo
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo
  • crates/dojo/lang/Cargo.toml
  • crates/dojo/core/src/model/definition.cairo
  • crates/dojo/core/src/lib.cairo
  • examples/spawn-and-move/src/models.cairo
  • crates/dojo/core/src/meta/introspect.cairo
  • crates/dojo/lang/src/derive_macros/mod.rs
  • crates/dojo/core/src/model/interface.cairo
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/dojo/core/src/model/model_value.cairo
  • crates/dojo/lang/src/lib.rs
  • crates/dojo/core/src/world/world_contract.cairo
  • crates/dojo/core/src/meta/layout.cairo
  • crates/dojo/world/src/contracts/model.rs
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo
  • crates/dojo/lang/src/attribute_macros/event.rs
  • crates/dojo/world/src/contracts/abigen/model.rs
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo
  • crates/dojo/core/src/storage/layout.cairo
  • crates/dojo/lang/src/cairo_plugin.rs
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo
  • crates/dojo/core/src/event/component.cairo
  • crates/dojo/lang/src/attribute_macros/model.rs
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs
  • examples/spawn-and-move/dojo_dev.toml
  • crates/dojo/core/src/model/model.cairo
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo
  • Cargo.toml
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs
  • crates/benches/contracts/src/models/character.cairo
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo
  • crates/sozo/ops/src/model.rs
  • crates/dojo/core/src/storage/dojo_store.cairo
🔇 Additional comments (1)
crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (1)

151-157: Confirm downstream code handles one-based variant selectors

The new _enum helper now shifts variant to i + 1, matching the DojoStore convention. Please double-check any runtime that still expects zero-based discriminants (legacy generators, Sozo CLI, on-chain deserialisers) to avoid mismatched layouts.

@remybar remybar force-pushed the fix-enum_options_storage branch from ea46867 to ff6958f Compare May 1, 2025 09:48
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1)

1-260: Consider extracting common test helpers to reduce duplication

The test file has some repetitive layout construction patterns that could be extracted into helper functions to improve readability and maintainability.

+ /// Creates a legacy Option<T> enum layout with the given inner layout
+ fn create_legacy_option_layout(inner_layout: Layout) -> Layout {
+     Layout::Enum(
+         [
+             dojo::meta::FieldLayout { // Some
+                 selector: 0, layout: inner_layout,
+             },
+             dojo::meta::FieldLayout { // None
+                 selector: 1, layout: Layout::Fixed([].span()),
+             },
+         ]
+             .span(),
+     )
+ }

This would allow simplifying test assertions like:

- let expected = Layout::Enum(
-     [
-         dojo::meta::FieldLayout { // Some
-             selector: 0, layout: Introspect::<u32>::layout(),
-         },
-         dojo::meta::FieldLayout { // None
-             selector: 1, layout: Layout::Fixed([].span()),
-         },
-     ]
-         .span(),
- );
+ let expected = create_legacy_option_layout(Introspect::<u32>::layout());
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between ea46867 and ff6958f.

⛔ Files ignored due to path filters (6)
  • Cargo.lock is excluded by !**/*.lock
  • crates/dojo/core-cairo-test/Scarb.lock is excluded by !**/*.lock
  • examples/simple/Scarb.lock is excluded by !**/*.lock
  • examples/spawn-and-move/Scarb.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (47)
  • Cargo.toml (1 hunks)
  • crates/benches/contracts/src/models/character.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/lib.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (4 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1 hunks)
  • crates/dojo/core/src/event/component.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/meta/introspect.cairo (1 hunks)
  • crates/dojo/core/src/meta/layout.cairo (1 hunks)
  • crates/dojo/core/src/model/component.cairo (1 hunks)
  • crates/dojo/core/src/model/definition.cairo (1 hunks)
  • crates/dojo/core/src/model/interface.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (4 hunks)
  • crates/dojo/core/src/model/model_value.cairo (2 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/layout.cairo (8 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (1 hunks)
  • crates/dojo/lang/Cargo.toml (1 hunks)
  • crates/dojo/lang/src/attribute_macros/element.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/mod.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3 hunks)
  • crates/dojo/lang/src/cairo_plugin.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs (3 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/mod.rs (1 hunks)
  • crates/dojo/lang/src/lib.rs (2 hunks)
  • crates/dojo/lang/src/utils.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/model.rs (2 hunks)
  • crates/dojo/world/src/contracts/model.rs (2 hunks)
  • crates/sozo/ops/src/model.rs (8 hunks)
  • examples/spawn-and-move/dojo_dev.toml (1 hunks)
  • examples/spawn-and-move/src/models.cairo (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/dojo/lang/src/attribute_macros/mod.rs
  • crates/dojo/lang/src/attribute_macros/element.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/dojo/lang/src/utils.rs
🚧 Files skipped from review as they are similar to previous changes (41)
  • crates/dojo/core/src/model/definition.cairo
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/dojo/core/src/model/component.cairo
  • crates/dojo/lang/src/derive_macros/mod.rs
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo
  • examples/spawn-and-move/src/models.cairo
  • crates/dojo/core/src/model/interface.cairo
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs
  • crates/dojo/lang/Cargo.toml
  • crates/dojo/core/src/model/model_value.cairo
  • crates/dojo/lang/src/lib.rs
  • crates/dojo/core/src/meta/introspect.cairo
  • crates/dojo/core/src/lib.cairo
  • crates/dojo/core/src/world/world_contract.cairo
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs
  • crates/dojo/core/src/event/component.cairo
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo
  • crates/dojo/lang/src/attribute_macros/event.rs
  • crates/dojo/world/src/contracts/abigen/model.rs
  • examples/spawn-and-move/dojo_dev.toml
  • crates/dojo/core/src/storage/layout.cairo
  • crates/dojo/core/src/meta/layout.cairo
  • crates/dojo/world/src/contracts/model.rs
  • crates/dojo/core-cairo-test/src/lib.cairo
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo
  • crates/dojo/core/src/model/model.cairo
  • Cargo.toml
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo
  • crates/dojo/lang/src/cairo_plugin.rs
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs
  • crates/benches/contracts/src/models/character.cairo
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs
  • crates/sozo/ops/src/model.rs
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo
  • crates/dojo/core/src/storage/dojo_store.cairo
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: fmt
🔇 Additional comments (15)
crates/dojo/lang/src/attribute_macros/model.rs (7)

61-72: Ohayo! Restricting generic models is a good defensive measure

Validating that models cannot be generic aligns perfectly with the PR objective since generic structs and enums aren't yet supported by the DojoStore trait's implementation. Clear error message with proper severity.


80-81: Smart detection of legacy storage flag, sensei!

This flag detection cleanly implements the backward compatibility strategy mentioned in the PR objectives, allowing models to choose between legacy Serde-based storage and the new DojoStore trait.


101-132: Enforcing key ordering is a wise structural constraint

This validation ensures that all key members appear before non-key members, providing clear diagnostics without interrupting the processing flow. This maintains a consistent model structure that's easier to reason about.


138-142: Clean propagation of legacy storage flag to serialization/deserialization helpers

The code properly forwards the legacy storage flag to both serialization and deserialization helper functions, ensuring consistent behavior throughout the serialization stack.

Also applies to: 146-150, 151-155


238-242: Excellent conditional path selection based on storage mode

This cleanly selects the appropriate deserialization path - either the legacy Serde implementation or the new DojoStore trait - based on the legacy storage flag.


244-252: Nice implementation of conditional layout generation

The code smartly switches between standard layout introspection and legacy layout building based on the storage strategy. This maintains backward compatibility while allowing new models to use the improved storage semantics.


263-264: Well integrated interpolation values for new deserialization approach

The new interpolation parameters for deserialized values, modelvalue creation and legacy storage flag properly wire up the template with the conditional code paths.

Also applies to: 276-278

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (4)

5-5: Ohayo sensei! Comprehensive Default trait implementation for storage

The addition of Default trait and Serde derivations to structs and enums is essential for supporting the new DojoStore trait requirements. Every enum also has a properly annotated #[default] variant, which ensures correct behavior during deserialization when handling uninitialized values.

The new GenericEnum<T> addition complements the renamed GenericStruct<T> (previously just Generic<T>) for more consistent naming and better test coverage of generic types.

Also applies to: 40-41, 58-59, 66-67, 74-75, 82-83, 91-92, 98-99, 105-106, 112-113, 117-118, 122-126


151-153: Critical change to enum variant indexing

This change addresses the core issue from the PR objectives - shifting enum variants from 0-based to 1-based indexing so that variant 0 can be used to represent uninitialized values. The comment clearly explains the reasoning behind this change.


170-190: Thorough tests for generic struct introspection

These tests validate that the introspection system properly handles generic structs, checking size, layout, and type information. This ensures the infrastructure supports the generic types that will be needed in future PRs.


192-212: Comprehensive generic enum introspection tests

Similar to the generic struct tests, these validate that the introspection system correctly handles generic enums, testing size, layout and type information. The tests are well-structured and verify all necessary aspects of the new functionality.

crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (4)

1-19: Ohayo! Clean Option legacy layout test

This test verifies that build_legacy_layout correctly transforms Option<u32> from the new 1-based indexing (variants at indexes 1 and 2) to the legacy 0-based indexing (variants at indexes 0 and 1). This is essential for backward compatibility.


21-44: Excellent simple enum conversion test

This test validates that the build_legacy_layout function correctly shifts enum variant selectors from 1-based indexing to 0-based indexing for legacy compatibility. The test case covers a simple enum with multiple variants, ensuring thorough coverage.


46-71: Comprehensive tests for compound types with Option elements

These tests for tuples, arrays, and structs containing Option<T> elements ensure that the build_legacy_layout function correctly handles nested data structures. Each test verifies that options are properly converted to legacy enum layouts with 0-based indexing.

Also applies to: 73-95, 97-129


131-194: Thorough tests for deeply nested data structures

These tests validate that build_legacy_layout correctly handles complex nested structures combining enums, arrays, tuples, and options. The tests are comprehensive and ensure that the legacy conversion works correctly at all levels of nesting.

The consistent test structure and detailed assertions make it easy to understand the expected behavior.

Also applies to: 196-260

@remybar remybar force-pushed the fix-enum_options_storage branch from ff6958f to ed43a45 Compare May 1, 2025 10:20
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
crates/dojo/lang/src/utils.rs (1)

158-163: ⚠️ Potential issue

Avoid unimplemented! – return a placeholder and emit diagnostics instead
The macro currently panics on any unsupported expression inside a tuple,
aborting the entire plugin and producing a compiler error that is hard to trace.
A graceful fallback—returning a dummy variable name and pushing a diagnostic—was
requested in a previous review but is still outstanding.

🧹 Nitpick comments (7)
crates/dojo/lang/src/utils.rs (1)

14-20: Prefer returning a &'static str to avoid repeated heap allocations

get_serialization_path is invoked from every (de)serialization helper.
Returning an owned String allocates on every call, which is unnecessary because
the returned value is always one of two static literals.

-#[inline(always)]
-pub fn get_serialization_path(use_serde: bool) -> String {
-    if use_serde {
-        "core::serde::Serde".to_string()
-    } else {
-        "dojo::storage::DojoStore".to_string()
-    }
+#[inline(always)]
+pub const fn get_serialization_path(use_serde: bool) -> &'static str {
+    if use_serde {
+        "core::serde::Serde"
+    } else {
+        "dojo::storage::DojoStore"
+    }
 }

This change removes the allocation, shortens generated code, and may shave a few
µs off macro expansion time.

crates/dojo/core/src/model/model.cairo (1)

84-88: Document the semantics of use_legacy_storage

Great to see run-time introspection of storage strategy!
A short doc-comment explaining that true ⇒ Serde layout, false ⇒ DojoStore
layout, will make the API self-explanatory for downstream tooling.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (2)

170-212: Great test coverage for the generic types!

The tests thoroughly verify the introspection of generic types, including size, layout, and type information. This ensures that the introspection system can handle generics correctly.

Consider adding a comment in test_generic_enum_introspect explaining that the size of 2 comes from the enum discriminant (variant index) plus the u32 value.


272-283: Ohayo sensei! Minor style issue with double blank line.

There's an extra blank line that could be removed for consistency with the rest of the file.

 assert!(size.unwrap() == 4);
}

-

#[test]
fn test_size_of_enum_with_variant_data() {
crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (3)

216-220: Fix comment in ByteArray test - incorrect type reference.

The comments for ByteArray serialization and deserialization incorrectly refer to i128 instead of ByteArray.

-assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<i128> serialization failed");
+assert_eq!(serialized, array![0, 0x68656c6c6f, 0x05], "DojoStore<ByteArray> serialization failed");

let mut values = [0, 0x68656c6c6f, 0x05].span();
let res = DojoStore::<ByteArray>::deserialize(ref values);
-assert_eq!(res, Option::Some("hello"), "DojoStore<i128> deserialization failed");
+assert_eq!(res, Option::Some("hello"), "DojoStore<ByteArray> deserialization failed");

277-280: Add clarity about default value handling for variant 0.

This test case demonstrates a crucial part of the PR's purpose - handling variant 0 as uninitialized storage. A comment would help future developers understand this special case.

let mut values = [0].span();
let res = DojoStore::<E>::deserialize(ref values);
+// Variant 0 is treated as uninitialized storage and returns the default value
assert_eq!(res, Option::Some(E::A(0)), "DojoStore<E> deserialization failed");

353-353: Minor style issue with double blank line.

There's an extra blank line that could be removed for consistency with the rest of the file.

}

-

#[test]
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between ff6958f and ed43a45.

⛔ Files ignored due to path filters (6)
  • Cargo.lock is excluded by !**/*.lock
  • crates/dojo/core-cairo-test/Scarb.lock is excluded by !**/*.lock
  • examples/simple/Scarb.lock is excluded by !**/*.lock
  • examples/spawn-and-move/Scarb.lock is excluded by !**/*.lock
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (47)
  • Cargo.toml (1 hunks)
  • crates/benches/contracts/src/models/character.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/lib.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo (5 hunks)
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3 hunks)
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (4 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo (1 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo (2 hunks)
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo (1 hunks)
  • crates/dojo/core/src/event/component.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/meta/introspect.cairo (1 hunks)
  • crates/dojo/core/src/meta/layout.cairo (1 hunks)
  • crates/dojo/core/src/model/component.cairo (1 hunks)
  • crates/dojo/core/src/model/definition.cairo (1 hunks)
  • crates/dojo/core/src/model/interface.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (4 hunks)
  • crates/dojo/core/src/model/model_value.cairo (2 hunks)
  • crates/dojo/core/src/storage/dojo_store.cairo (1 hunks)
  • crates/dojo/core/src/storage/layout.cairo (8 hunks)
  • crates/dojo/core/src/world/world_contract.cairo (1 hunks)
  • crates/dojo/lang/Cargo.toml (1 hunks)
  • crates/dojo/lang/src/attribute_macros/element.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/event.rs (2 hunks)
  • crates/dojo/lang/src/attribute_macros/mod.rs (0 hunks)
  • crates/dojo/lang/src/attribute_macros/model.rs (6 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo (1 hunks)
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo (3 hunks)
  • crates/dojo/lang/src/cairo_plugin.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs (1 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs (3 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/mod.rs (9 hunks)
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs (2 hunks)
  • crates/dojo/lang/src/derive_macros/mod.rs (1 hunks)
  • crates/dojo/lang/src/lib.rs (2 hunks)
  • crates/dojo/lang/src/utils.rs (1 hunks)
  • crates/dojo/world/src/contracts/abigen/model.rs (2 hunks)
  • crates/dojo/world/src/contracts/model.rs (2 hunks)
  • crates/sozo/ops/src/model.rs (8 hunks)
  • examples/spawn-and-move/dojo_dev.toml (1 hunks)
  • examples/spawn-and-move/src/models.cairo (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/dojo/lang/src/attribute_macros/mod.rs
  • crates/dojo/lang/src/attribute_macros/element.rs
🚧 Files skipped from review as they are similar to previous changes (40)
  • crates/dojo/core/src/model/interface.cairo
  • crates/dojo/core/src/model/definition.cairo
  • crates/dojo/core/src/lib.cairo
  • crates/dojo/core-cairo-test/src/tests/world/storage.cairo
  • crates/dojo/core/src/model/component.cairo
  • crates/dojo/core-cairo-test/src/tests/world/event.cairo
  • crates/dojo/core/src/storage/layout.cairo
  • examples/spawn-and-move/src/models.cairo
  • crates/dojo/lang/src/attribute_macros/patches/event.patch.cairo
  • crates/dojo/core-cairo-test/src/tests/expanded/selector_attack.cairo
  • crates/dojo/core/src/event/component.cairo
  • crates/dojo/world/src/contracts/model.rs
  • crates/dojo/core/src/world/world_contract.cairo
  • crates/dojo/core/src/model/model_value.cairo
  • crates/dojo/core-cairo-test/src/lib.cairo
  • crates/dojo/core/src/meta/introspect.cairo
  • crates/dojo/lang/Cargo.toml
  • crates/dojo/lang/src/derive_macros/introspect/layout.rs
  • crates/dojo/lang/src/lib.rs
  • crates/dojo/core/src/meta/layout.cairo
  • crates/dojo/world/src/contracts/abigen/model.rs
  • examples/spawn-and-move/dojo_dev.toml
  • crates/dojo/core-cairo-test/src/tests/world/model.cairo
  • crates/dojo/core-cairo-test/src/tests/helpers/helpers.cairo
  • crates/dojo/lang/src/derive_macros/introspect/utils.rs
  • crates/dojo/lang/src/derive_macros/mod.rs
  • crates/dojo/core-cairo-test/src/tests/helpers/model.cairo
  • crates/dojo/lang/src/cairo_plugin.rs
  • crates/dojo/lang/src/attribute_macros/model.rs
  • Cargo.toml
  • crates/dojo/lang/src/attribute_macros/patches/model.patch.cairo
  • crates/dojo/lang/src/attribute_macros/event.rs
  • crates/dojo/core-cairo-test/src/tests/helpers/event.cairo
  • crates/benches/contracts/src/models/character.cairo
  • crates/dojo/core-cairo-test/src/tests/meta/layout.cairo
  • crates/dojo/core-cairo-test/src/tests/storage/layout.cairo
  • crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo
  • crates/sozo/ops/src/model.rs
  • crates/dojo/core/src/storage/dojo_store.cairo
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: fmt
🔇 Additional comments (7)
crates/dojo/core/src/model/model.cairo (1)

53-55: Clarify deserialize contract – expects keys + values or values only?

from_serialized concatenates keys and values before passing the slice
to ModelParser::deserialize. If an implementation of ModelParser assumes it
receives values only, subtle decoding bugs will surface.

Please confirm in the derive macro that deserialize is implemented with the
same concatenation logic, or split the parameters explicitly.

crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo (3)

5-110: Ohayo sensei! Good work implementing the Default trait for all structs and enums!

You've consistently added #[derive(Default)] and #[default] annotations to ensure that all types have proper default values. This change is crucial for the new DojoStore trait to handle uninitialized storage values correctly.


112-126: Nice implementation of generic types with proper trait derivations!

I like how you've renamed Generic<T> to GenericStruct<T> for clarity and added the new GenericEnum<T> type. Both derive the necessary traits for proper serialization and introspection.


140-157: Critical change to enum variant indexing - excellent implementation!

The shift from zero-based to one-based indexing for enum variants (with the comment explaining why) is the key fix for the storage issue with enums and options.

 // in the new Dojo storage layout, variants start from 1.
 let variant = i + 1;

This ensures that variant ID 0 can be used to represent uninitialized storage, which solves the core problem mentioned in the PR description.

crates/dojo/core-cairo-test/src/tests/storage/dojo_store.cairo (3)

3-53: Well-structured test types with appropriate trait derivations!

You've created a good variety of test types with different structures and complexities, all deriving the necessary traits for testing the DojoStore functionality. The mix of primitive types, complex nested structures, and generics provides a comprehensive test suite.


248-252: Great testing of Option serialization with variant indices!

Ohayo sensei! Your testing shows how the DojoStore trait represents Option::Some with index 1 and Option::None with index 2, implementing the variant shift mentioned in the PR objectives.


388-442: Excellent comprehensive test for complex nested types!

The test_mix function thoroughly tests serialization and deserialization of deeply nested data structures with multiple types. This is a great way to ensure the DojoStore trait works correctly with real-world complex data.

Comment on lines +54 to +90
let mut parsing_keys = true;

members
.iter()
.map(|member_ast| {
let is_key = member_ast.has_attr(db, "key");

let member = Member {
name: member_ast.name(db).text(db).to_string(),
ty: member_ast
.type_clause(db)
.ty(db)
.as_syntax_node()
.get_text(db)
.trim()
.to_string(),
key: is_key,
};

// Make sure all keys are before values in the model.
if is_key && !parsing_keys {
diagnostics.push(PluginDiagnostic {
message: "Key members must be defined before non-key members.".into(),
stable_ptr: member_ast.name(db).stable_ptr().untyped(),
severity: Severity::Error,
});
// Don't return here, since we don't want to stop processing the members after the
// first error to avoid diagnostics just because the field is
// missing.
}

parsing_keys &= is_key;

member
})
.collect::<Vec<_>>()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Possible missing validation for duplicated #[key] fields

parse_members enforces key-before-value ordering, but it does not check:

  1. That at least one #[key] exists.
  2. That no two members are marked as the same key (e.g. duplicate names).

If an end-user mistakenly tags multiple fields with #[key] the generated
layout and entity_id computation become ambiguous.

Consider adding:

• A counter to ensure ≥ 1 and flag duplicates.
• Diagnostics with Severity::Error for violations.

🤖 Prompt for AI Agents (early access)
In crates/dojo/lang/src/utils.rs between lines 54 and 90, the parse_members function currently checks key-before-value ordering but does not validate that at least one member is marked as a key or that there are no duplicate key members. To fix this, add a counter or a set to track members marked with #[key], verify that at least one key exists after processing all members, and check for duplicates by comparing member names or identifiers. For any violations, push diagnostics with Severity::Error to inform the user about missing or duplicate key fields.

Comment on lines +215 to 235
fn build_generic_impls(
gen_types: &[String],
base_impls: &[String],
additional_impls: &[String],
) -> String {
let mut gen_impls = gen_types
.iter()
.map(|g| format!("{g}, impl {g}Introspect: dojo::meta::introspect::Introspect<{g}>"))
.collect::<Vec<_>>()
.join(", ");
.map(|g| {
format!(
"{g}, {base_impls}",
base_impls = base_impls.iter().map(|i| format!("{i}<{g}>")).join(", ")
)
})
.collect::<Vec<_>>();

if !gen_types.is_empty() {
gen_impls.extend(additional_impls.to_vec());
}

(generic_types, generic_impls)
gen_impls.join(", ")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Generated generic impl list duplicates type identifiers

build_generic_impls currently generates
"T, +DojoStore<T>, U, +DojoStore<U>" for <T, U>, mixing types and trait
bounds in a single comma-separated list. Cairo expects either:

T, U in the type list, and
+DojoStore<T>, +DojoStore<U> in the impl list,

but not interleaved.

The interleaving compiles only by accident and becomes unreadable with several
bounds.

Consider splitting return values:

struct GenericParts {
    types: String,   // "T, U"
    impls: String,   // "+DojoStore<T>, +DojoStore<U>"
}

and interpolating them into the template separately.

Comment on lines +82 to +86
let gen_types = build_generic_types(db, enum_ast.generic_params(db));
let gen_joined_types = gen_types.join(", ");

let enum_name_with_generics = format!("{enum_name}<{gen_joined_types}>");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

<> emitted for non-generic enums ⇒ invalid Cairo syntax

When gen_types.is_empty(), gen_joined_types is "", so
enum_name_with_generics becomes "MyEnum<>", which does not parse.

-let enum_name_with_generics = format!("{enum_name}<{gen_joined_types}>");
+let enum_name_with_generics = if gen_types.is_empty() {
+    enum_name.clone()
+} else {
+    format!("{enum_name}<{gen_joined_types}>")
+};

Failing to guard here will break every non-generic enum/model processed by the
macro.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let gen_types = build_generic_types(db, enum_ast.generic_params(db));
let gen_joined_types = gen_types.join(", ");
let enum_name_with_generics = format!("{enum_name}<{gen_joined_types}>");
let gen_types = build_generic_types(db, enum_ast.generic_params(db));
let gen_joined_types = gen_types.join(", ");
let enum_name_with_generics = if gen_types.is_empty() {
enum_name.clone()
} else {
format!("{enum_name}<{gen_joined_types}>")
};
🤖 Prompt for AI Agents (early access)
In crates/dojo/lang/src/derive_macros/introspect/mod.rs around lines 82 to 86, the code always appends angle brackets to the enum name even when there are no generic types, resulting in invalid syntax like "MyEnum<>". To fix this, add a conditional check to only append "<{gen_joined_types}>" when gen_types is not empty; otherwise, use just the enum name without angle brackets.

@glihm
Copy link
Collaborator

glihm commented May 13, 2025

Should target dev-1.6.0. 🫡

@remybar
Copy link
Collaborator Author

remybar commented May 14, 2025

Closed because manually recreated for 1.6.0: #3219

@remybar remybar closed this May 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants