### SpacetimeDSL Method Configuration Examples (Rust) Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md Provides Rust code examples illustrating different SpacetimeDSL method configurations for entities with varying mutability requirements. These examples show how to set `update` and `delete` methods to `true` or `false` based on the entity's intended lifecycle. ```rust // Immutable audit log - never changes after creation #[spacetimedsl::dsl( plural_name = audit_logs, method(update = false, delete = false) )] pub struct AuditLog { /* ... */ } // User profiles - can be updated but never deleted #[spacetimedsl::dsl( plural_name = user_profiles, method(update = true, delete = false) )] pub struct UserProfile { /* ... */ } // Temporary cache entries - can be both updated and deleted #[spacetimedsl::dsl( plural_name = cache_entries, method(update = true, delete = true) )] pub struct CacheEntry { /* ... */ } ``` -------------------------------- ### SpacetimeDSL Game Server Example: Entities, Players, Positions Source: https://context7.com/tamaro-skaljic/spacetimedsl/llms.txt This Rust code provides a comprehensive example of integrating SpacetimeDSL into a game server. It defines `Entity`, `Player`, and `Position` structs with appropriate foreign key relationships and demonstrates reducer functions for spawning players, moving them, and killing them, showcasing how entity deletion cascades to related player and position data. ```rust use spacetimedb::{Identity, ReducerContext, Timestamp}; use spacetimedsl::prelude::*; #[dsl(plural_name = entities, method(update = true, delete = true))] #[spacetimedb::table(name = entity, public)] pub struct Entity { #[primary_key] #[auto_inc] #[create_wrapper] #[referenced_by(path = self, table = player)] #[referenced_by(path = self, table = position)] id: i32, pub mass: i32, } #[dsl(plural_name = players, method(update = true, delete = true))] #[spacetimedb::table(name = player, public)] pub struct Player { #[primary_key] #[use_wrapper(EntityId)] #[foreign_key(path = self, table = entity, column = id, on_delete = Delete)] entity_id: i32, #[unique] identity: Identity, pub name: String, } #[dsl(plural_name = positions, method(update = true, delete = true))] #[spacetimedb::table(name = position, public)] pub struct Position { #[primary_key] #[use_wrapper(EntityId)] #[foreign_key(path = self, table = entity, column = id, on_delete = Delete)] entity_id: i32, pub x: f32, pub y: f32, } #[spacetimedb::reducer] pub fn spawn_player(ctx: &ReducerContext, name: String) -> Result<(), SpacetimeDSLError> { let dsl = dsl(ctx); // Create entity first let entity = dsl.create_entity(CreateEntity { mass: 10 })?; // Create player linked to entity dsl.create_player(CreatePlayer { entity_id: entity.get_id(), identity: ctx.sender, name, })?; // Create position linked to entity dsl.create_position(CreatePosition { entity_id: entity.get_id(), x: 0.0, y: 0.0, })?; Ok(()) } #[spacetimedb::reducer] pub fn move_player(ctx: &ReducerContext, dx: f32, dy: f32) -> Result<(), SpacetimeDSLError> { let dsl = dsl(ctx); let player = dsl.get_player_by_identity(&ctx.sender)?; let mut position = dsl.get_position_by_entity_id(player.get_entity_id())?; position.set_x(*position.get_x() + dx); position.set_y(*position.get_y() + dy); dsl.update_position_by_entity_id(position)?; Ok(()) } #[spacetimedb::reducer] pub fn kill_player(ctx: &ReducerContext) -> Result<(), SpacetimeDSLError> { let dsl = dsl(ctx); let player = dsl.get_player_by_identity(&ctx.sender)?; // Deleting entity cascades to player and position via foreign keys dsl.delete_entity_by_id(player.get_entity_id())?; Ok(()) } ``` -------------------------------- ### Initialize SpacetimeDSL Wrapper Source: https://context7.com/tamaro-skaljic/spacetimedsl/llms.txt Demonstrates how to create a DSL wrapper around the SpacetimeDB context using the `dsl()` function. This wrapper provides access to generated methods for interacting with SpacetimeDB tables. It's recommended to create this instance once at the start of a reducer. ```rust use spacetimedsl::prelude::*; #[spacetimedb::reducer] fn my_reducer(ctx: &ReducerContext) -> Result<(), String> { // Create DSL wrapper around the context let dsl = spacetimedbl::dsl(ctx); // Access the underlying context when needed let timestamp = dsl.ctx().timestamp; let sender = dsl.ctx().sender; // Pass DSL to helper functions process_data(&dsl)?; Ok(()) } fn process_data(dsl: &DSL<'_, T>) -> Result<(), SpacetimeDSLError> { // Use DSL methods here let entity = dsl.create_entity()?; Ok(()) } ``` -------------------------------- ### Hook-Method Compatibility Examples Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md These Rust code snippets demonstrate the compatibility requirements between hook configurations and method configurations in SpacetimeDSL. The first example shows an invalid configuration where an update hook is used without enabling the update method, leading to a compile error. The second example shows a valid configuration where the update method is enabled, allowing the update hook to function correctly. ```rust // ❌ Invalid: update hook without update method #[spacetimedsl::dsl( plural_name = entities, method(update = false, delete = true), hook(after(update)) // Compile error! )] pub struct Entity { /* ... */ } // ✅ Valid: update hook with update method enabled #[spacetimedsl::dsl( plural_name = entities, method(update = true, delete = true), hook(after(update)) // OK! )] pub struct Entity { /* ... */ } ``` -------------------------------- ### Iterating Collections with SpacetimeDSL Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Demonstrates how to iterate over collections of data and count them using the SpacetimeDSL. It shows fetching all positions, iterating through them, and getting the total count. It also includes examples of fetching and deleting data based on specific column values. ```rust let positions = dsl.get_all_positions(); for position in positions { println!("Position: {:?}", position); } let count: usize = dsl.count_of_all_positions(); // With indices let positions = dsl.get_positions_by_x(&5); let count = dsl.delete_positions_by_x(&5)?; ``` -------------------------------- ### SpacetimeDSL Error Handling Example Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Illustrates how to handle potential errors when using SpacetimeDSL operations, such as creating an entity. It shows how to match against different SpacetimeDSLError variants like UniqueConstraintViolation and ReferenceIntegrityViolation. ```rust match dsl.create_entity() { Ok(entity) => { /* ... */ }, Err(SpacetimeDSLError::UniqueConstraintViolation { .. }) => { // Handle duplicate }, Err(SpacetimeDSLError::ReferenceIntegrityViolation(err)) => { // Handle foreign key violation let deletion_result = err.deletion_result(); }, Err(e) => return Err(e.to_string()), } ``` -------------------------------- ### Rust SpacetimeDSL Multiple Foreign Keys to Same Table Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Provides a Rust example of configuring a table with multiple foreign keys pointing to the same target table using SpacetimeDSL. It illustrates how to define `parent_entity_id` and `child_entity_id` referencing the `entity` table with different `on_delete` strategies. ```rust pub struct EntityRelationship { #[primary_key] #[create_wrapper] id: u128, #[use_wrapper(EntityId)] #[foreign_key(path = crate, table = entity, on_delete = Error)] parent_entity_id: u128, #[use_wrapper(EntityId)] #[foreign_key(path = crate, table = entity, on_delete = Delete)] child_entity_id: u128, } ``` -------------------------------- ### Rust SpacetimeDSL Working with Wrapper Types Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Explains how to use wrapper types in Rust with SpacetimeDSL for enhanced type safety and flexibility. It shows examples of creating wrapper instances, extracting raw values, obtaining wrappers from entities, and passing them to methods that accept multiple forms. ```rust // Creating // let entity_id = EntityId::new(123); // Extracting value // let raw_id: u128 = entity_id.value(); // From entity // let id = entity.get_obj_id(); // Returns EntityId // Method accepts multiple forms // dsl.get_position_by_entity_id(&entity)?; // dsl.get_position_by_entity_id(entity.get_obj_id())?; // dsl.get_position_by_entity_id(EntityId::new(123))?; ``` -------------------------------- ### Rust SpacetimeDSL Self-Referencing Table Configuration Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Shows how to configure a self-referencing table in Rust using SpacetimeDSL attributes. The example demonstrates setting up a `parent_id` that references the same table (`entity_relationship3`) with update and delete methods enabled. ```rust @dsl(plural_name = entity_relationships3, method(update = true, delete = true)) pub struct EntityRelationship3 { #[primary_key] #[auto_inc] #[create_wrapper] #[referenced_by(path = crate, table = entity_relationship3)] id: u128, #[use_wrapper(EntityRelationship3Id)] #[foreign_key(path = crate, table = entity_relationship3, on_delete = SetZero)] pub parent_id: u128, } ``` -------------------------------- ### Rust SpacetimeDSL Timestamp Management Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Demonstrates automatic timestamp management for entity creation and updates in Rust using SpacetimeDSL. The example shows how `created_at` and `modified_at` fields are automatically populated based on context during create and update operations. ```rust pub struct Entity { created_at: Timestamp, // Auto-set on create modified_at: Option, // Auto-set on update } // Timestamps automatically handled // let entity = dsl.create_entity()?; // created_at = ctx.timestamp // let entity = dsl.update_entity_by_id(entity)?; // modified_at = Some(ctx.timestamp) ``` -------------------------------- ### Configure SpacetimeDSL Methods with Rust Attributes Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Demonstrates how to explicitly control generated methods (update, delete) for Rust structs using `#[dsl]` attributes. It shows examples of immutable, updatable, and fully mutable entities, highlighting the link between field visibility and method generation. ```rust pub struct AuditLog { #[primary_key] id: u128, created_at: Timestamp, } #[dsl(plural_name = audit_logs, method(update = false, delete = false))] // Immutable audit log - all fields private, no modified_at/updated_at pub struct UserProfile { #[primary_key] id: u128, pub name: String, // Public field enables updates modified_at: Option, } #[dsl(plural_name = user_profiles, method(update = true, delete = false))] // Updatable - has public field OR modified_at/updated_at pub struct CacheEntry { #[primary_key] id: u128, pub data: String, } #[dsl(plural_name = cache_entries, method(update = true, delete = true))] // Fully mutable ``` -------------------------------- ### Perform CRUD Operations with SpacetimeDSL in Rust Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Demonstrates common SpacetimeDB operations like creating, getting, updating, and deleting entities using the SpacetimeDSL. It requires a ReducerContext to initialize the DSL. ```rust #[spacetimedb::reducer] fn example(ctx: &ReducerContext) -> Result<(), String> { let dsl = spacetimedsl::dsl(ctx); // Create with smart defaults let entity = dsl.create_entity()?; // Get by ID let entity = dsl.get_entity_by_id(&entity)?; // Update let mut entity = dsl.get_entity_by_id(&entity)?; entity.set_some_field(value); dsl.update_entity_by_id(entity)?; // Delete dsl.delete_entity_by_id(&entity)?; Ok(()) } ``` -------------------------------- ### Implement SpacetimeDSL Hook Functions Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md This code illustrates the implementation of various hook functions in Rust, marked with the `#[spacetimedsl::hook]` attribute. It shows examples for `after_entity_insert`, `before_entity_update` (including mutation of the new row and error handling), and `before_entity_delete`. ```rust use spacetimedsl::hook; // After insert hook #[hook] pub fn after_entity_insert(dsl: &spacetimedsl::DSL<'_, T>, row: &Entity) -> Result<(), SpacetimeDSLError> { log::info!("Inserted entity with id={}", row.id()); Ok(()) } // Before update hook - has access to both old and new values and can Mutate the new row before the update occurs #[hook] pub fn before_entity_update( dsl: &spacetimedsl::DSL<'_, T>, old_row: &Entity, mut new_row: Entity ) -> Result { if new_row.get_value().is_empty() { return Err(SpacetimeDSLError::Error("Value cannot be empty".to_string())); } log::info!("Updating entity {} from '{}' to '{}'", old_row.get_id(), old_row.get_value(), new_row.get_value()); Ok(new_row) } // Before delete hook #[hook] pub fn before_entity_delete(dsl: &spacetimedsl::DSL<'_, T>, row: &Entity) -> Result<(), SpacetimeDSLError> { log::info!("Deleting entity with id={}", row.id()); Ok(()) } ``` -------------------------------- ### Rust Struct Definition with Wrapper Type Attributes Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md Example of a Rust struct 'Entity' demonstrating the usage of #[create_wrapper] and #[use_wrapper] attributes. These attributes control the generation and usage of wrapper types for fields like 'id' and 'parent_entity_id', promoting type safety and domain modeling. ```rust @spacetimedsl::dsl(plural_name = entities, method(update = false)) @spacetimedb::table(name = entity, public) pub struct Entity { // Default Name Strategy: EntityId // format!("{}{}", singular_table_name_pascal_case, column_name_pascal_case) #[create_wrapper] // Custom Name Strategy: EntityID #[create_wrapper(EntityID)] id: u128, // Use a wrapper type from the same module #[use_wrapper(EntityId)] // Use a wrapper type from another module #[use_wrapper(crate::entity::EntityId)] parent_entity_id: u128, } ``` -------------------------------- ### SpacetimeDSL Unique Multi-Column Index Example Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md This Rust code snippet demonstrates how to define a unique multi-column index for the EntityRelationship table using SpacetimeDSL attributes. It requires matching configurations in both `#[spacetimedsl::dsl]` and `#[spacetimedb::table]` to enforce uniqueness checks on create/update operations. ```rust #[dsl( plural_name = entity_relationships, method(update = false), unique_index(name = parent_child_entity_id) )] #[table( name = entity_relationship, public, index(name = parent_child_entity_id, btree(columns = [parent_entity_id, child_entity_id])) )] pub struct EntityRelationship { #[primary_key] #[auto_inc] id: u128, parent_entity_id: u128, child_entity_id: u128, } ``` -------------------------------- ### Implement Create Operation with SpacetimeDB and SpacetimeDSL (Rust) Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md Demonstrates the `create_example` reducer function in Rust, comparing vanilla SpacetimeDB insertion with SpacetimeDSL's `create_entity` method. SpacetimeDSL offers cleaner syntax and handles default values for `id` and `created_at` automatically. ```rust #[spacetimedb::reducer] pub fn create_example(ctx: &spacetimedb::ReducerContext) -> Result<(), String> { // Vanilla SpacetimeDB use spacetimedb::Table; // Without the question mark it would return a // Result> let entity: Entity = ctx.db.entity().try_insert( Entity { id: 0, created_at: ctx.timestamp, } )?; // SpacetimeDB with SpacetimeDSL let dsl: spacetimedsl::DSL<'_, spacetimedb::ReducerContext> = spacetimedsl::dsl(ctx); // Without the question mark it would return a Result let entity: Entity = dsl.create_entity()?; Ok(()) } ``` -------------------------------- ### Update Versions, Commit, Tag, and Push for Release Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/RELEASING.md This snippet details the manual steps required to prepare and initiate a release. It involves updating version numbers in Cargo.toml files, committing these changes, creating a Git tag for the new version, and pushing both the commit and tags to the remote repository. ```bash # 1. Update versions in Cargo.toml files # 2. Commit and tag git add . git commit -m "Release v0.9.1" git tag v0.9.1 git push origin main git push --tags ``` -------------------------------- ### Create SpacetimeDB Records with Smart Defaults Source: https://context7.com/tamaro-skaljic/spacetimedsl/llms.txt Shows how to create new records in a SpacetimeDB table using the generated `create_*` methods provided by SpacetimeDSL. These methods handle smart defaults for fields like auto-increment IDs, timestamps, and ensure referential integrity. ```rust use spacetimedbl::prelude::*; #[dsl(plural_name = players, method(update = true, delete = true))] #[spacetimedb::table(name = player, public)] pub struct Player { #[primary_key] #[auto_inc] #[create_wrapper] id: u128, #[unique] pub name: String, pub score: i32, created_at: Timestamp, modified_at: Option, } #[spacetimedb::reducer] fn create_player_example(ctx: &ReducerContext, name: String) -> Result<(), String> { let dsl = dsl(ctx); // Create struct auto-generated, only non-default fields required let player = dsl.create_player(CreatePlayer { name, score: 0, })?; // created_at automatically set to ctx.timestamp // modified_at automatically set to None // id automatically set by auto_inc log::info!("Created player {} with ID {}", player.get_name(), player.get_id()); Ok(()) } ``` -------------------------------- ### Implement Database Hooks with SpacetimeDSL in Rust Source: https://context7.com/tamaro-skaljic/spacetimedsl/llms.txt Demonstrates how to implement before and after hooks for insert, update, and delete operations on a SpacetimeDB table. Hooks allow custom logic, validation, and data transformation before or after database changes. They can access the DSL for querying and modify requests or new rows. ```rust #[dsl( plural_name = attributes, method(update = true, delete = true), hook( before(insert, update, delete), after(insert, update, delete) ) )] #[spacetimedb::table(name = attribute, public)] pub struct Attribute { #[primary_key] #[auto_inc] #[create_wrapper] id: u128, pub value: String, } // Before insert - can modify the create request #[spacetimedsl::hook] fn before_attribute_insert( dsl: &spacetimedsl::DSL<'_, T>, mut request: CreateAttribute, ) -> Result { // Validate or transform input request.value = request.value.to_uppercase(); // Can access DSL to read other data let count = dsl.count_of_all_attributes(); if count >= 100 { return Err(SpacetimeDSLError::Error("Too many attributes".into())); } Ok(request) } // After insert - row is already in database #[spacetimedsl::hook] fn after_attribute_insert( dsl: &spacetimedsl::DSL<'_, T>, new_row: &Attribute, ) -> Result<(), spacetimedsl::SpacetimeDSLError> { log::info!("Created attribute: {} (ID: {})", new_row.get_value(), new_row.get_id()); Ok(()) } // Before update - can modify the new row #[spacetimedsl::hook] fn before_attribute_update( dsl: &spacetimedsl::DSL<'_, T>, old_row: &Attribute, mut new_row: Attribute, ) -> Result { // Validate changes if new_row.get_value().is_empty() { return Err(SpacetimeDSLError::Error("Value cannot be empty".into())); } // Transform data new_row.set_value(new_row.get_value().to_uppercase()); Ok(new_row) } // After update #[spacetimedsl::hook] fn after_attribute_update( dsl: &spacetimedsl::DSL<'_, T>, old_row: &Attribute, new_row: &Attribute, ) -> Result<(), spacetimedsl::SpacetimeDSLError> { log::info!("Updated '{}' to '{}'", old_row.get_value(), new_row.get_value()); Ok(()) } // Before delete - can prevent deletion #[spacetimedsl::hook] fn before_attribute_delete( dsl: &spacetimedsl::DSL<'_, T>, old_row: &Attribute, ) -> Result<(), spacetimedsl::SpacetimeDSLError> { if old_row.get_value() == "PROTECTED" { return Err(SpacetimeDSLError::Error("Cannot delete protected attribute".into())); } Ok(()) } // After delete #[spacetimedsl::hook] fn after_attribute_delete( dsl: &spacetimedsl::DSL<'_, T>, old_row: &Attribute, ) -> Result<(), spacetimedsl::SpacetimeDSLError> { log::info!("Deleted attribute: {}", old_row.get_value()); Ok(()) } ``` -------------------------------- ### Automated Release Steps via GitHub Actions Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/RELEASING.md This snippet describes the automated processes triggered by GitHub Actions upon a successful release tag. It includes running all tests and, if tests pass, publishing all crates in the correct order. A known issue is noted regarding the verification step not waiting for tests to pass. ```bash # 3. GitHub Actions automatically: # - Runs all tests # - If tests pass, publishes all crates in correct order // FIXME: The Verification doesn't wait on the tests to pass, so it automatically fails. ``` -------------------------------- ### Update Record by ID in Rust Source: https://context7.com/tamaro-skaljic/spacetimedsl/llms.txt Shows how to update a record in a SpacetimeDSL table. It requires the `method(update = true)` attribute and at least one public field or a timestamp column. The example fetches a player, uses generated setters for public fields, and updates the record, returning the modified entity. ```rust #[spacetimedb::reducer] fn update_example(ctx: &ReducerContext, player_id: PlayerId) -> Result<(), String> { let dsl = dsl(ctx); // Fetch the entity let mut player = dsl.get_player_by_id(&player_id)?; // Use generated setters (only for public fields) player.set_name("NewName".to_string()); player.set_score(*player.get_score() + 100); // Update and get the updated entity back let player = dsl.update_player_by_id(player)?; // modified_at automatically updated to ctx.timestamp log::info!("Updated player, modified_at: {:?}", player.get_modified_at()); Ok(()) } ``` -------------------------------- ### Read Records by ID, Unique, Index, and All in Rust Source: https://context7.com/tamaro-skaljic/spacetimedsl/llms.txt Demonstrates reading records from a SpacetimeDSL table using generated methods. It shows fetching by primary key (returning a Result), by unique column, by indexed column (returning an Iterator), and fetching all records. It also includes counting all rows. ```rust #[dsl(plural_name = positions, method(update = true, delete = true))] #[spacetimedb::table(name = position, public)] pub struct Position { #[primary_key] #[auto_inc] #[create_wrapper] id: u128, #[unique] #[use_wrapper(PlayerId)] #[foreign_key(path = crate, table = player, column = id, on_delete = Delete)] player_id: u128, #[index(btree)] pub zone: String, pub x: f32, pub y: f32, } #[spacetimedb::reducer] fn read_examples(ctx: &ReducerContext) -> Result<(), String> { let dsl = dsl(ctx); // Get by primary key (returns Result) let position = dsl.get_position_by_id(&PositionId::new(1))?; // Get by unique column (returns Result) let position = dsl.get_position_by_player_id(&player)?; let position = dsl.get_position_by_player_id(player.get_id())?; // Get by indexed column (returns Iterator) for position in dsl.get_positions_by_zone("spawn_area") { log::info!("Position at ({}, {})", position.get_x(), position.get_y()); } // Get all rows (returns Iterator) let all_positions = dsl.get_all_positions(); // Count all rows let count: u64 = dsl.count_of_all_positions(); Ok(()) } ``` -------------------------------- ### SpacetimeDSL 'plural_name' Attribute Usage (Rust) Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md This Rust code snippet demonstrates the usage of the `#[spacetimedsl::dsl(plural_name = entities)]` attribute in SpacetimeDSL. This attribute is required and is used to define DSL method names for `#[index(btree)]` columns, specifically for 'Get Many' and 'Delete Many' operations. ```rust #[spacetimedsl::dsl(plural_name = entities)] ``` -------------------------------- ### Execute On-Delete Strategies for Multiple Rows (SetZero Strategy) Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md This function executes the 'SetZero' on-delete strategy for referencing tables when multiple rows are deleted from the identifier table. It is called after the 'Delete' strategy has been processed and is part of the overall error handling and result aggregation for deletion operations. ```rust match spacetimedsl::internal::DSLInternals::execute_on_delete_strategies_of_referencing_tables_after_multiple_rows_of_the_identifier_table_were_deleted( ctx, spacetimedsl::OnDeleteStrategy::SetZero, &primary_key_values_of_rows_to_delete[..], ) { Err( ``` -------------------------------- ### Define SpacetimeDB Table with #[dsl] Attribute Source: https://context7.com/tamaro-skaljic/spacetimedsl/llms.txt Illustrates defining a SpacetimeDB table using `#[spacetimedb::table]` and configuring code generation with the `#[spacetimedbl::dsl]` attribute. This includes settings for plural names, method generation (update/delete), unique multi-column indices, and lifecycle hooks. ```rust use spacetimedb::Timestamp; #[spacetimedbl::dsl( plural_name = entities, // Required: used for get_all_entities(), delete_entities_by_* method(update = true, delete = true), // Enable update and delete methods unique_index(name = parent_child_id), // Enforce uniqueness on multi-column index hook( before(insert, update, delete), after(insert, update, delete) ) )] #[spacetimedb::table( name = entity, public, index(name = parent_child_id, btree(columns = [parent_id, child_id])) )] pub struct Entity { #[primary_key] #[auto_inc] #[create_wrapper] // Generate EntityId wrapper type #[referenced_by(path = crate, table = position)] // Track references for cascade id: u128, #[index(btree)] parent_id: u128, #[index(btree)] child_id: u128, pub name: String, // Public = generates setter created_at: Timestamp, // Private = no setter, auto-set on create modified_at: Option, // Auto-updated on update } // Generated methods include: // - dsl.create_entity(CreateEntity { ... }) -> Result // - dsl.get_entity_by_id(&EntityId) -> Result // - dsl.get_all_entities() -> impl Iterator // - dsl.count_of_all_entities() -> u64 // - dsl.update_entity_by_id(Entity) -> Result // - dsl.delete_entity_by_id(&EntityId) -> Result ``` -------------------------------- ### Spacetime DSL Foreign Key Compatibility Requirements Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md Details the compatibility requirements for foreign key on-delete strategies in Spacetime DSL, focusing on table method configurations and column visibility. Incompatible configurations will lead to compilation errors. ```rust // ✅ Valid: table has delete methods enabled #[dsl(plural_name = children, method(update = true, delete = true))] #[table(name = child, public)] pub struct Child { #[primary_key] #[auto_inc] #[create_wrapper] id: u128, #[use_wrapper(ParentId)] #[foreign_key(path = crate, table = parent, on_delete = Delete)] parent_id: u128, // Can use Delete strategy } // ✅ Valid: table has update methods and column is public #[dsl(plural_name = items, method(update = true, delete = false))] #[table(name = item, public)] pub struct Item { #[primary_key] #[auto_inc] #[create_wrapper] id: u128, #[use_wrapper(OwnerId)] #[foreign_key(path = crate, table = owner, on_delete = SetZero)] pub owner_id: u128, // Must be public for SetZero } // ❌ Invalid: delete = false but using Delete strategy #[dsl(plural_name = invalid, method(update = true, delete = false))] pub struct Invalid { #[foreign_key(path = crate, table = parent, on_delete = Delete)] parent_id: u128, // Compile error! } // ❌ Invalid: private column with SetZero strategy #[dsl(plural_name = invalid, method(update = true, delete = true))] pub struct Invalid { #[foreign_key(path = crate, table = owner, on_delete = SetZero)] owner_id: u128, // Compile error! Must be public } ``` -------------------------------- ### Rust Compile-Time Validation for SpacetimeDSL Method Configuration Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Explains the compile-time checks enforced by the SpacetimeDSL macro for method configurations (`update`, `delete`). It details the requirements for enabling updates (public fields or timestamp fields) and deletions (foreign key constraints), preventing runtime errors. ```rust // `update = true` requires: // - At least one non-private field (generates setter), OR // - A `modified_at`/`updated_at` field (auto-updated on changes) // If neither exists → **Compilation error** // `update = false` requires: // - All fields must be private AND // - No `modified_at`/`updated_at` field allowed // If public fields exist → **Compilation error** // `delete = true` required if: // - Any foreign key references this table with `on_delete = Delete` // Otherwise → **Compilation error** // Hooks require matching method: // - `before_update`/`after_update` hooks require `method(update = true)` // - `before_delete`/`after_delete` hooks require `method(delete = true)` // Mismatch → **Compilation error** ``` -------------------------------- ### Implement Hooks for Custom Logic in SpacetimeDSL Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt The hooks system allows executing custom Rust code before or after database operations like insert, update, and delete. Hooks can modify requests or perform validations. They are defined using the `#[spacetimedsl::hook]` attribute and follow a specific naming convention: `{before|after}_{table_name}_{insert|update|delete}`. ```rust #[dsl( plural_name = attributes, method(update = true, delete = true), hook(before(insert, update, delete), after(insert, update, delete)) )] pub struct Attribute { #[primary_key] #[auto_inc] #[create_wrapper] id: u128, pub value: String, } // Hook implementations #[spacetimedsl::hook] fn before_attribute_insert( dsl: &impl DSLContext, mut create_request: CreateAttribute, ) -> Result { // Can mutate the request before insertion create_request.value = format!("{}_ATTRIBUTE", create_request.value); Ok(create_request) // Must return the (potentially modified) request } #[spacetimedsl::hook] fn after_attribute_insert( dsl: &impl DSLContext, new_row: &Attribute, ) -> Result<(), SpacetimeDSLError> { log::info!("Created attribute: {}", new_row.value()); Ok(()) } #[spacetimedsl::hook] fn before_attribute_update( dsl: &impl DSLContext, old_row: &Attribute, mut new_row: Attribute, ) -> Result { // Can mutate new_row before update, old_row is read-only // Validation logic here Ok(new_row) // Must return the (potentially modified) new_row } #[spacetimedsl::hook] fn after_attribute_update( dsl: &impl DSLContext, old_row: &Attribute, new_row: &Attribute, ) -> Result<(), SpacetimeDSLError> { log::info!("Updated attribute from '{}' to '{}'", old_row.value(), new_row.value()); Ok(()) } #[spacetimedsl::hook] fn before_attribute_delete( dsl: &impl DSLContext, old_row: &Attribute, ) -> Result<(), SpacetimeDSLError> { // Pre-deletion validation Ok(()) } #[spacetimedsl::hook] fn after_attribute_delete( dsl: &impl DSLContext, old_row: &Attribute, ) -> Result<(), SpacetimeDSLError> { log::info!("Deleted attribute: {}", old_row.value()); Ok(()) } ``` -------------------------------- ### Define Foreign Keys with SpacetimeDSL in Rust Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Shows how to define foreign key relationships between SpacetimeDB tables using `#[referenced_by]` and `#[foreign_key]` attributes. It includes options for `on_delete` strategies. ```rust // Referenced table #[dsl(plural_name = entities, method(update = true, delete = true))] pub struct Entity { #[primary_key] #[create_wrapper] #[referenced_by(path = crate, table = position)] id: u128, } // Referencing table #[dsl(plural_name = positions, method(update = true, delete = true))] pub struct Position { #[primary_key] #[create_wrapper] id: u128, #[use_wrapper(EntityId)] #[foreign_key(path = crate, table = entity, column = id, on_delete = Delete)] entity_id: u128, ``` -------------------------------- ### Configure SpacetimeDSL Methods (Rust) Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md Demonstrates how to explicitly configure which SpacetimeDSL methods (update, delete) are generated for a given struct. This Rust code uses attributes to control method generation, allowing for fine-grained control over entity mutability. ```rust #[spacetimedsl::dsl( plural_name = entities, method(update = true, delete = false) // Generate update methods, but not delete methods )] #[spacetimedb::table(name = entity, public)] pub struct Entity { // ... fields } ``` -------------------------------- ### Execute On-Delete Strategies for Multiple Rows (Delete Strategy) Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md This function executes the 'Delete' on-delete strategy for referencing tables when multiple rows are deleted from the identifier table. It handles potential errors during strategy execution and aggregates deletion results, including child entries, into a structured format. If errors occur, it returns an Err containing the aggregated deletion entries. ```rust match spacetimedsl::internal::DSLInternals::execute_on_delete_strategies_of_referencing_tables_after_multiple_rows_of_the_identifier_table_were_deleted( ctx, spacetimedsl::OnDeleteStrategy::Delete, &primary_key_values_of_rows_to_delete[..], ) { Err( child_entries_by_primary_key_value_of_a_row_to_delete, ) => { error = true; for ( primary_key_value_of_a_row_to_delete, mut child_entries, ) in child_entries_by_primary_key_value_of_a_row_to_delete { child_entries_by_primary_key_value_of_a_row_to_delete .get_mut(primary_key_value_of_a_row_to_delete) .unwrap() .append(&mut child_entries); } } Ok(child_entries_by_primary_key_value_of_a_row_to_delete) => { for ( primary_key_value_of_a_row_to_delete, mut child_entries, ) in child_entries_by_primary_key_value_of_a_row_to_delete { child_entries_by_primary_key_value_of_a_row_to_delete .get_mut(primary_key_value_of_a_row_to_delete) .unwrap() .append(&mut child_entries); } } }; match error { false => {} true => { for ( primary_key_value_of_a_row_of_another_table_to_delete, primary_key_values_of_rows_to_delete, ) in primary_key_values_of_rows_to_delete_by_primary_key_value_of_a_row_of_another_table_to_delete { for id in &primary_key_values_of_rows_to_delete { let child_entries = child_entries_by_primary_key_value_of_row_to_delete .remove(&id) .unwrap(); entries .get_mut( primary_key_value_of_a_row_of_another_table_to_delete, ) .unwrap() .push(spacetimedsl::DeletionResultEntry { table_name: "identifier".into(), column_name: "entity_id".into(), strategy: spacetimedsl::OnDeleteStrategy::Delete, row_value: format!("{0}", IdentifierId::new(id.clone())).into(), child_entries, }); } } return Err(entries); } }; ``` -------------------------------- ### Execute OnDelete Strategies for IdentifierReference Table (Rust) Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/README.md This Rust code implements on-delete strategies (SetZero, Delete, Ignore) for the 'identifier_reference' table within the SpacetimeDSL. It handles finding related rows, updating or deleting them based on the strategy, and returning deletion results. Dependencies include the spacetimedsl crate and its internal modules. ```rust impl ExecuteOnDeleteStrategiesOfTheIdentifierReferenceTableAfterMultipleRowsOfTheIdentifierTableWereDeleted for spacetimedsl::internal::DSLInternals { fn execute_on_delete_strategies_of_the_identifier_reference_table_after_multiple_rows_of_the_identifier_table_were_deleted( &self, ctx: &spacetimedsl::internal::DSLContext, primary_key_values_of_rows_of_another_table_to_delete: Vec, error: bool, ) -> Result>, std::collections::HashMap>> { let mut entries = std::collections::HashMap::new(); for primary_key_value_of_a_row_of_another_table_to_delete in primary_key_values_of_rows_of_another_table_to_delete { entries.insert(primary_key_value_of_a_row_of_another_table_to_delete.clone(), vec![]); } match spacetimedsl::OnDeleteStrategy::SetZero { spacetimedsl::OnDeleteStrategy::SetZero => { for primary_key_value_of_a_row_of_another_table_to_delete in primary_key_values_of_rows_of_another_table_to_delete { match ctx.db().identifier_reference().id3().find(primary_key_value_of_a_row_of_another_table_to_delete) { None => {} // Do nothing if no row is found Some(row) => { let child_entries = vec![]; let id = &row.id; entries.get_mut(primary_key_value_of_a_row_of_another_table_to_delete).unwrap().push(spacetimedsl::DeletionResultEntry { table_name: "identifier_reference".into(), column_name: "id3".into(), strategy: spacetimedsl::OnDeleteStrategy::SetZero, row_value: format!( "{0}", IdentifierId::new(id.clone())).into(), child_entries, }); ctx.db().identifier_reference().id().update(row); } }; } } spacetimedsl::OnDeleteStrategy::Delete => { for primary_key_value_of_a_row_of_another_table_to_delete in primary_key_values_of_rows_of_another_table_to_delete { match ctx.db().identifier_reference().id2().find(primary_key_value_of_a_row_of_another_table_to_delete) { None => {} // Do nothing if no row is found Some(row) => { let child_entries = vec![]; let id = &row.id; entries.get_mut(primary_key_value_of_a_row_of_another_table_to_delete).unwrap().push(spacetimedsl::DeletionResultEntry { table_name: "identifier_reference".into(), column_name: "id2".into(), strategy: spacetimedsl::OnDeleteStrategy::Delete, row_value: format!( "{0}", IdentifierId::new(id.clone())).into(), child_entries, }); ctx.db().identifier_reference().id().delete(row.id); } }; } } spacetimedsl::OnDeleteStrategy::Ignore => { for primary_key_value_of_a_row_of_another_table_to_delete in primary_key_values_of_rows_of_another_table_to_delete { match ctx.db().identifier_reference().id4().find(primary_key_value_of_a_row_of_another_table_to_delete) { None => {} // Do nothing if no row is found Some(row) => { let child_entries = vec![]; let id = &row.id; entries.get_mut(primary_key_value_of_a_row_of_another_table_to_delete).unwrap().push(spacetimedsl::DeletionResultEntry { table_name: "identifier_reference".into(), column_name: "id4".into(), strategy: spacetimedsl::OnDeleteStrategy::Ignore, row_value: format!( "{0}", IdentifierId::new(id.clone())).into(), child_entries, }); } }; } } }; match error { false => Ok(entries), true => Err(entries), } } } impl ExecuteOnDeleteStrategiesOfTheIdentifierReferenceTableAfterMultipleRowsOfTheIdentifierTableWereDeleted for spacetimedsl::internal::DSLInternals {} ``` -------------------------------- ### Implement Wrapper Types for Type Safety in Rust Source: https://github.com/tamaro-skaljic/spacetimedsl/blob/main/llms.txt Illustrates how to use SpacetimeDSL's `#[create_wrapper]` and `#[use_wrapper]` attributes to enforce type safety for primitive types, preventing common errors related to trait bounds. ```rust #[primary_key] #[create_wrapper] // Default name: EntityId (format: {TableName}{ColumnName}) id: u128, #[create_wrapper(EntityID)] // Custom name: EntityID id: u128, #[use_wrapper(EntityId)] // Uses wrapper from same module entity_id: u128, #[use_wrapper(crate::entity::EntityId)] // Uses wrapper from another module (supports full paths) entity_id: u128, ```