From 1a62fcacddbdbbb3175da5fa8f575fe585560a1c Mon Sep 17 00:00:00 2001 From: Timur Gordon <31495328+timurgordon@users.noreply.github.com> Date: Tue, 5 Aug 2025 12:53:24 +0200 Subject: [PATCH] add heroledger models --- heromodels/docs/payment_usage.md | 318 ----------- .../prompts/v_specs_to_rust_heromodels.md | 301 ++++++++++ heromodels/src/models/heroledger/dnsrecord.rs | 301 ++++++++++ heromodels/src/models/heroledger/group.rs | 236 ++++++++ .../src/models/heroledger/membership.rs | 115 ++++ heromodels/src/models/heroledger/mod.rs | 19 + heromodels/src/models/heroledger/money.rs | 515 ++++++++++++++++++ heromodels/src/models/heroledger/secretbox.rs | 142 +++++ heromodels/src/models/heroledger/signature.rs | 120 ++++ heromodels/src/models/heroledger/user.rs | 370 +++++++++++++ heromodels/src/models/heroledger/user_kvs.rs | 120 ++++ heromodels/src/models/mod.rs | 1 + 12 files changed, 2240 insertions(+), 318 deletions(-) delete mode 100644 heromodels/docs/payment_usage.md create mode 100644 heromodels/docs/prompts/v_specs_to_rust_heromodels.md create mode 100644 heromodels/src/models/heroledger/dnsrecord.rs create mode 100644 heromodels/src/models/heroledger/group.rs create mode 100644 heromodels/src/models/heroledger/membership.rs create mode 100644 heromodels/src/models/heroledger/mod.rs create mode 100644 heromodels/src/models/heroledger/money.rs create mode 100644 heromodels/src/models/heroledger/secretbox.rs create mode 100644 heromodels/src/models/heroledger/signature.rs create mode 100644 heromodels/src/models/heroledger/user.rs create mode 100644 heromodels/src/models/heroledger/user_kvs.rs diff --git a/heromodels/docs/payment_usage.md b/heromodels/docs/payment_usage.md deleted file mode 100644 index 666b448..0000000 --- a/heromodels/docs/payment_usage.md +++ /dev/null @@ -1,318 +0,0 @@ -# Payment Model Usage Guide - -This document provides comprehensive instructions for AI assistants on how to use the Payment model in the heromodels repository. - -## Overview - -The Payment model represents a payment transaction in the system, typically associated with company registration or subscription payments. It integrates with Stripe for payment processing and maintains comprehensive status tracking. - -## Model Structure - -```rust -pub struct Payment { - pub base_data: BaseModelData, // Auto-managed ID, timestamps, comments - pub payment_intent_id: String, // Stripe payment intent ID - pub company_id: u32, // Foreign key to Company - pub payment_plan: String, // "monthly", "yearly", "two_year" - pub setup_fee: f64, // One-time setup fee - pub monthly_fee: f64, // Recurring monthly fee - pub total_amount: f64, // Total amount paid - pub currency: String, // Currency code (defaults to "usd") - pub status: PaymentStatus, // Current payment status - pub stripe_customer_id: Option, // Stripe customer ID (set on completion) - pub created_at: i64, // Payment creation timestamp - pub completed_at: Option, // Payment completion timestamp -} - -pub enum PaymentStatus { - Pending, // Initial state - payment created but not processed - Processing, // Payment is being processed by Stripe - Completed, // Payment successfully completed - Failed, // Payment processing failed - Refunded, // Payment was refunded -} -``` - -## Basic Usage - -### 1. Creating a New Payment - -```rust -use heromodels::models::biz::{Payment, PaymentStatus}; - -// Create a new payment with required fields -let payment = Payment::new( - "pi_1234567890".to_string(), // Stripe payment intent ID - company_id, // Company ID from database - "monthly".to_string(), // Payment plan - 100.0, // Setup fee - 49.99, // Monthly fee - 149.99, // Total amount -); - -// Payment defaults: -// - status: PaymentStatus::Pending -// - currency: "usd" -// - stripe_customer_id: None -// - created_at: current timestamp -// - completed_at: None -``` - -### 2. Using Builder Pattern - -```rust -let payment = Payment::new( - "pi_1234567890".to_string(), - company_id, - "yearly".to_string(), - 500.0, - 99.99, - 1699.88, -) -.currency("eur".to_string()) -.stripe_customer_id(Some("cus_existing_customer".to_string())); -``` - -### 3. Database Operations - -```rust -use heromodels::db::Collection; - -// Save payment to database -let db = get_db()?; -let (payment_id, saved_payment) = db.set(&payment)?; - -// Retrieve payment by ID -let retrieved_payment: Payment = db.get_by_id(payment_id)?.unwrap(); - -// Update payment -let updated_payment = saved_payment.complete_payment(Some("cus_new_customer".to_string())); -let (_, final_payment) = db.set(&updated_payment)?; -``` - -## Payment Status Management - -### Status Transitions - -```rust -// 1. Start with Pending status (default) -let payment = Payment::new(/* ... */); -assert!(payment.is_pending()); - -// 2. Mark as processing when Stripe starts processing -let processing_payment = payment.process_payment(); -assert!(processing_payment.is_processing()); - -// 3. Complete payment when Stripe confirms success -let completed_payment = processing_payment.complete_payment(Some("cus_123".to_string())); -assert!(completed_payment.is_completed()); -assert!(completed_payment.completed_at.is_some()); - -// 4. Handle failure if payment fails -let failed_payment = processing_payment.fail_payment(); -assert!(failed_payment.has_failed()); - -// 5. Refund if needed -let refunded_payment = completed_payment.refund_payment(); -assert!(refunded_payment.is_refunded()); -``` - -### Status Check Methods - -```rust -// Check current status -if payment.is_pending() { - // Show "Payment Pending" UI -} else if payment.is_processing() { - // Show "Processing Payment" UI -} else if payment.is_completed() { - // Show "Payment Successful" UI - // Enable company features -} else if payment.has_failed() { - // Show "Payment Failed" UI - // Offer retry option -} else if payment.is_refunded() { - // Show "Payment Refunded" UI -} -``` - -## Integration with Company Model - -### Complete Payment Flow - -```rust -use heromodels::models::biz::{Company, CompanyStatus, Payment, PaymentStatus}; - -// 1. Create company with pending payment status -let company = Company::new( - "TechStart Inc.".to_string(), - "REG-TS-2024-001".to_string(), - chrono::Utc::now().timestamp(), -) -.email("contact@techstart.com".to_string()) -.status(CompanyStatus::PendingPayment); - -let (company_id, company) = db.set(&company)?; - -// 2. Create payment for the company -let payment = Payment::new( - stripe_payment_intent_id, - company_id, - "yearly".to_string(), - 500.0, // Setup fee - 99.0, // Monthly fee - 1688.0, // Total (setup + 12 months) -); - -let (payment_id, payment) = db.set(&payment)?; - -// 3. Process payment through Stripe -let processing_payment = payment.process_payment(); -let (_, processing_payment) = db.set(&processing_payment)?; - -// 4. On successful Stripe webhook -let completed_payment = processing_payment.complete_payment(Some(stripe_customer_id)); -let (_, completed_payment) = db.set(&completed_payment)?; - -// 5. Activate company -let active_company = company.status(CompanyStatus::Active); -let (_, active_company) = db.set(&active_company)?; -``` - -## Database Indexing - -The Payment model provides custom indexes for efficient querying: - -```rust -// Indexed fields for fast lookups: -// - payment_intent_id: Find payment by Stripe intent ID -// - company_id: Find all payments for a company -// - status: Find payments by status - -// Example queries (conceptual - actual implementation depends on your query layer) -// let pending_payments = db.find_by_index("status", "Pending")?; -// let company_payments = db.find_by_index("company_id", company_id.to_string())?; -// let stripe_payment = db.find_by_index("payment_intent_id", "pi_1234567890")?; -``` - -## Error Handling Best Practices - -```rust -use heromodels::db::DbError; - -fn process_payment_flow(payment_intent_id: String, company_id: u32) -> Result { - let db = get_db()?; - - // Create payment - let payment = Payment::new( - payment_intent_id, - company_id, - "monthly".to_string(), - 100.0, - 49.99, - 149.99, - ); - - // Save to database - let (payment_id, payment) = db.set(&payment)?; - - // Process through Stripe (external API call) - match process_stripe_payment(&payment.payment_intent_id) { - Ok(stripe_customer_id) => { - // Success: complete payment - let completed_payment = payment.complete_payment(Some(stripe_customer_id)); - let (_, final_payment) = db.set(&completed_payment)?; - Ok(final_payment) - } - Err(_) => { - // Failure: mark as failed - let failed_payment = payment.fail_payment(); - let (_, final_payment) = db.set(&failed_payment)?; - Ok(final_payment) - } - } -} -``` - -## Testing - -The Payment model includes comprehensive tests in `tests/payment.rs`. When working with payments: - -1. **Always test status transitions** -2. **Verify timestamp handling** -3. **Test database persistence** -4. **Test integration with Company model** -5. **Test builder pattern methods** - -```bash -# Run payment tests -cargo test payment - -# Run specific test -cargo test test_payment_completion -``` - -## Common Patterns - -### 1. Payment Retry Logic -```rust -fn retry_failed_payment(payment: Payment) -> Payment { - if payment.has_failed() { - // Reset to pending for retry - Payment::new( - payment.payment_intent_id, - payment.company_id, - payment.payment_plan, - payment.setup_fee, - payment.monthly_fee, - payment.total_amount, - ) - .currency(payment.currency) - } else { - payment - } -} -``` - -### 2. Payment Summary -```rust -fn get_payment_summary(payment: &Payment) -> String { - format!( - "Payment {} for company {}: {} {} ({})", - payment.payment_intent_id, - payment.company_id, - payment.total_amount, - payment.currency.to_uppercase(), - payment.status - ) -} -``` - -### 3. Payment Validation -```rust -fn validate_payment(payment: &Payment) -> Result<(), String> { - if payment.total_amount <= 0.0 { - return Err("Total amount must be positive".to_string()); - } - if payment.payment_intent_id.is_empty() { - return Err("Payment intent ID is required".to_string()); - } - if payment.company_id == 0 { - return Err("Valid company ID is required".to_string()); - } - Ok(()) -} -``` - -## Key Points for AI Assistants - -1. **Always use auto-generated IDs** - Don't manually set IDs, let OurDB handle them -2. **Follow status flow** - Pending → Processing → Completed/Failed → (optionally) Refunded -3. **Update timestamps** - `completed_at` is automatically set when calling `complete_payment()` -4. **Use builder pattern** - For optional fields and cleaner code -5. **Test thoroughly** - Payment logic is critical, always verify with tests -6. **Handle errors gracefully** - Payment failures should be tracked, not ignored -7. **Integrate with Company** - Payments typically affect company status -8. **Use proper indexing** - Leverage indexed fields for efficient queries - -This model follows the heromodels patterns and integrates seamlessly with the existing codebase architecture. diff --git a/heromodels/docs/prompts/v_specs_to_rust_heromodels.md b/heromodels/docs/prompts/v_specs_to_rust_heromodels.md new file mode 100644 index 0000000..2f97961 --- /dev/null +++ b/heromodels/docs/prompts/v_specs_to_rust_heromodels.md @@ -0,0 +1,301 @@ +# 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: +```v +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 +```rust +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` | +| `[]u32` | `Vec` | +| `u32` | `u32` | +| `u64` | `u64` | +| `f64` | `f64` | +| `bool` | `bool` | +| `map[string]string` | `std::collections::HashMap` | + +### 5. Struct Declaration Pattern +```rust +/// 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, + pub nested_struct: NestedType, + pub collection: Vec, + pub metadata: std::collections::HashMap, +} +``` + +### 6. Enum Conversion +```rust +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum UserStatus { + Active, + Inactive, + Suspended, +} +``` + +### 7. Fluent Builder Implementation +Every model must implement a fluent builder pattern: + +```rust +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) -> 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 +```rust +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 { + 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: + +```rust +#[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 + +```rust +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` 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.). diff --git a/heromodels/src/models/heroledger/dnsrecord.rs b/heromodels/src/models/heroledger/dnsrecord.rs new file mode 100644 index 0000000..47fc162 --- /dev/null +++ b/heromodels/src/models/heroledger/dnsrecord.rs @@ -0,0 +1,301 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Defines the supported DNS record types +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum NameType { + A, + AAAA, + CNAME, + MX, + TXT, + SRV, + PTR, + NS, +} + +impl Default for NameType { + fn default() -> Self { + NameType::A + } +} + +/// Category of the DNS record +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum NameCat { + IPv4, + IPv6, + Mycelium, +} + +impl Default for NameCat { + fn default() -> Self { + NameCat::IPv4 + } +} + +/// Status of a DNS zone +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum DNSZoneStatus { + Active, + Suspended, + Archived, +} + +impl Default for DNSZoneStatus { + fn default() -> Self { + DNSZoneStatus::Active + } +} + +/// Represents a DNS record configuration +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct DNSRecord { + pub subdomain: String, + pub record_type: NameType, + pub value: String, + pub priority: u32, + pub ttl: u32, + pub is_active: bool, + pub cat: NameCat, + pub is_wildcard: bool, +} + +impl DNSRecord { + pub fn new() -> Self { + Self { + subdomain: String::new(), + record_type: NameType::default(), + value: String::new(), + priority: 0, + ttl: 3600, + is_active: true, + cat: NameCat::default(), + is_wildcard: false, + } + } + + pub fn subdomain(mut self, subdomain: impl ToString) -> Self { + self.subdomain = subdomain.to_string(); + self + } + + pub fn record_type(mut self, record_type: NameType) -> Self { + self.record_type = record_type; + self + } + + pub fn value(mut self, value: impl ToString) -> Self { + self.value = value.to_string(); + self + } + + pub fn priority(mut self, priority: u32) -> Self { + self.priority = priority; + self + } + + pub fn ttl(mut self, ttl: u32) -> Self { + self.ttl = ttl; + self + } + + pub fn is_active(mut self, is_active: bool) -> Self { + self.is_active = is_active; + self + } + + pub fn cat(mut self, cat: NameCat) -> Self { + self.cat = cat; + self + } + + pub fn is_wildcard(mut self, is_wildcard: bool) -> Self { + self.is_wildcard = is_wildcard; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// SOA (Start of Authority) record for a DNS zone +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct SOARecord { + pub zone_id: u32, + pub primary_ns: String, + pub admin_email: String, + pub serial: u64, + pub refresh: u32, + pub retry: u32, + pub expire: u32, + pub minimum_ttl: u32, + pub is_active: bool, +} + +impl SOARecord { + pub fn new() -> Self { + Self { + zone_id: 0, + primary_ns: String::new(), + admin_email: String::new(), + serial: 0, + refresh: 3600, + retry: 600, + expire: 604800, + minimum_ttl: 3600, + is_active: true, + } + } + + pub fn zone_id(mut self, zone_id: u32) -> Self { + self.zone_id = zone_id; + self + } + + pub fn primary_ns(mut self, primary_ns: impl ToString) -> Self { + self.primary_ns = primary_ns.to_string(); + self + } + + pub fn admin_email(mut self, admin_email: impl ToString) -> Self { + self.admin_email = admin_email.to_string(); + self + } + + pub fn serial(mut self, serial: u64) -> Self { + self.serial = serial; + self + } + + pub fn refresh(mut self, refresh: u32) -> Self { + self.refresh = refresh; + self + } + + pub fn retry(mut self, retry: u32) -> Self { + self.retry = retry; + self + } + + pub fn expire(mut self, expire: u32) -> Self { + self.expire = expire; + self + } + + pub fn minimum_ttl(mut self, minimum_ttl: u32) -> Self { + self.minimum_ttl = minimum_ttl; + self + } + + pub fn is_active(mut self, is_active: bool) -> Self { + self.is_active = is_active; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// Represents a DNS zone with its configuration and records +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct DNSZone { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub domain: String, + pub dnsrecords: Vec, + pub administrators: Vec, + pub status: DNSZoneStatus, + pub metadata: HashMap, + pub soarecord: Vec, +} + +impl DNSZone { + /// Create a new DNS zone instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + domain: String::new(), + dnsrecords: Vec::new(), + administrators: Vec::new(), + status: DNSZoneStatus::default(), + metadata: HashMap::new(), + soarecord: Vec::new(), + } + } + + /// Set the domain name (fluent) + pub fn domain(mut self, domain: impl ToString) -> Self { + self.domain = domain.to_string(); + self + } + + /// Add a DNS record (fluent) + pub fn add_dnsrecord(mut self, record: DNSRecord) -> Self { + self.dnsrecords.push(record); + self + } + + /// Set all DNS records (fluent) + pub fn dnsrecords(mut self, dnsrecords: Vec) -> Self { + self.dnsrecords = dnsrecords; + self + } + + /// Add an administrator (fluent) + pub fn add_administrator(mut self, admin_id: u32) -> Self { + self.administrators.push(admin_id); + self + } + + /// Set all administrators (fluent) + pub fn administrators(mut self, administrators: Vec) -> Self { + self.administrators = administrators; + self + } + + /// Set the zone status (fluent) + pub fn status(mut self, status: DNSZoneStatus) -> Self { + self.status = status; + 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 + } + + /// Set all metadata (fluent) + pub fn metadata(mut self, metadata: HashMap) -> Self { + self.metadata = metadata; + self + } + + /// Add an SOA record (fluent) + pub fn add_soarecord(mut self, soa: SOARecord) -> Self { + self.soarecord.push(soa); + self + } + + /// Set all SOA records (fluent) + pub fn soarecord(mut self, soarecord: Vec) -> Self { + self.soarecord = soarecord; + self + } + + /// Build the final DNS zone instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/heroledger/group.rs b/heromodels/src/models/heroledger/group.rs new file mode 100644 index 0000000..a01b98d --- /dev/null +++ b/heromodels/src/models/heroledger/group.rs @@ -0,0 +1,236 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; + +/// Defines the lifecycle of a group +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum GroupStatus { + Active, + Inactive, + Suspended, + Archived, +} + +impl Default for GroupStatus { + fn default() -> Self { + GroupStatus::Active + } +} + +/// Visibility controls who can discover or view the group +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum Visibility { + Public, // Anyone can see and request to join + Private, // Only invited users can see the group + Unlisted, // Not visible in search; only accessible by direct link or DNS +} + +impl Default for Visibility { + fn default() -> Self { + Visibility::Public + } +} + +/// GroupConfig holds rules that govern group membership and behavior +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct GroupConfig { + pub max_members: u32, + pub allow_guests: bool, + pub auto_approve: bool, + pub require_invite: bool, +} + +impl GroupConfig { + pub fn new() -> Self { + Self { + max_members: 0, + allow_guests: false, + auto_approve: false, + require_invite: false, + } + } + + pub fn max_members(mut self, max_members: u32) -> Self { + self.max_members = max_members; + self + } + + pub fn allow_guests(mut self, allow_guests: bool) -> Self { + self.allow_guests = allow_guests; + self + } + + pub fn auto_approve(mut self, auto_approve: bool) -> Self { + self.auto_approve = auto_approve; + self + } + + pub fn require_invite(mut self, require_invite: bool) -> Self { + self.require_invite = require_invite; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// Represents a collaborative or access-controlled unit within the system +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct Group { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub name: String, + pub description: String, + pub dnsrecords: Vec, + pub administrators: Vec, + pub config: GroupConfig, + pub status: GroupStatus, + pub visibility: Visibility, + pub created: u64, + pub updated: u64, +} + +impl Group { + /// Create a new group instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + name: String::new(), + description: String::new(), + dnsrecords: Vec::new(), + administrators: Vec::new(), + config: GroupConfig::new(), + status: GroupStatus::default(), + visibility: Visibility::default(), + created: 0, + updated: 0, + } + } + + /// Set the group name (fluent) + pub fn name(mut self, name: impl ToString) -> Self { + self.name = name.to_string(); + self + } + + /// Set the group description (fluent) + pub fn description(mut self, description: impl ToString) -> Self { + self.description = description.to_string(); + self + } + + /// Add a DNS record ID (fluent) + pub fn add_dnsrecord(mut self, dnsrecord_id: u32) -> Self { + self.dnsrecords.push(dnsrecord_id); + self + } + + /// Set all DNS record IDs (fluent) + pub fn dnsrecords(mut self, dnsrecords: Vec) -> Self { + self.dnsrecords = dnsrecords; + self + } + + /// Add an administrator user ID (fluent) + pub fn add_administrator(mut self, user_id: u32) -> Self { + self.administrators.push(user_id); + self + } + + /// Set all administrator user IDs (fluent) + pub fn administrators(mut self, administrators: Vec) -> Self { + self.administrators = administrators; + self + } + + /// Set the group configuration (fluent) + pub fn config(mut self, config: GroupConfig) -> Self { + self.config = config; + self + } + + /// Set the group status (fluent) + pub fn status(mut self, status: GroupStatus) -> Self { + self.status = status; + self + } + + /// Set the group visibility (fluent) + pub fn visibility(mut self, visibility: Visibility) -> Self { + self.visibility = visibility; + self + } + + /// Set the created timestamp (fluent) + pub fn created(mut self, created: u64) -> Self { + self.created = created; + self + } + + /// Set the updated timestamp (fluent) + pub fn updated(mut self, updated: u64) -> Self { + self.updated = updated; + self + } + + /// Build the final group instance + pub fn build(self) -> Self { + self + } +} + + + +/// Represents the membership relationship between users and groups +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct UserGroupMembership { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub user_id: u32, + pub group_ids: Vec, +} + +impl UserGroupMembership { + /// Create a new user group membership instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + user_id: 0, + group_ids: Vec::new(), + } + } + + /// Set the user ID (fluent) + pub fn user_id(mut self, user_id: u32) -> Self { + self.user_id = user_id; + self + } + + /// Add a group ID (fluent) + pub fn add_group_id(mut self, group_id: u32) -> Self { + self.group_ids.push(group_id); + self + } + + /// Set all group IDs (fluent) + pub fn group_ids(mut self, group_ids: Vec) -> Self { + self.group_ids = group_ids; + self + } + + /// Build the final membership instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/heroledger/membership.rs b/heromodels/src/models/heroledger/membership.rs new file mode 100644 index 0000000..bce12b5 --- /dev/null +++ b/heromodels/src/models/heroledger/membership.rs @@ -0,0 +1,115 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; + +/// Defines the possible roles a member can have +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum MemberRole { + Owner, + Admin, + Moderator, + Member, + Guest, +} + +impl Default for MemberRole { + fn default() -> Self { + MemberRole::Member + } +} + +/// Represents the current status of membership +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum MemberStatus { + Active, + Pending, + Suspended, + Removed, +} + +impl Default for MemberStatus { + fn default() -> Self { + MemberStatus::Pending + } +} + +/// Represents a member within a circle +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct Member { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub user_id: u32, + pub role: MemberRole, + pub status: MemberStatus, + pub joined_at: u64, + pub invited_by: u32, + pub permissions: Vec, +} + +impl Member { + /// Create a new member instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + user_id: 0, + role: MemberRole::default(), + status: MemberStatus::default(), + joined_at: 0, + invited_by: 0, + permissions: Vec::new(), + } + } + + /// Set the user ID (fluent) + pub fn user_id(mut self, user_id: u32) -> Self { + self.user_id = user_id; + self + } + + /// Set the member role (fluent) + pub fn role(mut self, role: MemberRole) -> Self { + self.role = role; + self + } + + /// Set the member status (fluent) + pub fn status(mut self, status: MemberStatus) -> Self { + self.status = status; + self + } + + /// Set the joined timestamp (fluent) + pub fn joined_at(mut self, joined_at: u64) -> Self { + self.joined_at = joined_at; + self + } + + /// Set who invited this member (fluent) + pub fn invited_by(mut self, invited_by: u32) -> Self { + self.invited_by = invited_by; + self + } + + /// Add a permission (fluent) + pub fn add_permission(mut self, permission: impl ToString) -> Self { + self.permissions.push(permission.to_string()); + self + } + + /// Set all permissions (fluent) + pub fn permissions(mut self, permissions: Vec) -> Self { + self.permissions = permissions; + self + } + + /// Build the final member instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/heroledger/mod.rs b/heromodels/src/models/heroledger/mod.rs new file mode 100644 index 0000000..4237870 --- /dev/null +++ b/heromodels/src/models/heroledger/mod.rs @@ -0,0 +1,19 @@ +// Export all heroledger model modules +pub mod user; +pub mod group; +pub mod money; +pub mod membership; +pub mod dnsrecord; +pub mod secretbox; +pub mod signature; +pub mod user_kvs; + +// Re-export key types for convenience +pub use user::{User, UserStatus, UserProfile, KYCInfo, KYCStatus, SecretBox}; +pub use group::{Group, UserGroupMembership, GroupStatus, Visibility, GroupConfig}; +pub use money::{Account, Asset, AccountPolicy, AccountPolicyItem, Transaction, AccountStatus, TransactionType, Signature as TransactionSignature}; +pub use membership::{Member, MemberRole, MemberStatus}; +pub use dnsrecord::{DNSZone, DNSRecord, SOARecord, NameType, NameCat, DNSZoneStatus}; +pub use secretbox::{Notary, NotaryStatus, SecretBoxCategory}; +pub use signature::{Signature, SignatureStatus, ObjectType}; +pub use user_kvs::{UserKVS, UserKVSItem}; diff --git a/heromodels/src/models/heroledger/money.rs b/heromodels/src/models/heroledger/money.rs new file mode 100644 index 0000000..9bcb02d --- /dev/null +++ b/heromodels/src/models/heroledger/money.rs @@ -0,0 +1,515 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Represents the status of an account +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum AccountStatus { + Active, + Inactive, + Suspended, + Archived, +} + +impl Default for AccountStatus { + fn default() -> Self { + AccountStatus::Active + } +} + +/// Represents the type of transaction +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum TransactionType { + Transfer, + Clawback, + Freeze, + Unfreeze, + Issue, + Burn, +} + +impl Default for TransactionType { + fn default() -> Self { + TransactionType::Transfer + } +} + +/// Represents a signature for transactions +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct Signature { + pub signer_id: u32, + pub signature: String, + pub timestamp: u64, +} + +impl Signature { + pub fn new() -> Self { + Self { + signer_id: 0, + signature: String::new(), + timestamp: 0, + } + } + + pub fn signer_id(mut self, signer_id: u32) -> Self { + self.signer_id = signer_id; + self + } + + pub fn signature(mut self, signature: impl ToString) -> Self { + self.signature = signature.to_string(); + self + } + + pub fn timestamp(mut self, timestamp: u64) -> Self { + self.timestamp = timestamp; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// Policy item for account operations +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct AccountPolicyItem { + pub signers: Vec, + pub min_signatures: u32, + pub enabled: bool, + pub threshold: f64, + pub recipient: u32, +} + +impl AccountPolicyItem { + pub fn new() -> Self { + Self { + signers: Vec::new(), + min_signatures: 0, + enabled: false, + threshold: 0.0, + recipient: 0, + } + } + + pub fn add_signer(mut self, signer_id: u32) -> Self { + self.signers.push(signer_id); + self + } + + pub fn signers(mut self, signers: Vec) -> Self { + self.signers = signers; + self + } + + pub fn min_signatures(mut self, min_signatures: u32) -> Self { + self.min_signatures = min_signatures; + self + } + + pub fn enabled(mut self, enabled: bool) -> Self { + self.enabled = enabled; + self + } + + pub fn threshold(mut self, threshold: f64) -> Self { + self.threshold = threshold; + self + } + + pub fn recipient(mut self, recipient: u32) -> Self { + self.recipient = recipient; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// Represents an account in the financial system +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct Account { + /// Base model data + pub base_data: BaseModelData, + pub owner_id: u32, + #[index] + pub address: String, + pub balance: f64, + pub currency: String, + pub assetid: u32, + pub last_activity: u64, + pub administrators: Vec, + pub accountpolicy: u32, +} + +impl Account { + /// Create a new account instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + owner_id: 0, + address: String::new(), + balance: 0.0, + currency: String::new(), + assetid: 0, + last_activity: 0, + administrators: Vec::new(), + accountpolicy: 0, + } + } + + /// Set the owner ID (fluent) + pub fn owner_id(mut self, owner_id: u32) -> Self { + self.owner_id = owner_id; + self + } + + /// Set the blockchain address (fluent) + pub fn address(mut self, address: impl ToString) -> Self { + self.address = address.to_string(); + self + } + + /// Set the balance (fluent) + pub fn balance(mut self, balance: f64) -> Self { + self.balance = balance; + self + } + + /// Set the currency (fluent) + pub fn currency(mut self, currency: impl ToString) -> Self { + self.currency = currency.to_string(); + self + } + + /// Set the asset ID (fluent) + pub fn assetid(mut self, assetid: u32) -> Self { + self.assetid = assetid; + self + } + + /// Set the last activity timestamp (fluent) + pub fn last_activity(mut self, last_activity: u64) -> Self { + self.last_activity = last_activity; + self + } + + /// Add an administrator (fluent) + pub fn add_administrator(mut self, admin_id: u32) -> Self { + self.administrators.push(admin_id); + self + } + + /// Set all administrators (fluent) + pub fn administrators(mut self, administrators: Vec) -> Self { + self.administrators = administrators; + self + } + + /// Set the account policy ID (fluent) + pub fn accountpolicy(mut self, accountpolicy: u32) -> Self { + self.accountpolicy = accountpolicy; + self + } + + /// Build the final account instance + pub fn build(self) -> Self { + self + } +} + + + +/// Represents an asset in the financial system +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct Asset { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub address: String, + pub assetid: u32, + pub asset_type: String, + pub issuer: u32, + pub supply: f64, + pub decimals: u8, + pub is_frozen: bool, + pub metadata: HashMap, + pub administrators: Vec, + pub min_signatures: u32, +} + +impl Asset { + /// Create a new asset instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + address: String::new(), + assetid: 0, + asset_type: String::new(), + issuer: 0, + supply: 0.0, + decimals: 0, + is_frozen: false, + metadata: HashMap::new(), + administrators: Vec::new(), + min_signatures: 0, + } + } + + /// Set the blockchain address (fluent) + pub fn address(mut self, address: impl ToString) -> Self { + self.address = address.to_string(); + self + } + + /// Set the asset ID (fluent) + pub fn assetid(mut self, assetid: u32) -> Self { + self.assetid = assetid; + self + } + + /// Set the asset type (fluent) + pub fn asset_type(mut self, asset_type: impl ToString) -> Self { + self.asset_type = asset_type.to_string(); + self + } + + /// Set the issuer (fluent) + pub fn issuer(mut self, issuer: u32) -> Self { + self.issuer = issuer; + self + } + + /// Set the supply (fluent) + pub fn supply(mut self, supply: f64) -> Self { + self.supply = supply; + self + } + + /// Set the decimals (fluent) + pub fn decimals(mut self, decimals: u8) -> Self { + self.decimals = decimals; + self + } + + /// Set the frozen status (fluent) + pub fn is_frozen(mut self, is_frozen: bool) -> Self { + self.is_frozen = is_frozen; + 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 + } + + /// Set all metadata (fluent) + pub fn metadata(mut self, metadata: HashMap) -> Self { + self.metadata = metadata; + self + } + + /// Add an administrator (fluent) + pub fn add_administrator(mut self, admin_id: u32) -> Self { + self.administrators.push(admin_id); + self + } + + /// Set all administrators (fluent) + pub fn administrators(mut self, administrators: Vec) -> Self { + self.administrators = administrators; + self + } + + /// Set minimum signatures required (fluent) + pub fn min_signatures(mut self, min_signatures: u32) -> Self { + self.min_signatures = min_signatures; + self + } + + /// Build the final asset instance + pub fn build(self) -> Self { + self + } +} + + + +/// Represents account policies for various operations +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct AccountPolicy { + /// Base model data + pub base_data: BaseModelData, + pub transferpolicy: AccountPolicyItem, + pub adminpolicy: AccountPolicyItem, + pub clawbackpolicy: AccountPolicyItem, + pub freezepolicy: AccountPolicyItem, +} + +impl AccountPolicy { + /// Create a new account policy instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + transferpolicy: AccountPolicyItem::new(), + adminpolicy: AccountPolicyItem::new(), + clawbackpolicy: AccountPolicyItem::new(), + freezepolicy: AccountPolicyItem::new(), + } + } + + /// Set the transfer policy (fluent) + pub fn transferpolicy(mut self, transferpolicy: AccountPolicyItem) -> Self { + self.transferpolicy = transferpolicy; + self + } + + /// Set the admin policy (fluent) + pub fn adminpolicy(mut self, adminpolicy: AccountPolicyItem) -> Self { + self.adminpolicy = adminpolicy; + self + } + + /// Set the clawback policy (fluent) + pub fn clawbackpolicy(mut self, clawbackpolicy: AccountPolicyItem) -> Self { + self.clawbackpolicy = clawbackpolicy; + self + } + + /// Set the freeze policy (fluent) + pub fn freezepolicy(mut self, freezepolicy: AccountPolicyItem) -> Self { + self.freezepolicy = freezepolicy; + self + } + + /// Build the final account policy instance + pub fn build(self) -> Self { + self + } +} + + + +/// Represents a financial transaction +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct Transaction { + /// Base model data + pub base_data: BaseModelData, + pub txid: u32, + pub source: u32, + pub destination: u32, + pub assetid: u32, + pub amount: f64, + pub timestamp: u64, + pub status: String, + pub memo: String, + pub tx_type: TransactionType, + pub signatures: Vec, +} + +impl Transaction { + /// Create a new transaction instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + txid: 0, + source: 0, + destination: 0, + assetid: 0, + amount: 0.0, + timestamp: 0, + status: String::new(), + memo: String::new(), + tx_type: TransactionType::default(), + signatures: Vec::new(), + } + } + + /// Set the transaction ID (fluent) + pub fn txid(mut self, txid: u32) -> Self { + self.txid = txid; + self + } + + /// Set the source account (fluent) + pub fn source(mut self, source: u32) -> Self { + self.source = source; + self + } + + /// Set the destination account (fluent) + pub fn destination(mut self, destination: u32) -> Self { + self.destination = destination; + self + } + + /// Set the asset ID (fluent) + pub fn assetid(mut self, assetid: u32) -> Self { + self.assetid = assetid; + self + } + + /// Set the amount (fluent) + pub fn amount(mut self, amount: f64) -> Self { + self.amount = amount; + self + } + + /// Set the timestamp (fluent) + pub fn timestamp(mut self, timestamp: u64) -> Self { + self.timestamp = timestamp; + self + } + + /// Set the status (fluent) + pub fn status(mut self, status: impl ToString) -> Self { + self.status = status.to_string(); + self + } + + /// Set the memo (fluent) + pub fn memo(mut self, memo: impl ToString) -> Self { + self.memo = memo.to_string(); + self + } + + /// Set the transaction type (fluent) + pub fn tx_type(mut self, tx_type: TransactionType) -> Self { + self.tx_type = tx_type; + self + } + + /// Add a signature (fluent) + pub fn add_signature(mut self, signature: Signature) -> Self { + self.signatures.push(signature); + self + } + + /// Set all signatures (fluent) + pub fn signatures(mut self, signatures: Vec) -> Self { + self.signatures = signatures; + self + } + + /// Build the final transaction instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/heroledger/secretbox.rs b/heromodels/src/models/heroledger/secretbox.rs new file mode 100644 index 0000000..4f93e39 --- /dev/null +++ b/heromodels/src/models/heroledger/secretbox.rs @@ -0,0 +1,142 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; + +/// Category of the secret box +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum SecretBoxCategory { + Profile, +} + +impl Default for SecretBoxCategory { + fn default() -> Self { + SecretBoxCategory::Profile + } +} + +/// Status of a notary +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum NotaryStatus { + Active, + Inactive, + Suspended, + Archived, + Error, +} + +impl Default for NotaryStatus { + fn default() -> Self { + NotaryStatus::Active + } +} + +/// Represents an encrypted secret box for storing sensitive data +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct SecretBox { + pub notary_id: u32, + pub value: String, + pub version: u16, + pub timestamp: u64, + pub cat: SecretBoxCategory, +} + +impl SecretBox { + pub fn new() -> Self { + Self { + notary_id: 0, + value: String::new(), + version: 1, + timestamp: 0, + cat: SecretBoxCategory::default(), + } + } + + pub fn notary_id(mut self, notary_id: u32) -> Self { + self.notary_id = notary_id; + self + } + + pub fn value(mut self, value: impl ToString) -> Self { + self.value = value.to_string(); + self + } + + pub fn version(mut self, version: u16) -> Self { + self.version = version; + self + } + + pub fn timestamp(mut self, timestamp: u64) -> Self { + self.timestamp = timestamp; + self + } + + pub fn cat(mut self, cat: SecretBoxCategory) -> Self { + self.cat = cat; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// Represents a notary who can decrypt secret boxes +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct Notary { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub userid: u32, + pub status: NotaryStatus, + pub myceliumaddress: String, + #[index] + pub pubkey: String, +} + +impl Notary { + /// Create a new notary instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + userid: 0, + status: NotaryStatus::default(), + myceliumaddress: String::new(), + pubkey: String::new(), + } + } + + /// Set the user ID (fluent) + pub fn userid(mut self, userid: u32) -> Self { + self.userid = userid; + self + } + + /// Set the notary status (fluent) + pub fn status(mut self, status: NotaryStatus) -> Self { + self.status = status; + self + } + + /// Set the mycelium address (fluent) + pub fn myceliumaddress(mut self, myceliumaddress: impl ToString) -> Self { + self.myceliumaddress = myceliumaddress.to_string(); + self + } + + /// Set the public key (fluent) + pub fn pubkey(mut self, pubkey: impl ToString) -> Self { + self.pubkey = pubkey.to_string(); + self + } + + /// Build the final notary instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/heroledger/signature.rs b/heromodels/src/models/heroledger/signature.rs new file mode 100644 index 0000000..c97cf89 --- /dev/null +++ b/heromodels/src/models/heroledger/signature.rs @@ -0,0 +1,120 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; + +/// Status of a signature +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum SignatureStatus { + Active, + Inactive, + Pending, + Revoked, +} + +impl Default for SignatureStatus { + fn default() -> Self { + SignatureStatus::Pending + } +} + +/// Type of object being signed +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum ObjectType { + Account, + DNSRecord, + Membership, + User, + Transaction, + KYC, +} + +impl Default for ObjectType { + fn default() -> Self { + ObjectType::User + } +} + +/// Represents a cryptographic signature for various objects +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct Signature { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub signature_id: u32, + #[index] + pub user_id: u32, + pub value: String, + #[index] + pub objectid: u32, + pub objecttype: ObjectType, + pub status: SignatureStatus, + pub timestamp: u64, +} + +impl Signature { + /// Create a new signature instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + signature_id: 0, + user_id: 0, + value: String::new(), + objectid: 0, + objecttype: ObjectType::default(), + status: SignatureStatus::default(), + timestamp: 0, + } + } + + /// Set the signature ID (fluent) + pub fn signature_id(mut self, signature_id: u32) -> Self { + self.signature_id = signature_id; + self + } + + /// Set the user ID (fluent) + pub fn user_id(mut self, user_id: u32) -> Self { + self.user_id = user_id; + self + } + + /// Set the signature value (fluent) + pub fn value(mut self, value: impl ToString) -> Self { + self.value = value.to_string(); + self + } + + /// Set the object ID (fluent) + pub fn objectid(mut self, objectid: u32) -> Self { + self.objectid = objectid; + self + } + + /// Set the object type (fluent) + pub fn objecttype(mut self, objecttype: ObjectType) -> Self { + self.objecttype = objecttype; + self + } + + /// Set the signature status (fluent) + pub fn status(mut self, status: SignatureStatus) -> Self { + self.status = status; + self + } + + /// Set the timestamp (fluent) + pub fn timestamp(mut self, timestamp: u64) -> Self { + self.timestamp = timestamp; + self + } + + /// Build the final signature instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/heroledger/user.rs b/heromodels/src/models/heroledger/user.rs new file mode 100644 index 0000000..b9d34d3 --- /dev/null +++ b/heromodels/src/models/heroledger/user.rs @@ -0,0 +1,370 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Represents the status of a user in the system +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum UserStatus { + Active, + Inactive, + Suspended, + Archived, +} + +impl Default for UserStatus { + fn default() -> Self { + UserStatus::Active + } +} + +/// Represents the KYC status of a user +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum KYCStatus { + Pending, + Approved, + Rejected, +} + +impl Default for KYCStatus { + fn default() -> Self { + KYCStatus::Pending + } +} + +/// User profile information +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct UserProfile { + pub user_id: u32, + pub full_name: String, + pub bio: String, + pub profile_pic: String, + pub links: HashMap, + pub metadata: HashMap, +} + +impl UserProfile { + pub fn new() -> Self { + Self { + user_id: 0, + full_name: String::new(), + bio: String::new(), + profile_pic: String::new(), + links: HashMap::new(), + metadata: HashMap::new(), + } + } + + pub fn user_id(mut self, user_id: u32) -> Self { + self.user_id = user_id; + self + } + + pub fn full_name(mut self, full_name: impl ToString) -> Self { + self.full_name = full_name.to_string(); + self + } + + pub fn bio(mut self, bio: impl ToString) -> Self { + self.bio = bio.to_string(); + self + } + + pub fn profile_pic(mut self, profile_pic: impl ToString) -> Self { + self.profile_pic = profile_pic.to_string(); + self + } + + pub fn add_link(mut self, key: impl ToString, value: impl ToString) -> Self { + self.links.insert(key.to_string(), value.to_string()); + self + } + + pub fn links(mut self, links: HashMap) -> Self { + self.links = links; + self + } + + pub fn add_metadata(mut self, key: impl ToString, value: impl ToString) -> Self { + self.metadata.insert(key.to_string(), value.to_string()); + self + } + + pub fn metadata(mut self, metadata: HashMap) -> Self { + self.metadata = metadata; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// KYC (Know Your Customer) information for a user +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct KYCInfo { + pub user_id: u32, + pub full_name: String, + pub date_of_birth: u64, + pub address: String, + pub phone_number: String, + pub id_number: String, + pub id_type: String, + pub id_expiry: u64, + pub kyc_status: KYCStatus, + pub kyc_verified: bool, + pub kyc_verified_by: u32, + pub kyc_verified_at: u64, + pub kyc_rejected_reason: String, + pub kyc_signature: u32, + pub metadata: HashMap, +} + +impl KYCInfo { + pub fn new() -> Self { + Self { + user_id: 0, + full_name: String::new(), + date_of_birth: 0, + address: String::new(), + phone_number: String::new(), + id_number: String::new(), + id_type: String::new(), + id_expiry: 0, + kyc_status: KYCStatus::default(), + kyc_verified: false, + kyc_verified_by: 0, + kyc_verified_at: 0, + kyc_rejected_reason: String::new(), + kyc_signature: 0, + metadata: HashMap::new(), + } + } + + pub fn user_id(mut self, user_id: u32) -> Self { + self.user_id = user_id; + self + } + + pub fn full_name(mut self, full_name: impl ToString) -> Self { + self.full_name = full_name.to_string(); + self + } + + pub fn date_of_birth(mut self, date_of_birth: u64) -> Self { + self.date_of_birth = date_of_birth; + self + } + + pub fn address(mut self, address: impl ToString) -> Self { + self.address = address.to_string(); + self + } + + pub fn phone_number(mut self, phone_number: impl ToString) -> Self { + self.phone_number = phone_number.to_string(); + self + } + + pub fn id_number(mut self, id_number: impl ToString) -> Self { + self.id_number = id_number.to_string(); + self + } + + pub fn id_type(mut self, id_type: impl ToString) -> Self { + self.id_type = id_type.to_string(); + self + } + + pub fn id_expiry(mut self, id_expiry: u64) -> Self { + self.id_expiry = id_expiry; + self + } + + pub fn kyc_status(mut self, kyc_status: KYCStatus) -> Self { + self.kyc_status = kyc_status; + self + } + + pub fn kyc_verified(mut self, kyc_verified: bool) -> Self { + self.kyc_verified = kyc_verified; + self + } + + pub fn kyc_verified_by(mut self, kyc_verified_by: u32) -> Self { + self.kyc_verified_by = kyc_verified_by; + self + } + + pub fn kyc_verified_at(mut self, kyc_verified_at: u64) -> Self { + self.kyc_verified_at = kyc_verified_at; + self + } + + pub fn kyc_rejected_reason(mut self, kyc_rejected_reason: impl ToString) -> Self { + self.kyc_rejected_reason = kyc_rejected_reason.to_string(); + self + } + + pub fn kyc_signature(mut self, kyc_signature: u32) -> Self { + self.kyc_signature = kyc_signature; + self + } + + pub fn add_metadata(mut self, key: impl ToString, value: impl ToString) -> Self { + self.metadata.insert(key.to_string(), value.to_string()); + self + } + + pub fn metadata(mut self, metadata: HashMap) -> Self { + self.metadata = metadata; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// Represents a secret box for storing encrypted data +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct SecretBox { + pub data: Vec, + pub nonce: Vec, +} + +impl SecretBox { + pub fn new() -> Self { + Self { + data: Vec::new(), + nonce: Vec::new(), + } + } + + pub fn data(mut self, data: Vec) -> Self { + self.data = data; + self + } + + pub fn nonce(mut self, nonce: Vec) -> Self { + self.nonce = nonce; + self + } + + pub fn build(self) -> Self { + self + } +} + +/// Represents a user in the heroledger system +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct User { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub username: String, + #[index] + pub pubkey: String, + pub email: Vec, + pub status: UserStatus, + pub userprofile: Vec, + pub kyc: Vec, +} + +impl Default for User { + fn default() -> Self { + Self { + base_data: BaseModelData::new(), + username: String::new(), + pubkey: String::new(), + email: Vec::new(), + status: UserStatus::default(), + userprofile: Vec::new(), + kyc: Vec::new(), + } + } +} + +impl User { + /// Create a new user instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + username: String::new(), + pubkey: String::new(), + email: Vec::new(), + status: UserStatus::default(), + userprofile: Vec::new(), + kyc: Vec::new(), + } + } + + /// Get the user ID + pub fn id(&self) -> u32 { + self.base_data.id + } + + /// Set the username (fluent) + pub fn username(mut self, username: impl ToString) -> Self { + self.username = username.to_string(); + self + } + + /// Set the public key (fluent) + pub fn pubkey(mut self, pubkey: impl ToString) -> Self { + self.pubkey = pubkey.to_string(); + self + } + + /// Add an email address (fluent) + pub fn add_email(mut self, email: impl ToString) -> Self { + self.email.push(email.to_string()); + self + } + + /// Set all email addresses (fluent) + pub fn email(mut self, email: Vec) -> Self { + self.email = email; + self + } + + /// Set the user status (fluent) + pub fn status(mut self, status: UserStatus) -> Self { + self.status = status; + self + } + + /// Add a user profile secret box (fluent) + pub fn add_userprofile(mut self, profile: SecretBox) -> Self { + self.userprofile.push(profile); + self + } + + /// Set all user profile secret boxes (fluent) + pub fn userprofile(mut self, userprofile: Vec) -> Self { + self.userprofile = userprofile; + self + } + + /// Add a KYC secret box (fluent) + pub fn add_kyc(mut self, kyc: SecretBox) -> Self { + self.kyc.push(kyc); + self + } + + /// Set all KYC secret boxes (fluent) + pub fn kyc(mut self, kyc: Vec) -> Self { + self.kyc = kyc; + self + } + + /// Build the final user instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/heroledger/user_kvs.rs b/heromodels/src/models/heroledger/user_kvs.rs new file mode 100644 index 0000000..da64ecf --- /dev/null +++ b/heromodels/src/models/heroledger/user_kvs.rs @@ -0,0 +1,120 @@ +use heromodels_core::{Model, BaseModelData, IndexKey}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; +use super::secretbox::SecretBox; + +/// Represents a per-user key-value store +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct UserKVS { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub userid: u32, + pub name: String, +} + +impl UserKVS { + /// Create a new user KVS instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + userid: 0, + name: String::new(), + } + } + + /// Set the user ID (fluent) + pub fn userid(mut self, userid: u32) -> Self { + self.userid = userid; + self + } + + /// Set the KVS name (fluent) + pub fn name(mut self, name: impl ToString) -> Self { + self.name = name.to_string(); + self + } + + /// Build the final user KVS instance + pub fn build(self) -> Self { + self + } +} + + + +/// Represents an item in a user's key-value store +#[model] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct UserKVSItem { + /// Base model data + pub base_data: BaseModelData, + #[index] + pub userkvs_id: u32, + pub key: String, + pub value: String, + pub secretbox: Vec, + pub timestamp: u64, +} + +impl UserKVSItem { + /// Create a new user KVS item instance + pub fn new(id: u32) -> Self { + let mut base_data = BaseModelData::new(); + base_data.update_id(id); + Self { + base_data, + userkvs_id: 0, + key: String::new(), + value: String::new(), + secretbox: Vec::new(), + timestamp: 0, + } + } + + /// Set the user KVS ID (fluent) + pub fn userkvs_id(mut self, userkvs_id: u32) -> Self { + self.userkvs_id = userkvs_id; + self + } + + /// Set the key (fluent) + pub fn key(mut self, key: impl ToString) -> Self { + self.key = key.to_string(); + self + } + + /// Set the value (fluent) + pub fn value(mut self, value: impl ToString) -> Self { + self.value = value.to_string(); + self + } + + /// Add a secret box (fluent) + pub fn add_secretbox(mut self, secretbox: SecretBox) -> Self { + self.secretbox.push(secretbox); + self + } + + /// Set all secret boxes (fluent) + pub fn secretbox(mut self, secretbox: Vec) -> Self { + self.secretbox = secretbox; + self + } + + /// Set the timestamp (fluent) + pub fn timestamp(mut self, timestamp: u64) -> Self { + self.timestamp = timestamp; + self + } + + /// Build the final user KVS item instance + pub fn build(self) -> Self { + self + } +} + + diff --git a/heromodels/src/models/mod.rs b/heromodels/src/models/mod.rs index 305777b..b1cd40f 100644 --- a/heromodels/src/models/mod.rs +++ b/heromodels/src/models/mod.rs @@ -10,6 +10,7 @@ pub mod contact; pub mod finance; pub mod flow; pub mod governance; +pub mod heroledger; pub mod legal; pub mod library; pub mod object;