db/heromodels/docs/prompts/v_specs_to_rust_heromodels.md
2025-08-05 12:53:24 +02:00

8.1 KiB

AI Prompt: Convert V Language Specs to Rust Hero Models

Objective

Convert V language model specifications (.v files) to Rust hero models that integrate with the heromodels framework. The generated Rust models should follow the established patterns for base data embedding, indexing, fluent builder APIs, and Rhai scripting integration.

V Language Input Structure Analysis

V Spec Patterns to Recognize:

  1. Module Declaration: module circle or module group
  2. Base Embedding: core.Base - represents the base model data
  3. Index Fields: Fields marked with @[index] comments
  4. Mutability: Fields declared with pub mut:
  5. Enums: pub enum Status { active, inactive, suspended }
  6. Nested Structs: Embedded configuration or related data structures
  7. Collections: []u32, []string, map[string]string
  8. References: u32 fields typically represent foreign key references

Example V Spec Structure:

module circle

import freeflowuniverse.herolib.hero.models.core

pub struct User {
    core.Base
pub mut:
    username     string       @[index] // Unique username
    email        []string     @[index] // Multiple email addresses
    status       UserStatus   // Enum reference
    profile      UserProfile  // Nested struct
    metadata     map[string]string // Key-value pairs
}

pub enum UserStatus {
    active
    inactive
    suspended
}

pub struct UserProfile {
pub mut:
    full_name    string
    bio          string
    links        map[string]string
}

Rust Hero Model Conversion Rules

1. File Structure and Imports

use heromodels_core::{Model, BaseModelData, IndexKey};
use heromodels_derive::model;
use rhai::CustomType;
use rhailib_derive::RhaiApi;
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};

2. Base Data Embedding

  • V: core.Base
  • Rust: pub base_data: BaseModelData,

3. Index Field Conversion

  • V: field_name string @[index]
  • Rust: #[index] pub field_name: String,

4. Type Mappings

V Type Rust Type
string String
[]string Vec<String>
[]u32 Vec<u32>
u32 u32
u64 u64
f64 f64
bool bool
map[string]string std::collections::HashMap<String, String>

5. Struct Declaration Pattern

/// Documentation comment describing the model
#[model]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default, RhaiApi)]
pub struct ModelName {
    /// Base model data
    pub base_data: BaseModelData,
    #[index]
    pub indexed_field: String,
    pub regular_field: String,
    pub optional_field: Option<String>,
    pub nested_struct: NestedType,
    pub collection: Vec<u32>,
    pub metadata: std::collections::HashMap<String, String>,
}

6. Enum Conversion

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum UserStatus {
    Active,
    Inactive,
    Suspended,
}

7. Fluent Builder Implementation

Every model must implement a fluent builder pattern:

impl ModelName {
    /// Create a new instance
    pub fn new(id: u32) -> Self {
        Self {
            base_data: BaseModelData::new(id),
            indexed_field: String::new(),
            regular_field: String::new(),
            optional_field: None,
            nested_struct: NestedType::new(),
            collection: Vec::new(),
            metadata: std::collections::HashMap::new(),
        }
    }

    /// Set indexed field (fluent)
    pub fn indexed_field(mut self, value: impl ToString) -> Self {
        self.indexed_field = value.to_string();
        self
    }

    /// Set regular field (fluent)
    pub fn regular_field(mut self, value: impl ToString) -> Self {
        self.regular_field = value.to_string();
        self
    }

    /// Set optional field (fluent)
    pub fn optional_field(mut self, value: impl ToString) -> Self {
        self.optional_field = Some(value.to_string());
        self
    }

    /// Set nested struct (fluent)
    pub fn nested_struct(mut self, value: NestedType) -> Self {
        self.nested_struct = value;
        self
    }

    /// Add to collection (fluent)
    pub fn add_to_collection(mut self, value: u32) -> Self {
        self.collection.push(value);
        self
    }

    /// Set entire collection (fluent)
    pub fn collection(mut self, value: Vec<u32>) -> Self {
        self.collection = value;
        self
    }

    /// Add metadata entry (fluent)
    pub fn add_metadata(mut self, key: impl ToString, value: impl ToString) -> Self {
        self.metadata.insert(key.to_string(), value.to_string());
        self
    }

    /// Build the final instance
    pub fn build(self) -> Self {
        self
    }
}

8. Model Trait Implementation

impl Model for ModelName {
    fn db_prefix() -> &'static str {
        "modelname"
    }

    fn get_id(&self) -> u32 {
        self.base_data.id
    }

    fn base_data_mut(&mut self) -> &mut BaseModelData {
        &mut self.base_data
    }

    fn db_keys(&self) -> Vec<IndexKey> {
        let mut keys = Vec::new();
        
        // Add index keys for fields marked with #[index]
        keys.push(IndexKey::new("indexed_field", &self.indexed_field));
        
        // Add additional index keys as needed
        keys
    }
}

9. Nested Struct Builder Pattern

For embedded types, implement similar builder patterns:

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct NestedType {
    pub field1: String,
    pub field2: String,
}

impl NestedType {
    pub fn new() -> Self {
        Self {
            field1: String::new(),
            field2: String::new(),
        }
    }

    pub fn field1(mut self, value: impl ToString) -> Self {
        self.field1 = value.to_string();
        self
    }

    pub fn field2(mut self, value: impl ToString) -> Self {
        self.field2 = value.to_string();
        self
    }

    pub fn build(self) -> Self {
        self
    }
}

Conversion Steps

  1. Analyze V Spec Structure

    • Identify the module name and main structs
    • Note which fields are marked with @[index]
    • Identify nested structs and enums
    • Map field types from V to Rust
  2. Create Rust File Structure

    • Add appropriate imports
    • Convert enums first (they're often referenced by structs)
    • Convert nested structs before main structs
  3. Implement Main Struct

    • Add #[model] macro and derives
    • Embed BaseModelData as base_data
    • Mark indexed fields with #[index]
    • Convert field types according to mapping table
  4. Implement Builder Pattern

    • Add new(id: u32) constructor
    • Add fluent setter methods for each field
    • Handle optional fields appropriately
    • Add collection manipulation methods
  5. Implement Model Trait

    • Define appropriate db_prefix
    • Implement required trait methods
    • Add index keys for searchable fields
  6. Add Documentation

    • Document the struct and its purpose
    • Document each field's meaning
    • Add usage examples in comments

Example Usage After Conversion

let user = User::new(1)
    .username("john_doe")
    .add_email("john@example.com")
    .add_email("john.doe@company.com")
    .status(UserStatus::Active)
    .profile(
        UserProfile::new()
            .full_name("John Doe")
            .bio("Software developer")
            .build()
    )
    .add_metadata("department", "engineering")
    .build();

Notes and Best Practices

  1. Field Naming: Convert V snake_case to Rust snake_case (usually no change needed)
  2. Optional Fields: Use Option<T> for fields that may be empty in V
  3. Collections: Always provide both add_item and set_collection methods
  4. Error Handling: Builder methods should not panic; use appropriate defaults
  5. Documentation: Include comprehensive documentation for public APIs
  6. Testing: Consider adding unit tests for builder patterns
  7. Validation: Add validation logic in builder methods if needed

File Organization

Place the converted Rust models in the appropriate subdirectory under heromodels/src/models/ based on the domain (e.g., user/, finance/, governance/, etc.).