From 98af5a3b029e162a924bcee7f30e79fa81095ceb Mon Sep 17 00:00:00 2001 From: despiegk Date: Mon, 21 Apr 2025 03:39:00 +0200 Subject: [PATCH] ... --- herodb/src/cmd/dbexample_gov/main.rs | 6 +- herodb/src/db/db.rs | 39 +- herodb/src/db/store.rs | 60 ++- .../models/gov/GOVERNANCE_ENHANCEMENT_PLAN.md | 496 ------------------ 4 files changed, 59 insertions(+), 542 deletions(-) delete mode 100644 herodb/src/models/gov/GOVERNANCE_ENHANCEMENT_PLAN.md diff --git a/herodb/src/cmd/dbexample_gov/main.rs b/herodb/src/cmd/dbexample_gov/main.rs index 9718618..3a3be71 100644 --- a/herodb/src/cmd/dbexample_gov/main.rs +++ b/herodb/src/cmd/dbexample_gov/main.rs @@ -7,8 +7,7 @@ use herodb::models::gov::{ User, Vote, VoteOption, Ballot, VoteStatus, Resolution, ResolutionStatus, Approval, - Committee, CommitteeRole, CommitteeMember, - ComplianceRequirement, ComplianceDocument, ComplianceAudit + Committee, CommitteeRole, CommitteeMember }; use std::path::PathBuf; use std::fs; @@ -34,9 +33,6 @@ fn main() -> Result<(), Box> { .register_model::() .register_model::() .register_model::() - .register_model::() - .register_model::() - .register_model::() .build()?; println!("\n1. Creating a Company"); diff --git a/herodb/src/db/db.rs b/herodb/src/db/db.rs index df3297c..bdf1dfa 100644 --- a/herodb/src/db/db.rs +++ b/herodb/src/db/db.rs @@ -43,7 +43,7 @@ pub struct DB { db_path: PathBuf, // Type map for generic operations - type_map: HashMap>, + type_map: HashMap>>, // Transaction state transaction: Arc>>, @@ -58,7 +58,7 @@ pub struct DBBuilder { /// Trait for model registration pub trait ModelRegistration: Send + Sync { - fn register(&self, path: &Path) -> DbResult<(TypeId, Box)>; + fn register(&self, path: &Path) -> DbResult<(TypeId, Arc>)>; } /// Implementation of ModelRegistration for any Model type @@ -75,9 +75,9 @@ impl ModelRegistrar { } impl ModelRegistration for ModelRegistrar { - fn register(&self, path: &Path) -> DbResult<(TypeId, Box)> { + fn register(&self, path: &Path) -> DbResult<(TypeId, Arc>)> { let store = OurDbStore::::open(path.join(T::db_prefix()))?; - Ok((TypeId::of::(), Box::new(store) as Box)) + Ok((TypeId::of::(), Arc::new(RwLock::new(store)) as Arc>)) } } @@ -116,13 +116,13 @@ impl DBBuilder { } // Register all models - let mut type_map: HashMap> = HashMap::new(); + let mut type_map: HashMap>> = HashMap::new(); for registration in self.model_registrations { let (type_id, store) = registration.register(&base_path).map_err(|e| { EvalAltResult::ErrorSystem("Could not register type".to_string(), Box::new(e)) })?; - type_map.insert(type_id, store.into()); + type_map.insert(type_id, store); } let transaction = Arc::new(RwLock::new(None)); @@ -179,7 +179,8 @@ impl DB { // Get the database operations for this model type if let Some(db_ops) = self.type_map.get(&model_type) { // Just pass the raw serialized data to a special raw insert method - return db_ops.insert_raw(serialized); + let mut db_ops_guard = db_ops.write().unwrap(); + return db_ops_guard.insert_raw(serialized); } Err(DbError::GeneralError(format!( @@ -211,7 +212,8 @@ impl DB { .type_map .get(&model_type) .ok_or_else(|| DbError::TypeError)?; - db_ops.delete(id)?; + let mut db_ops_guard = db_ops.write().unwrap(); + db_ops_guard.delete(id)?; } } } @@ -266,7 +268,10 @@ impl DB { // Execute directly match self.type_map.get(&TypeId::of::()) { - Some(db_ops) => db_ops.insert(model), + Some(db_ops) => { + let mut db_ops_guard = db_ops.write().unwrap(); + db_ops_guard.insert(model) + }, None => Err(DbError::TypeError), } } @@ -335,7 +340,8 @@ impl DB { // If no pending value, look up from the database match self.type_map.get(&TypeId::of::()) { Some(db_ops) => { - let result_any = db_ops.get(id)?; + let mut db_ops_guard = db_ops.write().unwrap(); + let result_any = db_ops_guard.get(id)?; // We expect the result to be of type T since we looked it up by TypeId match result_any.downcast::() { Ok(t) => Ok(*t), @@ -370,7 +376,10 @@ impl DB { // Execute directly match self.type_map.get(&TypeId::of::()) { - Some(db_ops) => db_ops.delete(id), + Some(db_ops) => { + let mut db_ops_guard = db_ops.write().unwrap(); + db_ops_guard.delete(id) + }, None => Err(DbError::TypeError), } } @@ -380,7 +389,8 @@ impl DB { // Look up the correct DB operations for type T in our type map match self.type_map.get(&TypeId::of::()) { Some(db_ops) => { - let result_any = db_ops.list()?; + let db_ops_guard = db_ops.read().unwrap(); + let result_any = db_ops_guard.list()?; // We expect the result to be of type Vec since we looked it up by TypeId match result_any.downcast::>() { Ok(vec_t) => Ok(*vec_t), @@ -396,7 +406,8 @@ impl DB { // Look up the correct DB operations for type T in our type map match self.type_map.get(&TypeId::of::()) { Some(db_ops) => { - let result_any = db_ops.get_history(id, depth)?; + let mut db_ops_guard = db_ops.write().unwrap(); + let result_any = db_ops_guard.get_history(id, depth)?; let mut result = Vec::with_capacity(result_any.len()); for item in result_any { @@ -415,7 +426,7 @@ impl DB { // Register a model type with this DB instance pub fn register(&mut self) -> DbResult<()> { let store = OurDbStore::::open(&self.db_path)?; - self.type_map.insert(TypeId::of::(), Arc::new(store)); + self.type_map.insert(TypeId::of::(), Arc::new(RwLock::new(store))); Ok(()) } } diff --git a/herodb/src/db/store.rs b/herodb/src/db/store.rs index 879e040..0e11696 100644 --- a/herodb/src/db/store.rs +++ b/herodb/src/db/store.rs @@ -7,12 +7,12 @@ use std::any::Any; /// Trait for type-erased database operations pub trait DbOperations: Send + Sync { - fn delete(&self, id: u32) -> DbResult<()>; - fn get(&self, id: u32) -> DbResult>; + fn delete(&mut self, id: u32) -> DbResult<()>; + fn get(&mut self, id: u32) -> DbResult>; fn list(&self) -> DbResult>; - fn insert(&self, model: &dyn Any) -> DbResult<()>; - fn insert_raw(&self, serialized: &[u8]) -> DbResult<()>; - fn get_history(&self, id: u32, depth: u8) -> DbResult>>; + fn insert(&mut self, model: &dyn Any) -> DbResult<()>; + fn insert_raw(&mut self, serialized: &[u8]) -> DbResult<()>; + fn get_history(&mut self, id: u32, depth: u8) -> DbResult>>; } /// A store implementation using OurDB as the backend @@ -50,12 +50,13 @@ impl OurDbStore { /// Inserts or updates a model instance in the database pub fn insert(&mut self, model: &T) -> DbResult<()> { - let id = model.get_id(); // Use the new method name let data = model.to_bytes()?; + // Don't pass the ID when using incremental mode + // OurDB will automatically assign an ID self.db.set(OurDBSetArgs { - id: Some(id), + id: None, data: &data, }).map_err(DbError::OurDbError)?; @@ -112,16 +113,13 @@ impl OurDbStore { } impl DbOperations for OurDbStore { - fn delete(&self, _id: u32) -> DbResult<()> { - // We need to mutably borrow self, but the trait requires &self - // This is a design issue that needs to be fixed at the trait level - Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string())) + fn delete(&mut self, id: u32) -> DbResult<()> { + self.delete(id) } - fn get(&self, _id: u32) -> DbResult> { - // We need to mutably borrow self, but the trait requires &self - // This is a design issue that needs to be fixed at the trait level - Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string())) + fn get(&mut self, id: u32) -> DbResult> { + let result = self.get(id)?; + Ok(Box::new(result)) } fn list(&self) -> DbResult> { @@ -130,21 +128,29 @@ impl DbOperations for OurDbStore { Ok(Box::new(result)) } - fn insert(&self, _model: &dyn Any) -> DbResult<()> { - // We need to mutably borrow self, but the trait requires &self - // This is a design issue that needs to be fixed at the trait level - Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string())) + fn insert(&mut self, model: &dyn Any) -> DbResult<()> { + // Downcast the Any to T + if let Some(model_t) = model.downcast_ref::() { + self.insert(model_t) + } else { + Err(DbError::TypeError) + } } - fn insert_raw(&self, _serialized: &[u8]) -> DbResult<()> { - // We need to mutably borrow self, but the trait requires &self - // This is a design issue that needs to be fixed at the trait level - Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string())) + fn insert_raw(&mut self, serialized: &[u8]) -> DbResult<()> { + // Deserialize the raw bytes to a model + let model = T::from_bytes(serialized)?; + self.insert(&model) } - fn get_history(&self, _id: u32, _depth: u8) -> DbResult>> { - // We need to mutably borrow self, but the trait requires &self - // This is a design issue that needs to be fixed at the trait level - Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string())) + fn get_history(&mut self, id: u32, depth: u8) -> DbResult>> { + let history = self.get_history(id, depth)?; + let mut result = Vec::with_capacity(history.len()); + + for item in history { + result.push(Box::new(item) as Box); + } + + Ok(result) } } \ No newline at end of file diff --git a/herodb/src/models/gov/GOVERNANCE_ENHANCEMENT_PLAN.md b/herodb/src/models/gov/GOVERNANCE_ENHANCEMENT_PLAN.md deleted file mode 100644 index 6e31a4b..0000000 --- a/herodb/src/models/gov/GOVERNANCE_ENHANCEMENT_PLAN.md +++ /dev/null @@ -1,496 +0,0 @@ -# Governance Module Enhancement Plan (Revised) - -## 1. Current State Analysis - -The governance module currently consists of: -- **Company**: Company model with basic company information -- **Shareholder**: Shareholder model for managing company ownership -- **Meeting**: Meeting and Attendee models for board meetings -- **User**: User model for system users -- **Vote**: Vote, VoteOption, and Ballot models for voting - -All models implement the `Storable` and `SledModel` traits for database integration, but the module has several limitations: -- Not imported in src/models/mod.rs, making it inaccessible to the rest of the project -- No mod.rs file to organize and re-export the types -- No README.md file to document the purpose and usage -- Inconsistent imports across files (e.g., crate::db vs crate::core) -- Limited utility methods and relationships between models -- No integration with other modules like biz, mcc, or circle - -## 2. Planned Enhancements - -### 2.1 Module Organization and Integration - -- Create a mod.rs file to organize and re-export the types -- Add the governance module to src/models/mod.rs -- Create a README.md file to document the purpose and usage -- Standardize imports across all files - -### 2.2 New Models - -#### 2.2.1 Resolution Model - -Create a new `resolution.rs` file with a Resolution model for managing board resolutions: -- Resolution information (title, description, text) -- Resolution status (Draft, Proposed, Approved, Rejected) -- Voting results and approvals -- Integration with Meeting and Vote models - -### 2.3 Enhanced Relationships and Integration - -#### 2.3.1 Integration with Biz Module - -- Link Company with biz::Customer and biz::Contract -- Link Shareholder with biz::Customer -- Link Meeting with biz::Invoice for expense tracking - -#### 2.3.2 Integration with MCC Module - -- Link Meeting with mcc::Calendar and mcc::Event -- Link User with mcc::Contact -- Link Vote with mcc::Message for notifications - -#### 2.3.3 Integration with Circle Module - -- Link Company with circle::Circle for group-based access control -- Link User with circle::Member for role-based permissions - -### 2.4 Utility Methods and Functionality - -- Add filtering and searching methods to all models -- Add relationship management methods between models -- Add validation and business logic methods - -## 3. Implementation Plan - -```mermaid -flowchart TD - A[Review Current Models] --> B[Create mod.rs and Update models/mod.rs] - B --> C[Standardize Imports and Fix Inconsistencies] - C --> D[Create Resolution Model] - D --> E[Implement Integration with Other Modules] - E --> F[Add Utility Methods] - F --> G[Create README.md and Documentation] - G --> H[Write Tests] -``` - -### 3.1 Detailed Changes - -#### 3.1.1 Module Organization - -Create a new `mod.rs` file in the governance directory: - -```rust -pub mod company; -pub mod shareholder; -pub mod meeting; -pub mod user; -pub mod vote; -pub mod resolution; - -// Re-export all model types for convenience -pub use company::{Company, CompanyStatus, BusinessType}; -pub use shareholder::{Shareholder, ShareholderType}; -pub use meeting::{Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus}; -pub use user::User; -pub use vote::{Vote, VoteOption, Ballot, VoteStatus}; -pub use resolution::{Resolution, ResolutionStatus, Approval}; - -// Re-export database components from db module -pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB}; -``` - -Update `src/models/mod.rs` to include the governance module: - -```rust -pub mod biz; -pub mod mcc; -pub mod circle; -pub mod governance; -``` - -#### 3.1.2 Resolution Model (`resolution.rs`) - -```rust -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use crate::db::{SledModel, Storable, SledDB, SledDBError}; -use crate::models::gov::{Meeting, Vote}; - -/// ResolutionStatus represents the status of a resolution -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum ResolutionStatus { - Draft, - Proposed, - Approved, - Rejected, - Withdrawn, -} - -/// Resolution represents a board resolution -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct Resolution { - pub id: u32, - pub company_id: u32, - pub meeting_id: Option, - pub vote_id: Option, - pub title: String, - pub description: String, - pub text: String, - pub status: ResolutionStatus, - pub proposed_by: u32, // User ID - pub proposed_at: DateTime, - pub approved_at: Option>, - pub rejected_at: Option>, - pub created_at: DateTime, - pub updated_at: DateTime, - pub approvals: Vec, -} - -/// Approval represents an approval of a resolution by a board member -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct Approval { - pub id: u32, - pub resolution_id: u32, - pub user_id: u32, - pub name: String, - pub approved: bool, - pub comments: String, - pub created_at: DateTime, -} - -impl Resolution { - /// Create a new resolution with default values - pub fn new( - id: u32, - company_id: u32, - title: String, - description: String, - text: String, - proposed_by: u32, - ) -> Self { - let now = Utc::now(); - Self { - id, - company_id, - meeting_id: None, - vote_id: None, - title, - description, - text, - status: ResolutionStatus::Draft, - proposed_by, - proposed_at: now, - approved_at: None, - rejected_at: None, - created_at: now, - updated_at: now, - approvals: Vec::new(), - } - } - - /// Propose the resolution - pub fn propose(&mut self) { - self.status = ResolutionStatus::Proposed; - self.proposed_at = Utc::now(); - self.updated_at = Utc::now(); - } - - /// Approve the resolution - pub fn approve(&mut self) { - self.status = ResolutionStatus::Approved; - self.approved_at = Some(Utc::now()); - self.updated_at = Utc::now(); - } - - /// Reject the resolution - pub fn reject(&mut self) { - self.status = ResolutionStatus::Rejected; - self.rejected_at = Some(Utc::now()); - self.updated_at = Utc::now(); - } - - /// Add an approval to the resolution - pub fn add_approval(&mut self, user_id: u32, name: String, approved: bool, comments: String) -> &Approval { - let id = if self.approvals.is_empty() { - 1 - } else { - self.approvals.iter().map(|a| a.id).max().unwrap_or(0) + 1 - }; - - let approval = Approval { - id, - resolution_id: self.id, - user_id, - name, - approved, - comments, - created_at: Utc::now(), - }; - - self.approvals.push(approval); - self.updated_at = Utc::now(); - self.approvals.last().unwrap() - } - - /// Link this resolution to a meeting - pub fn link_to_meeting(&mut self, meeting_id: u32) { - self.meeting_id = Some(meeting_id); - self.updated_at = Utc::now(); - } - - /// Link this resolution to a vote - pub fn link_to_vote(&mut self, vote_id: u32) { - self.vote_id = Some(vote_id); - self.updated_at = Utc::now(); - } -} - -// Implement Storable trait (provides default dump/load) -impl Storable for Resolution {} -impl Storable for Approval {} - -// Implement SledModel trait -impl SledModel for Resolution { - fn get_id(&self) -> String { - self.id.to_string() - } - - fn db_prefix() -> &'static str { - "resolution" - } -} -``` - -#### 3.1.3 Enhanced Company Model (`company.rs`) - -Add integration with other modules: - -```rust -impl Company { - // ... existing methods ... - - /// Link this company to a Circle for access control - pub fn link_to_circle(&mut self, circle_id: u32) -> Result<(), SledDBError> { - // Implementation details - self.updated_at = Utc::now(); - Ok(()) - } - - /// Link this company to a Customer in the biz module - pub fn link_to_customer(&mut self, customer_id: u32) -> Result<(), SledDBError> { - // Implementation details - self.updated_at = Utc::now(); - Ok(()) - } - - /// Get all resolutions for this company - pub fn get_resolutions(&self, db: &SledDB) -> Result, SledDBError> { - let all_resolutions = db.list()?; - let company_resolutions = all_resolutions - .into_iter() - .filter(|resolution| resolution.company_id == self.id) - .collect(); - - Ok(company_resolutions) - } -} -``` - -#### 3.1.4 Enhanced Meeting Model (`meeting.rs`) - -Add integration with other modules: - -```rust -impl Meeting { - // ... existing methods ... - - /// Link this meeting to a Calendar Event in the mcc module - pub fn link_to_event(&mut self, event_id: u32) -> Result<(), SledDBError> { - // Implementation details - self.updated_at = Utc::now(); - Ok(()) - } - - /// Get all resolutions discussed in this meeting - pub fn get_resolutions(&self, db: &SledDB) -> Result, SledDBError> { - let all_resolutions = db.list()?; - let meeting_resolutions = all_resolutions - .into_iter() - .filter(|resolution| resolution.meeting_id == Some(self.id)) - .collect(); - - Ok(meeting_resolutions) - } -} -``` - -#### 3.1.5 Enhanced Vote Model (`vote.rs`) - -Add integration with Resolution model: - -```rust -impl Vote { - // ... existing methods ... - - /// Get the resolution associated with this vote - pub fn get_resolution(&self, db: &SledDB) -> Result, SledDBError> { - let all_resolutions = db.list()?; - let vote_resolution = all_resolutions - .into_iter() - .find(|resolution| resolution.vote_id == Some(self.id)); - - Ok(vote_resolution) - } -} -``` - -#### 3.1.6 Create README.md - -Create a README.md file to document the purpose and usage of the governance module. - -## 4. Data Model Diagram - -```mermaid -classDiagram - class Company { - +u32 id - +String name - +String registration_number - +DateTime incorporation_date - +String fiscal_year_end - +String email - +String phone - +String website - +String address - +BusinessType business_type - +String industry - +String description - +CompanyStatus status - +DateTime created_at - +DateTime updated_at - +add_shareholder() - +link_to_circle() - +link_to_customer() - +get_resolutions() - } - - class Shareholder { - +u32 id - +u32 company_id - +u32 user_id - +String name - +f64 shares - +f64 percentage - +ShareholderType type_ - +DateTime since - +DateTime created_at - +DateTime updated_at - +update_shares() - } - - class Meeting { - +u32 id - +u32 company_id - +String title - +DateTime date - +String location - +String description - +MeetingStatus status - +String minutes - +DateTime created_at - +DateTime updated_at - +Vec~Attendee~ attendees - +add_attendee() - +update_status() - +update_minutes() - +find_attendee_by_user_id() - +confirmed_attendees() - +link_to_event() - +get_resolutions() - } - - class User { - +u32 id - +String name - +String email - +String password - +String company - +String role - +DateTime created_at - +DateTime updated_at - } - - class Vote { - +u32 id - +u32 company_id - +String title - +String description - +DateTime start_date - +DateTime end_date - +VoteStatus status - +DateTime created_at - +DateTime updated_at - +Vec~VoteOption~ options - +Vec~Ballot~ ballots - +Vec~u32~ private_group - +add_option() - +add_ballot() - +get_resolution() - } - - class Resolution { - +u32 id - +u32 company_id - +Option~u32~ meeting_id - +Option~u32~ vote_id - +String title - +String description - +String text - +ResolutionStatus status - +u32 proposed_by - +DateTime proposed_at - +Option~DateTime~ approved_at - +Option~DateTime~ rejected_at - +DateTime created_at - +DateTime updated_at - +Vec~Approval~ approvals - +propose() - +approve() - +reject() - +add_approval() - +link_to_meeting() - +link_to_vote() - } - - Company "1" -- "many" Shareholder: has - Company "1" -- "many" Meeting: holds - Company "1" -- "many" Vote: conducts - Company "1" -- "many" Resolution: issues - Meeting "1" -- "many" Attendee: has - Meeting "1" -- "many" Resolution: discusses - Vote "1" -- "many" VoteOption: has - Vote "1" -- "many" Ballot: collects - Vote "1" -- "1" Resolution: decides - Resolution "1" -- "many" Approval: receives -``` - -## 5. Testing Strategy - -1. Unit tests for each model to verify: - - Basic functionality - - Serialization/deserialization - - Utility methods - - Integration with other models -2. Integration tests to verify: - - Database operations with the models - - Relationships between models - - Integration with other modules - -## 6. Future Considerations - -1. **Committee Model**: Add a Committee model in the future if needed -2. **Compliance Model**: Add compliance-related models in the future if needed -3. **API Integration**: Develop REST API endpoints for the governance module -4. **UI Components**: Create UI components for managing governance entities -5. **Reporting**: Implement reporting functionality for governance metrics \ No newline at end of file