...
This commit is contained in:
		@@ -1,91 +0,0 @@
 | 
			
		||||
# HeroDB Architecture
 | 
			
		||||
 | 
			
		||||
This document explains the architecture of HeroDB, focusing on the separation between model definitions and database logic.
 | 
			
		||||
 | 
			
		||||
## Core Principles
 | 
			
		||||
 | 
			
		||||
1. **Separation of Concerns**: The DB core should not know about specific models
 | 
			
		||||
2. **Registration-Based System**: Models get registered with the DB through a factory pattern
 | 
			
		||||
3. **Type-Safety**: Despite the separation, we maintain full type safety
 | 
			
		||||
 | 
			
		||||
## Components
 | 
			
		||||
 | 
			
		||||
### Core Module
 | 
			
		||||
 | 
			
		||||
The `core` module provides the database foundation without knowing about specific models:
 | 
			
		||||
 | 
			
		||||
- `SledModel` trait: Defines the interface models must implement
 | 
			
		||||
- `Storable` trait: Provides serialization/deserialization capabilities
 | 
			
		||||
- `SledDB<T>`: Generic database wrapper for any model type
 | 
			
		||||
- `DB`: Main database manager that holds registered models
 | 
			
		||||
- `DBBuilder`: Builder for creating a DB with registered models
 | 
			
		||||
 | 
			
		||||
### Zaz Module
 | 
			
		||||
 | 
			
		||||
The `zaz` module contains domain-specific models and factories:
 | 
			
		||||
 | 
			
		||||
- `models`: Defines specific model types like User, Company, etc.
 | 
			
		||||
- `factory`: Provides functions to create a DB with zaz models registered
 | 
			
		||||
 | 
			
		||||
## Using the DB
 | 
			
		||||
 | 
			
		||||
### Option 1: Factory Function
 | 
			
		||||
 | 
			
		||||
The easiest way to create a DB with all zaz models is to use the factory:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use herodb::zaz::create_zaz_db;
 | 
			
		||||
 | 
			
		||||
// Create a DB with all zaz models registered
 | 
			
		||||
let db = create_zaz_db("/path/to/db")?;
 | 
			
		||||
 | 
			
		||||
// Use the DB with specific model types
 | 
			
		||||
let user = User::new(...);
 | 
			
		||||
db.set(&user)?;
 | 
			
		||||
let retrieved: User = db.get(&id)?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Option 2: Builder Pattern
 | 
			
		||||
 | 
			
		||||
For more control, use the builder pattern to register only the models you need:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use herodb::core::{DBBuilder, DB};
 | 
			
		||||
use herodb::zaz::models::{User, Company};
 | 
			
		||||
 | 
			
		||||
// Create a DB with only User and Company models
 | 
			
		||||
let db = DBBuilder::new("/path/to/db")
 | 
			
		||||
    .register_model::<User>()
 | 
			
		||||
    .register_model::<Company>()
 | 
			
		||||
    .build()?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Option 3: Dynamic Registration
 | 
			
		||||
 | 
			
		||||
You can also register models with an existing DB:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use herodb::core::DB;
 | 
			
		||||
use herodb::zaz::models::User;
 | 
			
		||||
 | 
			
		||||
// Create an empty DB
 | 
			
		||||
let mut db = DB::new("/path/to/db")?;
 | 
			
		||||
 | 
			
		||||
// Register the User model
 | 
			
		||||
db.register::<User>()?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Benefits of this Architecture
 | 
			
		||||
 | 
			
		||||
1. **Modularity**: The core DB code doesn't need to change when models change
 | 
			
		||||
2. **Extensibility**: New model types can be added without modifying core DB code
 | 
			
		||||
3. **Flexibility**: Different modules can define and use their own models with the same DB code
 | 
			
		||||
4. **Type Safety**: Full compile-time type checking is maintained
 | 
			
		||||
 | 
			
		||||
## Implementation Details
 | 
			
		||||
 | 
			
		||||
The key to this architecture is the combination of generic types and trait objects:
 | 
			
		||||
 | 
			
		||||
- `SledDB<T>` provides type-safe operations for specific model types
 | 
			
		||||
- `AnyDbOperations` trait allows type-erased operations through a common interface
 | 
			
		||||
- `TypeId` mapping enables runtime lookup of the correct DB for a given model type
 | 
			
		||||
@@ -1,168 +0,0 @@
 | 
			
		||||
//! Integration tests for zaz database module
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use sled;
 | 
			
		||||
    use bincode;
 | 
			
		||||
    use chrono::{DateTime, Utc};
 | 
			
		||||
    use serde::{Deserialize, Serialize};
 | 
			
		||||
    use std::path::Path;
 | 
			
		||||
    use tempfile::tempdir;
 | 
			
		||||
    use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
    /// Test model for database operations
 | 
			
		||||
    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
			
		||||
    struct User {
 | 
			
		||||
        id: u32,
 | 
			
		||||
        name: String,
 | 
			
		||||
        email: String,
 | 
			
		||||
        balance: f64,
 | 
			
		||||
        created_at: DateTime<Utc>,
 | 
			
		||||
        updated_at: DateTime<Utc>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl User {
 | 
			
		||||
        fn new(id: u32, name: String, email: String, balance: f64) -> Self {
 | 
			
		||||
            let now = Utc::now();
 | 
			
		||||
            Self {
 | 
			
		||||
                id,
 | 
			
		||||
                name,
 | 
			
		||||
                email,
 | 
			
		||||
                balance,
 | 
			
		||||
                created_at: now,
 | 
			
		||||
                updated_at: now,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Test basic CRUD operations
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_basic_crud() {
 | 
			
		||||
        // Create a temporary directory for testing
 | 
			
		||||
        let temp_dir = tempdir().expect("Failed to create temp directory");
 | 
			
		||||
        println!("Created temporary directory at: {:?}", temp_dir.path());
 | 
			
		||||
 | 
			
		||||
        // Open a sled database in the temporary directory
 | 
			
		||||
        let db = sled::open(temp_dir.path().join("users")).expect("Failed to open database");
 | 
			
		||||
        println!("Opened database at: {:?}", temp_dir.path().join("users"));
 | 
			
		||||
 | 
			
		||||
        // CREATE a user
 | 
			
		||||
        let user = User::new(1, "Test User".to_string(), "test@example.com".to_string(), 100.0);
 | 
			
		||||
        let user_key = user.id.to_string();
 | 
			
		||||
        let user_value = bincode::serialize(&user).expect("Failed to serialize user");
 | 
			
		||||
        db.insert(user_key.as_bytes(), user_value).expect("Failed to insert user");
 | 
			
		||||
        db.flush().expect("Failed to flush database");
 | 
			
		||||
        println!("Created user: {} ({})", user.name, user.email);
 | 
			
		||||
 | 
			
		||||
        // READ the user
 | 
			
		||||
        let result = db.get(user_key.as_bytes()).expect("Failed to query database");
 | 
			
		||||
        assert!(result.is_some(), "User should exist");
 | 
			
		||||
        if let Some(data) = result {
 | 
			
		||||
            let retrieved_user: User = bincode::deserialize(&data).expect("Failed to deserialize user");
 | 
			
		||||
            println!("Retrieved user: {} ({})", retrieved_user.name, retrieved_user.email);
 | 
			
		||||
            assert_eq!(user, retrieved_user, "Retrieved user should match original");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // UPDATE the user
 | 
			
		||||
        let updated_user = User::new(1, "Updated User".to_string(), "updated@example.com".to_string(), 150.0);
 | 
			
		||||
        let updated_value = bincode::serialize(&updated_user).expect("Failed to serialize updated user");
 | 
			
		||||
        db.insert(user_key.as_bytes(), updated_value).expect("Failed to update user");
 | 
			
		||||
        db.flush().expect("Failed to flush database");
 | 
			
		||||
        println!("Updated user: {} ({})", updated_user.name, updated_user.email);
 | 
			
		||||
 | 
			
		||||
        let result = db.get(user_key.as_bytes()).expect("Failed to query database");
 | 
			
		||||
        if let Some(data) = result {
 | 
			
		||||
            let retrieved_user: User = bincode::deserialize(&data).expect("Failed to deserialize user");
 | 
			
		||||
            assert_eq!(updated_user, retrieved_user, "Retrieved user should match updated version");
 | 
			
		||||
        } else {
 | 
			
		||||
            panic!("User should exist after update");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // DELETE the user
 | 
			
		||||
        db.remove(user_key.as_bytes()).expect("Failed to delete user");
 | 
			
		||||
        db.flush().expect("Failed to flush database");
 | 
			
		||||
        println!("Deleted user");
 | 
			
		||||
 | 
			
		||||
        let result = db.get(user_key.as_bytes()).expect("Failed to query database");
 | 
			
		||||
        assert!(result.is_none(), "User should be deleted");
 | 
			
		||||
 | 
			
		||||
        // Clean up
 | 
			
		||||
        drop(db);
 | 
			
		||||
        temp_dir.close().expect("Failed to cleanup temporary directory");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Test transaction-like behavior with multiple operations
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_transaction_behavior() {
 | 
			
		||||
        // Create a temporary directory for testing
 | 
			
		||||
        let temp_dir = tempdir().expect("Failed to create temp directory");
 | 
			
		||||
        println!("Created temporary directory at: {:?}", temp_dir.path());
 | 
			
		||||
 | 
			
		||||
        // Open a sled database in the temporary directory
 | 
			
		||||
        let db = sled::open(temp_dir.path().join("tx_test")).expect("Failed to open database");
 | 
			
		||||
        println!("Opened transaction test database at: {:?}", temp_dir.path().join("tx_test"));
 | 
			
		||||
 | 
			
		||||
        // Create initial users
 | 
			
		||||
        let user1 = User::new(1, "User One".to_string(), "one@example.com".to_string(), 100.0);
 | 
			
		||||
        let user2 = User::new(2, "User Two".to_string(), "two@example.com".to_string(), 50.0);
 | 
			
		||||
 | 
			
		||||
        // Insert initial users
 | 
			
		||||
        db.insert(user1.id.to_string().as_bytes(), bincode::serialize(&user1).unwrap()).unwrap();
 | 
			
		||||
        db.insert(user2.id.to_string().as_bytes(), bincode::serialize(&user2).unwrap()).unwrap();
 | 
			
		||||
        db.flush().unwrap();
 | 
			
		||||
        println!("Inserted initial users");
 | 
			
		||||
 | 
			
		||||
        // Simulate a transaction - transfer 25.0 from user1 to user2
 | 
			
		||||
        println!("Starting transaction simulation: transfer 25.0 from user1 to user2");
 | 
			
		||||
 | 
			
		||||
        // Create transaction workspace
 | 
			
		||||
        let mut tx_workspace = HashMap::new();
 | 
			
		||||
 | 
			
		||||
        // Retrieve current state
 | 
			
		||||
        if let Some(data) = db.get(user1.id.to_string().as_bytes()).unwrap() {
 | 
			
		||||
            let user: User = bincode::deserialize(&data).unwrap();
 | 
			
		||||
            tx_workspace.insert(user1.id.to_string(), user);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(data) = db.get(user2.id.to_string().as_bytes()).unwrap() {
 | 
			
		||||
            let user: User = bincode::deserialize(&data).unwrap();
 | 
			
		||||
            tx_workspace.insert(user2.id.to_string(), user);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Modify both users in the transaction
 | 
			
		||||
        let mut updated_user1 = tx_workspace.get(&user1.id.to_string()).unwrap().clone();
 | 
			
		||||
        let mut updated_user2 = tx_workspace.get(&user2.id.to_string()).unwrap().clone();
 | 
			
		||||
 | 
			
		||||
        updated_user1.balance -= 25.0;
 | 
			
		||||
        updated_user2.balance += 25.0;
 | 
			
		||||
 | 
			
		||||
        // Update the workspace
 | 
			
		||||
        tx_workspace.insert(user1.id.to_string(), updated_user1);
 | 
			
		||||
        tx_workspace.insert(user2.id.to_string(), updated_user2);
 | 
			
		||||
 | 
			
		||||
        // Commit the transaction
 | 
			
		||||
        println!("Committing transaction");
 | 
			
		||||
        for (key, user) in tx_workspace {
 | 
			
		||||
            let user_bytes = bincode::serialize(&user).unwrap();
 | 
			
		||||
            db.insert(key.as_bytes(), user_bytes).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
        db.flush().unwrap();
 | 
			
		||||
 | 
			
		||||
        // Verify the results
 | 
			
		||||
        if let Some(data) = db.get(user1.id.to_string().as_bytes()).unwrap() {
 | 
			
		||||
            let final_user1: User = bincode::deserialize(&data).unwrap();
 | 
			
		||||
            assert_eq!(final_user1.balance, 75.0, "User1 balance should be 75.0");
 | 
			
		||||
            println!("Verified user1 balance is now {}", final_user1.balance);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(data) = db.get(user2.id.to_string().as_bytes()).unwrap() {
 | 
			
		||||
            let final_user2: User = bincode::deserialize(&data).unwrap();
 | 
			
		||||
            assert_eq!(final_user2.balance, 75.0, "User2 balance should be 75.0");
 | 
			
		||||
            println!("Verified user2 balance is now {}", final_user2.balance);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Clean up
 | 
			
		||||
        drop(db);
 | 
			
		||||
        temp_dir.close().expect("Failed to cleanup temporary directory");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
// Examples for using the Zaz database
 | 
			
		||||
 | 
			
		||||
use crate::zaz::models::*;
 | 
			
		||||
use crate::zaz::factory::create_zaz_db;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use chrono::Utc;
 | 
			
		||||
 | 
			
		||||
/// Run a simple example of the DB operations
 | 
			
		||||
pub fn run_db_examples() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    println!("Running Zaz DB examples...");
 | 
			
		||||
    
 | 
			
		||||
    // Create a temp DB path
 | 
			
		||||
    let db_path = PathBuf::from("/tmp/zaz-examples");
 | 
			
		||||
    std::fs::create_dir_all(&db_path)?;
 | 
			
		||||
    
 | 
			
		||||
    // Create DB instance
 | 
			
		||||
    let db = create_zaz_db(&db_path)?;
 | 
			
		||||
    
 | 
			
		||||
    // Example 1: User operations
 | 
			
		||||
    println!("\n--- User Examples ---");
 | 
			
		||||
    let user = User::new(
 | 
			
		||||
        1,
 | 
			
		||||
        "John Doe".to_string(),
 | 
			
		||||
        "john@example.com".to_string(),
 | 
			
		||||
        "secure123".to_string(),
 | 
			
		||||
        "Example Corp".to_string(),
 | 
			
		||||
        "User".to_string(),
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    db.set(&user)?;
 | 
			
		||||
    println!("Inserted user: {}", user.name);
 | 
			
		||||
    
 | 
			
		||||
    let retrieved_user = db.get::<User>(&user.id.to_string())?;
 | 
			
		||||
    println!("Retrieved user: {} ({})", retrieved_user.name, retrieved_user.email);
 | 
			
		||||
    
 | 
			
		||||
    // Example 2: Company operations
 | 
			
		||||
    println!("\n--- Company Examples ---");
 | 
			
		||||
    let company = Company::new(
 | 
			
		||||
        1,
 | 
			
		||||
        "Example Corp".to_string(),
 | 
			
		||||
        "EX123456".to_string(),
 | 
			
		||||
        Utc::now(),
 | 
			
		||||
        "12-31".to_string(),
 | 
			
		||||
        "info@example.com".to_string(),
 | 
			
		||||
        "123-456-7890".to_string(),
 | 
			
		||||
        "www.example.com".to_string(),
 | 
			
		||||
        "123 Example St, Example City".to_string(),
 | 
			
		||||
        BusinessType::Global,
 | 
			
		||||
        "Technology".to_string(),
 | 
			
		||||
        "An example company".to_string(),
 | 
			
		||||
        CompanyStatus::Active,
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    db.set(&company)?;
 | 
			
		||||
    println!("Inserted company: {}", company.name);
 | 
			
		||||
    
 | 
			
		||||
    let companies = db.list::<Company>()?;
 | 
			
		||||
    println!("Found {} companies", companies.len());
 | 
			
		||||
    
 | 
			
		||||
    // Clean up
 | 
			
		||||
    std::fs::remove_dir_all(db_path)?;
 | 
			
		||||
    
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
//! Factory module for creating a DB with all zaz models registered
 | 
			
		||||
 | 
			
		||||
use crate::core::{DB, DBBuilder, SledDBResult};
 | 
			
		||||
use crate::zaz::models::*;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
 | 
			
		||||
/// Create a new DB instance with all zaz models registered
 | 
			
		||||
pub fn create_zaz_db<P: Into<PathBuf>>(path: P) -> SledDBResult<DB> {
 | 
			
		||||
    // Using the builder pattern to register all models
 | 
			
		||||
    DBBuilder::new(path)
 | 
			
		||||
        .register_model::<User>()
 | 
			
		||||
        .register_model::<Company>()
 | 
			
		||||
        .register_model::<Meeting>()
 | 
			
		||||
        .register_model::<Product>()
 | 
			
		||||
        .register_model::<Sale>()
 | 
			
		||||
        .register_model::<Vote>()
 | 
			
		||||
        .register_model::<Shareholder>()
 | 
			
		||||
        .build()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Register all zaz models with an existing DB instance
 | 
			
		||||
pub fn register_zaz_models(db: &mut DB) -> SledDBResult<()> {
 | 
			
		||||
    // Dynamically register all zaz models
 | 
			
		||||
    db.register::<User>()?;
 | 
			
		||||
    db.register::<Company>()?;
 | 
			
		||||
    db.register::<Meeting>()?;
 | 
			
		||||
    db.register::<Product>()?;
 | 
			
		||||
    db.register::<Sale>()?;
 | 
			
		||||
    db.register::<Vote>()?;
 | 
			
		||||
    db.register::<Shareholder>()?;
 | 
			
		||||
    
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
use chrono::{Utc, Duration};
 | 
			
		||||
use herodb::db::DBBuilder;
 | 
			
		||||
use herodb::models::governance::{
 | 
			
		||||
use herodb::db::{DBBuilder, SledDB, SledModel};
 | 
			
		||||
use herodb::models::gov::{
 | 
			
		||||
    Company, CompanyStatus, BusinessType,
 | 
			
		||||
    Shareholder, ShareholderType,
 | 
			
		||||
    Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus,
 | 
			
		||||
@@ -12,11 +12,11 @@ use std::path::PathBuf;
 | 
			
		||||
use std::fs;
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    println!("DB Example: Governance Module");
 | 
			
		||||
    println!("DB Example: Gov Module");
 | 
			
		||||
    println!("============================");
 | 
			
		||||
 | 
			
		||||
    // Create a temporary directory for the database
 | 
			
		||||
    let db_path = PathBuf::from("/tmp/dbexample_governance");
 | 
			
		||||
    let db_path = PathBuf::from("/tmp/dbexample_gov");
 | 
			
		||||
    if db_path.exists() {
 | 
			
		||||
        fs::remove_dir_all(&db_path)?;
 | 
			
		||||
    }
 | 
			
		||||
@@ -54,7 +54,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Insert the company
 | 
			
		||||
    db.insert(&company)?;
 | 
			
		||||
    db.set(&company)?;
 | 
			
		||||
    println!("Company created: {} (ID: {})", company.name, company.id);
 | 
			
		||||
    println!("Status: {:?}, Business Type: {:?}", company.status, company.business_type);
 | 
			
		||||
 | 
			
		||||
@@ -90,9 +90,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Insert the users
 | 
			
		||||
    db.insert(&user1)?;
 | 
			
		||||
    db.insert(&user2)?;
 | 
			
		||||
    db.insert(&user3)?;
 | 
			
		||||
    db.set(&user1)?;
 | 
			
		||||
    db.set(&user2)?;
 | 
			
		||||
    db.set(&user3)?;
 | 
			
		||||
 | 
			
		||||
    println!("User created: {} ({})", user1.name, user1.role);
 | 
			
		||||
    println!("User created: {} ({})", user2.name, user2.role);
 | 
			
		||||
@@ -133,9 +133,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Insert the shareholders
 | 
			
		||||
    db.insert(&shareholder1)?;
 | 
			
		||||
    db.insert(&shareholder2)?;
 | 
			
		||||
    db.insert(&shareholder3)?;
 | 
			
		||||
    db.set(&shareholder1)?;
 | 
			
		||||
    db.set(&shareholder2)?;
 | 
			
		||||
    db.set(&shareholder3)?;
 | 
			
		||||
 | 
			
		||||
    println!("Shareholder created: {} ({} shares, {}%)", 
 | 
			
		||||
        shareholder1.name, shareholder1.shares, shareholder1.percentage);
 | 
			
		||||
@@ -146,7 +146,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
 | 
			
		||||
    // Update shareholder shares
 | 
			
		||||
    shareholder1.update_shares(1100.0, 44.0);
 | 
			
		||||
    db.insert(&shareholder1)?;
 | 
			
		||||
    db.set(&shareholder1)?;
 | 
			
		||||
    println!("Updated shareholder: {} ({} shares, {}%)", 
 | 
			
		||||
        shareholder1.name, shareholder1.shares, shareholder1.percentage);
 | 
			
		||||
 | 
			
		||||
@@ -194,7 +194,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    meeting.add_attendee(attendee3);
 | 
			
		||||
 | 
			
		||||
    // Insert the meeting
 | 
			
		||||
    db.insert(&meeting)?;
 | 
			
		||||
    db.set(&meeting)?;
 | 
			
		||||
    println!("Meeting created: {} ({})", meeting.title, meeting.date.format("%Y-%m-%d %H:%M"));
 | 
			
		||||
    println!("Status: {:?}, Attendees: {}", meeting.status, meeting.attendees.len());
 | 
			
		||||
 | 
			
		||||
@@ -205,7 +205,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    if let Some(attendee) = meeting.find_attendee_by_user_id_mut(user3.id) {
 | 
			
		||||
        attendee.update_status(AttendeeStatus::Confirmed);
 | 
			
		||||
    }
 | 
			
		||||
    db.insert(&meeting)?;
 | 
			
		||||
    db.set(&meeting)?;
 | 
			
		||||
 | 
			
		||||
    // Get confirmed attendees
 | 
			
		||||
    let confirmed = meeting.confirmed_attendees();
 | 
			
		||||
@@ -238,19 +238,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    resolution.link_to_meeting(meeting.id);
 | 
			
		||||
 | 
			
		||||
    // Insert the resolution
 | 
			
		||||
    db.insert(&resolution)?;
 | 
			
		||||
    db.set(&resolution)?;
 | 
			
		||||
    println!("Resolution created: {} (Status: {:?})", resolution.title, resolution.status);
 | 
			
		||||
 | 
			
		||||
    // Propose the resolution
 | 
			
		||||
    resolution.propose();
 | 
			
		||||
    db.insert(&resolution)?;
 | 
			
		||||
    db.set(&resolution)?;
 | 
			
		||||
    println!("Resolution proposed on {}", resolution.proposed_at.format("%Y-%m-%d"));
 | 
			
		||||
 | 
			
		||||
    // Add approvals
 | 
			
		||||
    resolution.add_approval(user1.id, user1.name.clone(), true, "Approved as proposed".to_string());
 | 
			
		||||
    resolution.add_approval(user2.id, user2.name.clone(), true, "Financials look good".to_string());
 | 
			
		||||
    resolution.add_approval(user3.id, user3.name.clone(), true, "No concerns".to_string());
 | 
			
		||||
    db.insert(&resolution)?;
 | 
			
		||||
    db.set(&resolution)?;
 | 
			
		||||
 | 
			
		||||
    // Check approval status
 | 
			
		||||
    println!("Approvals: {}, Rejections: {}", 
 | 
			
		||||
@@ -259,7 +259,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
 | 
			
		||||
    // Approve the resolution
 | 
			
		||||
    resolution.approve();
 | 
			
		||||
    db.insert(&resolution)?;
 | 
			
		||||
    db.set(&resolution)?;
 | 
			
		||||
    println!("Resolution approved on {}", 
 | 
			
		||||
        resolution.approved_at.unwrap().format("%Y-%m-%d"));
 | 
			
		||||
 | 
			
		||||
@@ -283,7 +283,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    vote.add_option("Abstain".to_string(), 0);
 | 
			
		||||
 | 
			
		||||
    // Insert the vote
 | 
			
		||||
    db.insert(&vote)?;
 | 
			
		||||
    db.set(&vote)?;
 | 
			
		||||
    println!("Vote created: {} (Status: {:?})", vote.title, vote.status);
 | 
			
		||||
    println!("Voting period: {} to {}", 
 | 
			
		||||
        vote.start_date.format("%Y-%m-%d"),
 | 
			
		||||
@@ -293,7 +293,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    vote.add_ballot(user1.id, 1, 1000); // User 1 votes "Approve" with 1000 shares
 | 
			
		||||
    vote.add_ballot(user2.id, 1, 750);  // User 2 votes "Approve" with 750 shares
 | 
			
		||||
    vote.add_ballot(user3.id, 3, 750);  // User 3 votes "Abstain" with 750 shares
 | 
			
		||||
    db.insert(&vote)?;
 | 
			
		||||
    db.set(&vote)?;
 | 
			
		||||
 | 
			
		||||
    // Check voting results
 | 
			
		||||
    println!("Voting results:");
 | 
			
		||||
@@ -314,7 +314,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    // Link the resolution to the vote
 | 
			
		||||
    vote_resolution.link_to_vote(vote.id);
 | 
			
		||||
    vote_resolution.propose();
 | 
			
		||||
    db.insert(&vote_resolution)?;
 | 
			
		||||
    db.set(&vote_resolution)?;
 | 
			
		||||
    println!("Created resolution linked to vote: {}", vote_resolution.title);
 | 
			
		||||
 | 
			
		||||
    println!("\n7. Retrieving Related Objects");
 | 
			
		||||
							
								
								
									
										2161
									
								
								herodb/src/cmd/dbexample_governance/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2161
									
								
								herodb/src/cmd/dbexample_governance/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,12 +0,0 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "dbexample_governance"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[[bin]]
 | 
			
		||||
name = "dbexample_governance"
 | 
			
		||||
path = "main.rs"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
herodb = { path = "../../.." }
 | 
			
		||||
chrono = "0.4"
 | 
			
		||||
@@ -115,7 +115,7 @@ pub mod governance;
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBError};
 | 
			
		||||
use crate::models::governance::{Meeting, Vote};
 | 
			
		||||
use crate::models::gov::{Meeting, Vote};
 | 
			
		||||
 | 
			
		||||
/// ResolutionStatus represents the status of a resolution
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBError};
 | 
			
		||||
use crate::models::governance::User;
 | 
			
		||||
use crate::models::gov::User;
 | 
			
		||||
 | 
			
		||||
/// CommitteeRole represents the role of a member in a committee
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
@@ -123,8 +123,8 @@ impl Company {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get all resolutions for this company
 | 
			
		||||
    pub fn get_resolutions(&self, db: &SledDB<crate::models::governance::Resolution>) -> Result<Vec<crate::models::governance::Resolution>, SledDBError> {
 | 
			
		||||
        let all_resolutions = db.list()?;
 | 
			
		||||
    pub fn get_resolutions(&self, db: &crate::db::DB) -> Result<Vec<super::Resolution>, SledDBError> {
 | 
			
		||||
        let all_resolutions = db.list::<super::Resolution>()?;
 | 
			
		||||
        let company_resolutions = all_resolutions
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .filter(|resolution| resolution.company_id == self.id)
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBError};
 | 
			
		||||
use crate::models::governance::Company;
 | 
			
		||||
use crate::models::gov::Company;
 | 
			
		||||
 | 
			
		||||
/// ComplianceRequirement represents a regulatory requirement
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
			
		||||
@@ -164,8 +164,8 @@ impl Meeting {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get all resolutions discussed in this meeting
 | 
			
		||||
    pub fn get_resolutions(&self, db: &SledDB<crate::models::governance::Resolution>) -> Result<Vec<crate::models::governance::Resolution>, SledDBError> {
 | 
			
		||||
        let all_resolutions = db.list()?;
 | 
			
		||||
    pub fn get_resolutions(&self, db: &crate::db::DB) -> Result<Vec<super::Resolution>, SledDBError> {
 | 
			
		||||
        let all_resolutions = db.list::<super::Resolution>()?;
 | 
			
		||||
        let meeting_resolutions = all_resolutions
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .filter(|resolution| resolution.meeting_id == Some(self.id))
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBError};
 | 
			
		||||
use crate::models::governance::{Meeting, Vote};
 | 
			
		||||
use crate::models::gov::{Meeting, Vote};
 | 
			
		||||
 | 
			
		||||
/// ResolutionStatus represents the status of a resolution
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
@@ -158,10 +158,10 @@ impl Resolution {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get the meeting associated with this resolution
 | 
			
		||||
    pub fn get_meeting(&self, db: &SledDB<Meeting>) -> Result<Option<Meeting>, SledDBError> {
 | 
			
		||||
    pub fn get_meeting(&self, db: &crate::db::DB) -> Result<Option<Meeting>, SledDBError> {
 | 
			
		||||
        match self.meeting_id {
 | 
			
		||||
            Some(meeting_id) => {
 | 
			
		||||
                let meeting = db.get(&meeting_id.to_string())?;
 | 
			
		||||
                let meeting = db.get::<Meeting>(&meeting_id.to_string())?;
 | 
			
		||||
                Ok(Some(meeting))
 | 
			
		||||
            }
 | 
			
		||||
            None => Ok(None),
 | 
			
		||||
@@ -169,10 +169,10 @@ impl Resolution {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get the vote associated with this resolution
 | 
			
		||||
    pub fn get_vote(&self, db: &SledDB<Vote>) -> Result<Option<Vote>, SledDBError> {
 | 
			
		||||
    pub fn get_vote(&self, db: &crate::db::DB) -> Result<Option<Vote>, SledDBError> {
 | 
			
		||||
        match self.vote_id {
 | 
			
		||||
            Some(vote_id) => {
 | 
			
		||||
                let vote = db.get(&vote_id.to_string())?;
 | 
			
		||||
                let vote = db.get::<Vote>(&vote_id.to_string())?;
 | 
			
		||||
                Ok(Some(vote))
 | 
			
		||||
            }
 | 
			
		||||
            None => Ok(None),
 | 
			
		||||
@@ -128,8 +128,8 @@ impl Vote {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get the resolution associated with this vote
 | 
			
		||||
    pub fn get_resolution(&self, db: &SledDB<crate::models::governance::Resolution>) -> Result<Option<crate::models::governance::Resolution>, SledDBError> {
 | 
			
		||||
        let all_resolutions = db.list()?;
 | 
			
		||||
    pub fn get_resolution(&self, db: &crate::db::DB) -> Result<Option<super::Resolution>, SledDBError> {
 | 
			
		||||
        let all_resolutions = db.list::<super::Resolution>()?;
 | 
			
		||||
        let vote_resolution = all_resolutions
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .find(|resolution| resolution.vote_id == Some(self.id));
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
pub mod biz;
 | 
			
		||||
pub mod mcc;
 | 
			
		||||
pub mod circle;
 | 
			
		||||
pub mod governance;
 | 
			
		||||
pub mod gov;
 | 
			
		||||
		Reference in New Issue
	
	Block a user