merge branches and cleanup db

This commit is contained in:
timurgordon
2025-06-27 12:11:04 +03:00
parent 5563d7e27e
commit 1f9ec01934
177 changed files with 1202 additions and 174 deletions

View File

@@ -0,0 +1,48 @@
# Business Models Example
This example demonstrates the business models in HeroDB, showcasing a complete business transaction flow from product definition to payment processing.
## Features Demonstrated
1. **Product Definition**: Creating two types of server node products with different components and pricing
2. **Component Definition**: Defining the parts that make up each server node (CPU, RAM, Storage, GPU)
3. **Pricing Setup**: Setting up prices for products using the Currency model
4. **Product Availability**: Checking which products can be purchased based on their status and availability
5. **Sales Process**: Simulating a customer purchasing a product
6. **Invoice Generation**: Creating an invoice for the sale
7. **Payment Processing**: Processing a payment for the invoice and updating its status
## Business Flow
The example follows this business flow:
```
Define Products → Check Availability → Customer Purchase → Generate Invoice → Process Payment
```
## Models Used
- **Product & ProductComponent**: For defining server nodes and their components
- **Customer**: For representing the buyer
- **Sale & SaleItem**: For recording the purchase transaction
- **Invoice & InvoiceItem**: For billing the customer
- **Payment**: For recording the payment
## Running the Example
To run this example, use:
```bash
cargo run --bin dbexample_biz
```
The output will show each step of the business process with relevant details.
## Key Concepts
- **Builder Pattern**: All models use builders for flexible object creation
- **Status Tracking**: Sales and invoices have status enums to track their state
- **Relationship Modeling**: The example shows how different business entities relate to each other
- **Financial Calculations**: Demonstrates tax and total calculations
This example provides a template for implementing business logic in your own applications using HeroDB.

View File

@@ -0,0 +1,326 @@
use chrono::{Duration, Utc};
use herodb::db::{DBBuilder, DB, Model};
use herodb::models::biz::{
Currency, CurrencyBuilder,
Product, ProductBuilder, ProductComponent, ProductComponentBuilder, ProductType, ProductStatus,
Sale, SaleBuilder, SaleItem, SaleItemBuilder, SaleStatus,
Invoice, InvoiceBuilder, InvoiceItem, InvoiceItemBuilder, InvoiceStatus, Payment, PaymentStatus,
Customer, CustomerBuilder,
};
use std::path::PathBuf;
use std::fs;
/// This example demonstrates the business models in action:
/// 1. Defining products (2 types of server nodes)
/// 2. Defining components (parts of the nodes)
/// 3. Setting up pricing
/// 4. Creating a function to check which products can be bought
/// 5. Simulating a user buying a product
/// 6. Generating an invoice
/// 7. Simulating payment
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Business Models Example");
println!("=======================\n");
// Create a temporary directory for the database
let db_path = PathBuf::from("/tmp/dbexample_biz");
if db_path.exists() {
fs::remove_dir_all(&db_path)?;
}
fs::create_dir_all(&db_path)?;
println!("Database path: {:?}", db_path);
// Create a database instance with our business models registered
let db = DBBuilder::new(&db_path)
.register_model::<Customer>()
.register_model::<Product>()
.register_model::<Sale>()
.register_model::<Invoice>()
.build()?;
// Create a customer
let customer = create_customer();
println!("Created customer: {}", customer.name);
db.set(&customer)?;
// Define products (server nodes)
let (standard_node, premium_node) = create_server_products();
println!("Created server products:");
println!(" - Standard Node: ${} {}", standard_node.price.amount, standard_node.price.currency_code);
println!(" - Premium Node: ${} {}", premium_node.price.amount, premium_node.price.currency_code);
// Store products in the database
db.set(&standard_node)?;
db.set(&premium_node)?;
// Check which products can be purchased
println!("\nChecking which products can be purchased:");
let purchasable_products = get_purchasable_products(&[&standard_node, &premium_node]);
for product in purchasable_products {
println!(" - {} is available for purchase", product.name);
}
// Simulate a user buying a product
println!("\nSimulating purchase of a Premium Node:");
let sale = create_sale(&customer, &premium_node);
println!(" - Sale created with ID: {}", sale.get_id());
println!(" - Total amount: ${} {}", sale.total_amount.amount, sale.total_amount.currency_code);
db.set(&sale)?;
// Generate an invoice
println!("\nGenerating invoice:");
let invoice = create_invoice(&customer, &sale);
println!(" - Invoice created with ID: {}", invoice.get_id());
println!(" - Total amount: ${} {}", invoice.total_amount.amount, invoice.total_amount.currency_code);
println!(" - Due date: {}", invoice.due_date);
println!(" - Status: {:?}", invoice.status);
db.set(&invoice)?;
// Simulate payment
println!("\nSimulating payment:");
let mut invoice_copy = invoice.clone();
process_payment(&mut invoice_copy);
println!(" - Payment processed");
println!(" - New balance due: ${} {}", invoice_copy.balance_due.amount, invoice_copy.balance_due.currency_code);
println!(" - Payment status: {:?}", invoice_copy.payment_status);
println!(" - Invoice status: {:?}", invoice_copy.status);
db.set(&invoice_copy)?;
// Retrieve data from the database to verify persistence
println!("\nRetrieving data from database:");
// Retrieve customer
let retrieved_customer = db.get::<Customer>(customer.get_id())?;
println!("Retrieved customer: {} (ID: {})", retrieved_customer.name, retrieved_customer.get_id());
// Retrieve product
let retrieved_product = db.get::<Product>(premium_node.get_id())?;
println!("Retrieved product: {} (ID: {})", retrieved_product.name, retrieved_product.get_id());
// Retrieve sale
let retrieved_sale = db.get::<Sale>(sale.get_id())?;
println!("Retrieved sale: ID {} with {} items", retrieved_sale.get_id(), retrieved_sale.items.len());
// Retrieve invoice
let retrieved_invoice = db.get::<Invoice>(invoice.get_id())?;
println!("Retrieved invoice: ID {} with status {:?}", retrieved_invoice.get_id(), retrieved_invoice.status);
println!("\nBusiness transaction completed successfully!");
Ok(())
}
/// Create a customer for our example
fn create_customer() -> Customer {
CustomerBuilder::new()
.id(1)
.name("TechCorp Inc.")
.description("Enterprise technology company")
.pubkey("tech-corp-public-key-123")
.build()
.expect("Failed to create customer")
}
/// Create two types of server node products with their components
fn create_server_products() -> (Product, Product) {
// Create currencies for pricing
let standard_price = CurrencyBuilder::new()
.id(100)
.amount(99.99)
.currency_code("USD")
.build()
.expect("Failed to create currency");
let premium_price = CurrencyBuilder::new()
.id(101)
.amount(199.99)
.currency_code("USD")
.build()
.expect("Failed to create currency");
// Standard Node Components
let cpu_standard = ProductComponentBuilder::new()
.id(1)
.name("CPU")
.description("4-core CPU")
.quantity(1)
.build()
.expect("Failed to create CPU component");
let ram_standard = ProductComponentBuilder::new()
.id(2)
.name("RAM")
.description("16GB RAM")
.quantity(1)
.build()
.expect("Failed to create RAM component");
let storage_standard = ProductComponentBuilder::new()
.id(3)
.name("Storage")
.description("500GB SSD")
.quantity(1)
.build()
.expect("Failed to create Storage component");
// Premium Node Components
let cpu_premium = ProductComponentBuilder::new()
.id(4)
.name("CPU")
.description("8-core CPU")
.quantity(1)
.build()
.expect("Failed to create CPU component");
let ram_premium = ProductComponentBuilder::new()
.id(5)
.name("RAM")
.description("32GB RAM")
.quantity(1)
.build()
.expect("Failed to create RAM component");
let storage_premium = ProductComponentBuilder::new()
.id(6)
.name("Storage")
.description("1TB SSD")
.quantity(1)
.build()
.expect("Failed to create Storage component");
let gpu_premium = ProductComponentBuilder::new()
.id(7)
.name("GPU")
.description("Dedicated GPU")
.quantity(1)
.build()
.expect("Failed to create GPU component");
// Create Standard Node Product
let standard_node = ProductBuilder::new()
.id(1)
.name("Standard Server Node")
.description("Basic server node for general workloads")
.price(standard_price)
.type_(ProductType::Product)
.category("Servers")
.status(ProductStatus::Available)
.max_amount(100)
.validity_days(365)
.add_component(cpu_standard)
.add_component(ram_standard)
.add_component(storage_standard)
.build()
.expect("Failed to create Standard Node product");
// Create Premium Node Product
let premium_node = ProductBuilder::new()
.id(2)
.name("Premium Server Node")
.description("High-performance server node for demanding workloads")
.price(premium_price)
.type_(ProductType::Product)
.category("Servers")
.status(ProductStatus::Available)
.max_amount(50)
.validity_days(365)
.add_component(cpu_premium)
.add_component(ram_premium)
.add_component(storage_premium)
.add_component(gpu_premium)
.build()
.expect("Failed to create Premium Node product");
(standard_node, premium_node)
}
/// Check which products can be purchased
fn get_purchasable_products<'a>(products: &[&'a Product]) -> Vec<&'a Product> {
products.iter()
.filter(|p| p.is_purchasable())
.copied()
.collect()
}
/// Create a sale for a customer buying a product
fn create_sale(customer: &Customer, product: &Product) -> Sale {
let now = Utc::now();
let active_till = now + Duration::days(365);
// Create a sale item for the product
let sale_item = SaleItemBuilder::new()
.id(1)
.sale_id(1)
.product_id(Some(product.get_id()))
.name(product.name.clone())
.description(product.description.clone())
.comments("Customer requested expedited setup")
.quantity(1)
.unit_price(product.price.clone())
.tax_rate(10.0) // 10% tax rate
.active_till(active_till)
.build()
.expect("Failed to create sale item");
// Create the sale
let sale = SaleBuilder::new()
.id(1)
.company_id(101) // Assuming company ID 101
.customer_id(customer.get_id())
.buyer_name(customer.name.clone())
.buyer_email("contact@techcorp.com") // Example email
.currency_code(product.price.currency_code.clone())
.status(SaleStatus::Completed)
.add_item(sale_item)
.build()
.expect("Failed to create sale");
sale
}
/// Create an invoice for a sale
fn create_invoice(customer: &Customer, sale: &Sale) -> Invoice {
let now = Utc::now();
let due_date = now + Duration::days(30); // Due in 30 days
// Create an invoice item for the sale
let invoice_item = InvoiceItemBuilder::new()
.id(1)
.invoice_id(1)
.description(format!("Purchase of {}", sale.items[0].name))
.amount(sale.total_amount.clone())
.sale_id(sale.get_id())
.build()
.expect("Failed to create invoice item");
// Create the invoice
let invoice = InvoiceBuilder::new()
.id(1)
.customer_id(customer.get_id())
.currency_code(sale.total_amount.currency_code.clone())
.status(InvoiceStatus::Sent)
.issue_date(now)
.due_date(due_date)
.add_item(invoice_item)
.build()
.expect("Failed to create invoice");
invoice
}
/// Process a payment for an invoice
fn process_payment(invoice: &mut Invoice) {
// Create a payment for the full amount
let payment = Payment::new(
invoice.total_amount.clone(),
"Credit Card".to_string(),
"Payment received via credit card ending in 1234".to_string()
);
// Add the payment to the invoice
invoice.add_payment(payment);
// The invoice should now be marked as paid
assert_eq!(invoice.payment_status, PaymentStatus::Paid);
assert_eq!(invoice.status, InvoiceStatus::Paid);
}

View File

@@ -0,0 +1,7 @@
//! Business example for HeroDB
//!
//! This module demonstrates business models in action,
//! including products, sales, invoices, and payments.
// Include the main module directly
pub mod main;

View File

@@ -0,0 +1,368 @@
use chrono::{Utc, Duration};
use herodb::db::{DBBuilder, DB};
use herodb::models::gov::{
Company, CompanyStatus, BusinessType,
Shareholder, ShareholderType,
Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus,
User,
Vote, VoteOption, Ballot, VoteStatus,
Resolution, ResolutionStatus, Approval,
Committee, CommitteeRole, CommitteeMember
};
use std::path::PathBuf;
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("DB Example: Gov Module");
println!("============================");
// Create a temporary directory for the database
let db_path = PathBuf::from("/tmp/dbexample_gov");
if db_path.exists() {
fs::remove_dir_all(&db_path)?;
}
fs::create_dir_all(&db_path)?;
println!("Database path: {:?}", db_path);
// Create a database instance with our governance models registered
let db = DBBuilder::new(&db_path)
.register_model::<Company>()
.register_model::<Shareholder>()
.register_model::<Meeting>()
.register_model::<User>()
.register_model::<Vote>()
.register_model::<Resolution>()
.register_model::<Committee>()
.build()?;
println!("\n1. Creating a Company");
println!("-------------------");
// Create a company
let company = Company::new(
1,
"Acme Corporation".to_string(),
"ACM123456".to_string(),
Utc::now(),
"December 31".to_string(),
"info@acmecorp.com".to_string(),
"+1-555-123-4567".to_string(),
"https://acmecorp.com".to_string(),
"123 Main St, Anytown, USA".to_string(),
BusinessType::new(BusinessType::COOP.to_string())
.unwrap_or_else(|e| {
eprintln!("Warning: {}", e);
BusinessType::new_unchecked(BusinessType::COOP.to_string())
}),
"Technology".to_string(),
"A leading technology company".to_string(),
CompanyStatus::Active,
);
// Insert the company
db.set(&company)?;
println!("Company created: {} (ID: {})", company.name, company.id);
println!("Status: {:?}, Business Type: {}", company.status, company.business_type.as_str());
println!("\n2. Creating Users");
println!("---------------");
// Create users
let user1 = User::new(
1,
"John Doe".to_string(),
"john.doe@acmecorp.com".to_string(),
"password123".to_string(), // In a real app, this would be hashed
"Acme Corporation".to_string(),
"CEO".to_string(),
);
let user2 = User::new(
2,
"Jane Smith".to_string(),
"jane.smith@acmecorp.com".to_string(),
"password456".to_string(), // In a real app, this would be hashed
"Acme Corporation".to_string(),
"CFO".to_string(),
);
let user3 = User::new(
3,
"Bob Johnson".to_string(),
"bob.johnson@acmecorp.com".to_string(),
"password789".to_string(), // In a real app, this would be hashed
"Acme Corporation".to_string(),
"CTO".to_string(),
);
// Insert the users
db.set(&user1)?;
db.set(&user2)?;
db.set(&user3)?;
println!("User created: {} ({})", user1.name, user1.role);
println!("User created: {} ({})", user2.name, user2.role);
println!("User created: {} ({})", user3.name, user3.role);
println!("\n3. Creating Shareholders");
println!("----------------------");
// Create shareholders
let mut shareholder1 = Shareholder::new(
1,
company.id,
user1.id,
user1.name.clone(),
1000.0,
40.0,
ShareholderType::Individual,
);
let mut shareholder2 = Shareholder::new(
2,
company.id,
user2.id,
user2.name.clone(),
750.0,
30.0,
ShareholderType::Individual,
);
let mut shareholder3 = Shareholder::new(
3,
company.id,
user3.id,
user3.name.clone(),
750.0,
30.0,
ShareholderType::Individual,
);
// Insert the shareholders
db.set(&shareholder1)?;
db.set(&shareholder2)?;
db.set(&shareholder3)?;
println!("Shareholder created: {} ({} shares, {}%)",
shareholder1.name, shareholder1.shares, shareholder1.percentage);
println!("Shareholder created: {} ({} shares, {}%)",
shareholder2.name, shareholder2.shares, shareholder2.percentage);
println!("Shareholder created: {} ({} shares, {}%)",
shareholder3.name, shareholder3.shares, shareholder3.percentage);
// Update shareholder shares
shareholder1.update_shares(1100.0, 44.0);
db.set(&shareholder1)?;
println!("Updated shareholder: {} ({} shares, {}%)",
shareholder1.name, shareholder1.shares, shareholder1.percentage);
println!("\n4. Creating a Meeting");
println!("------------------");
// Create a meeting
let mut meeting = Meeting::new(
1,
company.id,
"Q2 Board Meeting".to_string(),
Utc::now() + Duration::days(7), // Meeting in 7 days
"Conference Room A".to_string(),
"Quarterly board meeting to discuss financial results".to_string(),
);
// Create attendees
let attendee1 = Attendee::new(
1,
meeting.id,
user1.id,
user1.name.clone(),
AttendeeRole::Coordinator,
);
let attendee2 = Attendee::new(
2,
meeting.id,
user2.id,
user2.name.clone(),
AttendeeRole::Member,
);
let attendee3 = Attendee::new(
3,
meeting.id,
user3.id,
user3.name.clone(),
AttendeeRole::Member,
);
// Add attendees to the meeting
meeting.add_attendee(attendee1);
meeting.add_attendee(attendee2);
meeting.add_attendee(attendee3);
// Insert the 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());
// Update attendee status
if let Some(attendee) = meeting.find_attendee_by_user_id_mut(user2.id) {
attendee.update_status(AttendeeStatus::Confirmed);
}
if let Some(attendee) = meeting.find_attendee_by_user_id_mut(user3.id) {
attendee.update_status(AttendeeStatus::Confirmed);
}
db.set(&meeting)?;
// Get confirmed attendees
let confirmed = meeting.confirmed_attendees();
println!("Confirmed attendees: {}", confirmed.len());
for attendee in confirmed {
println!(" - {} ({})", attendee.name, match attendee.role {
AttendeeRole::Coordinator => "Coordinator",
AttendeeRole::Member => "Member",
AttendeeRole::Secretary => "Secretary",
AttendeeRole::Participant => "Participant",
AttendeeRole::Advisor => "Advisor",
AttendeeRole::Admin => "Admin",
});
}
println!("\n5. Creating a Resolution");
println!("----------------------");
// Create a resolution
let mut resolution = Resolution::new(
1,
company.id,
"Approval of Q1 Financial Statements".to_string(),
"Resolution to approve the Q1 financial statements".to_string(),
"The Board of Directors hereby approves the financial statements for Q1 2025.".to_string(),
user1.id, // Proposed by the CEO
);
// Link the resolution to the meeting
resolution.link_to_meeting(meeting.id);
// Insert the resolution
db.set(&resolution)?;
println!("Resolution created: {} (Status: {:?})", resolution.title, resolution.status);
// Propose the resolution
resolution.propose();
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.set(&resolution)?;
// Check approval status
println!("Approvals: {}, Rejections: {}",
resolution.approval_count(),
resolution.rejection_count());
// Approve the resolution
resolution.approve();
db.set(&resolution)?;
println!("Resolution approved on {}",
resolution.approved_at.unwrap().format("%Y-%m-%d"));
println!("\n6. Creating a Vote");
println!("----------------");
// Create a vote
let mut vote = Vote::new(
1,
company.id,
"Vote on New Product Line".to_string(),
"Vote to approve investment in new product line".to_string(),
Utc::now(),
Utc::now() + Duration::days(3), // Voting period of 3 days
VoteStatus::Open,
);
// Add voting options
vote.add_option("Approve".to_string(), 0);
vote.add_option("Reject".to_string(), 0);
vote.add_option("Abstain".to_string(), 0);
// Insert the vote
db.set(&vote)?;
println!("Vote created: {} (Status: {:?})", vote.title, vote.status);
println!("Voting period: {} to {}",
vote.start_date.format("%Y-%m-%d"),
vote.end_date.format("%Y-%m-%d"));
// Cast ballots
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.set(&vote)?;
// Check voting results
println!("Voting results:");
for option in &vote.options {
println!(" - {}: {} votes", option.text, option.count);
}
// Create a resolution for this vote
let mut vote_resolution = Resolution::new(
2,
company.id,
"Investment in New Product Line".to_string(),
"Resolution to approve investment in new product line".to_string(),
"The Board of Directors hereby approves an investment of $1,000,000 in the new product line.".to_string(),
user1.id, // Proposed by the CEO
);
// Link the resolution to the vote
vote_resolution.link_to_vote(vote.id);
vote_resolution.propose();
db.set(&vote_resolution)?;
println!("Created resolution linked to vote: {}", vote_resolution.title);
println!("\n7. Retrieving Related Objects");
println!("---------------------------");
// Retrieve company and related objects
let retrieved_company = db.get::<Company>(company.id)?;
println!("Company: {} (ID: {})", retrieved_company.name, retrieved_company.id);
// Get resolutions for this company
let company_resolutions = retrieved_company.get_resolutions(&db)?;
println!("Company has {} resolutions:", company_resolutions.len());
for res in company_resolutions {
println!(" - {} (Status: {:?})", res.title, res.status);
}
// Get meeting and its resolutions
let retrieved_meeting = db.get::<Meeting>(meeting.id)?;
println!("Meeting: {} ({})", retrieved_meeting.title, retrieved_meeting.date.format("%Y-%m-%d"));
let meeting_resolutions = retrieved_meeting.get_resolutions(&db)?;
println!("Meeting has {} resolutions:", meeting_resolutions.len());
for res in meeting_resolutions {
println!(" - {} (Status: {:?})", res.title, res.status);
}
// Get vote and its resolution
let retrieved_vote = db.get::<Vote>(vote.id)?;
println!("Vote: {} (Status: {:?})", retrieved_vote.title, retrieved_vote.status);
if let Ok(Some(vote_res)) = retrieved_vote.get_resolution(&db) {
println!("Vote is linked to resolution: {}", vote_res.title);
}
// Get resolution and its related objects
let retrieved_resolution = db.get::<Resolution>(resolution.id)?;
println!("Resolution: {} (Status: {:?})", retrieved_resolution.title, retrieved_resolution.status);
if let Ok(Some(res_meeting)) = retrieved_resolution.get_meeting(&db) {
println!("Resolution is discussed in meeting: {}", res_meeting.title);
}
println!("\nExample completed successfully!");
Ok(())
}

View File

@@ -0,0 +1,399 @@
use chrono::{Utc, Duration};
use herodb::db::{DBBuilder, GetId};
use herodb::models::mcc::{
Calendar, Event,
Email, Attachment, Envelope,
Contact, Message
};
use herodb::models::circle::Circle;
use std::path::PathBuf;
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("DB Example MCC: Mail, Calendar, Contacts with Group Support");
println!("=======================================================");
// Create a temporary directory for the database
let db_path = PathBuf::from("/tmp/dbexample_mcc");
if db_path.exists() {
fs::remove_dir_all(&db_path)?;
}
fs::create_dir_all(&db_path)?;
println!("Database path: {:?}", db_path);
// Create a database instance with our models registered
let db = DBBuilder::new(&db_path)
.register_type::<Calendar>("calendar")
.register_type::<Event>("event")
.register_type::<Email>("email")
.register_type::<Contact>("contact")
.register_type::<Message>("message")
.register_model::<Circle>() // Circle still uses the Model trait
.build()?;
println!("\n1. Creating Circles (Groups)");
println!("---------------------------");
// Create circles (groups)
let work_circle = Circle::new(
1,
"Work".to_string(),
"Work-related communications".to_string()
);
let family_circle = Circle::new(
2,
"Family".to_string(),
"Family communications".to_string()
);
let friends_circle = Circle::new(
3,
"Friends".to_string(),
"Friends communications".to_string()
);
// Insert circles
db.set::<Circle>(&work_circle)?;
db.set::<Circle>(&family_circle)?;
db.set::<Circle>(&friends_circle)?;
println!("Created circles:");
println!(" - Circle #{}: {}", work_circle.id, work_circle.name);
println!(" - Circle #{}: {}", family_circle.id, family_circle.name);
println!(" - Circle #{}: {}", friends_circle.id, friends_circle.name);
println!("\n2. Creating Contacts with Group Support");
println!("------------------------------------");
// Create contacts
let mut john = Contact::new(
1,
"John".to_string(),
"Doe".to_string(),
"john.doe@example.com".to_string(),
"work".to_string()
);
john.add_group(work_circle.id);
let mut alice = Contact::new(
2,
"Alice".to_string(),
"Smith".to_string(),
"alice.smith@example.com".to_string(),
"family".to_string()
);
alice.add_group(family_circle.id);
let mut bob = Contact::new(
3,
"Bob".to_string(),
"Johnson".to_string(),
"bob.johnson@example.com".to_string(),
"friends".to_string()
);
bob.add_group(friends_circle.id);
bob.add_group(work_circle.id); // Bob is both a friend and a work contact
// Insert contacts
db.set_any::<Contact>(&john, "contact")?;
db.set_any::<Contact>(&alice, "contact")?;
db.set_any::<Contact>(&bob, "contact")?;
println!("Created contacts:");
println!(" - {}: {} (Groups: {:?})", john.full_name(), john.email, john.groups);
println!(" - {}: {} (Groups: {:?})", alice.full_name(), alice.email, alice.groups);
println!(" - {}: {} (Groups: {:?})", bob.full_name(), bob.email, bob.groups);
println!("\n3. Creating Calendars with Group Support");
println!("-------------------------------------");
// Create calendars
let mut work_calendar = Calendar::new(
1,
"Work Calendar".to_string(),
"Work-related events".to_string()
);
work_calendar.add_group(work_circle.id);
let mut personal_calendar = Calendar::new(
2,
"Personal Calendar".to_string(),
"Personal events".to_string()
);
personal_calendar.add_group(family_circle.id);
personal_calendar.add_group(friends_circle.id);
// Insert calendars
db.set_any::<Calendar>(&work_calendar, "calendar")?;
db.set_any::<Calendar>(&personal_calendar, "calendar")?;
println!("Created calendars:");
println!(" - {}: {} (Groups: {:?})", work_calendar.id, work_calendar.title, work_calendar.groups);
println!(" - {}: {} (Groups: {:?})", personal_calendar.id, personal_calendar.title, personal_calendar.groups);
println!("\n4. Creating Events with Group Support");
println!("----------------------------------");
// Create events
let now = Utc::now();
let tomorrow = now + Duration::days(1);
let next_week = now + Duration::days(7);
let mut work_meeting = Event::new(
1,
work_calendar.id,
"Team Meeting".to_string(),
"Weekly team sync".to_string(),
"Conference Room A".to_string(),
tomorrow,
tomorrow + Duration::hours(1),
"organizer@example.com".to_string()
);
work_meeting.add_group(work_circle.id);
work_meeting.add_attendee(john.email.clone());
work_meeting.add_attendee(bob.email.clone());
let mut family_dinner = Event::new(
2,
personal_calendar.id,
"Family Dinner".to_string(),
"Weekly family dinner".to_string(),
"Home".to_string(),
next_week,
next_week + Duration::hours(2),
"me@example.com".to_string()
);
family_dinner.add_group(family_circle.id);
family_dinner.add_attendee(alice.email.clone());
// Insert events
db.set_any::<Event>(&work_meeting, "event")?;
db.set_any::<Event>(&family_dinner, "event")?;
println!("Created events:");
println!(" - {}: {} on {} (Groups: {:?})",
work_meeting.id,
work_meeting.title,
work_meeting.start_time.format("%Y-%m-%d %H:%M"),
work_meeting.groups
);
println!(" - {}: {} on {} (Groups: {:?})",
family_dinner.id,
family_dinner.title,
family_dinner.start_time.format("%Y-%m-%d %H:%M"),
family_dinner.groups
);
println!("\n5. Creating Emails with Group Support");
println!("----------------------------------");
// Create emails
let mut work_email = Email::new(
1,
101,
1,
"INBOX".to_string(),
"Here are the meeting notes from yesterday's discussion.".to_string()
);
work_email.add_group(work_circle.id);
let work_attachment = Attachment {
filename: "meeting_notes.pdf".to_string(),
content_type: "application/pdf".to_string(),
hash: "abc123def456".to_string(),
size: 1024,
};
work_email.add_attachment(work_attachment);
let work_envelope = Envelope {
date: now.timestamp(),
subject: "Meeting Notes".to_string(),
from: vec!["john.doe@example.com".to_string()],
sender: vec!["john.doe@example.com".to_string()],
reply_to: vec!["john.doe@example.com".to_string()],
to: vec!["me@example.com".to_string()],
cc: vec!["bob.johnson@example.com".to_string()],
bcc: vec![],
in_reply_to: "".to_string(),
message_id: "msg123@example.com".to_string(),
};
work_email.set_envelope(work_envelope);
let mut family_email = Email::new(
2,
102,
2,
"INBOX".to_string(),
"Looking forward to seeing you at dinner next week!".to_string()
);
family_email.add_group(family_circle.id);
let family_envelope = Envelope {
date: now.timestamp(),
subject: "Family Dinner".to_string(),
from: vec!["alice.smith@example.com".to_string()],
sender: vec!["alice.smith@example.com".to_string()],
reply_to: vec!["alice.smith@example.com".to_string()],
to: vec!["me@example.com".to_string()],
cc: vec![],
bcc: vec![],
in_reply_to: "".to_string(),
message_id: "msg456@example.com".to_string(),
};
family_email.set_envelope(family_envelope);
// Insert emails
db.set_any::<Email>(&work_email, "email")?;
db.set_any::<Email>(&family_email, "email")?;
println!("Created emails:");
println!(" - From: {}, Subject: {} (Groups: {:?})",
work_email.envelope.as_ref().unwrap().from[0],
work_email.envelope.as_ref().unwrap().subject,
work_email.groups
);
println!(" - From: {}, Subject: {} (Groups: {:?})",
family_email.envelope.as_ref().unwrap().from[0],
family_email.envelope.as_ref().unwrap().subject,
family_email.groups
);
println!("\n6. Creating Messages (Chat) with Group Support");
println!("-----------------------------------------");
// Create messages
let mut work_chat = Message::new(
1,
"thread_work_123".to_string(),
"john.doe@example.com".to_string(),
"Can we move the meeting to 3pm?".to_string()
);
work_chat.add_group(work_circle.id);
work_chat.add_recipient("me@example.com".to_string());
work_chat.add_recipient("bob.johnson@example.com".to_string());
let mut friends_chat = Message::new(
2,
"thread_friends_456".to_string(),
"bob.johnson@example.com".to_string(),
"Are we still on for the game this weekend?".to_string()
);
friends_chat.add_group(friends_circle.id);
friends_chat.add_recipient("me@example.com".to_string());
friends_chat.add_reaction("👍".to_string());
// Insert messages
db.set_any::<Message>(&work_chat, "message")?;
db.set_any::<Message>(&friends_chat, "message")?;
println!("Created messages:");
println!(" - From: {}, Content: {} (Groups: {:?})",
work_chat.sender_id,
work_chat.content,
work_chat.groups
);
println!(" - From: {}, Content: {} (Groups: {:?}, Reactions: {:?})",
friends_chat.sender_id,
friends_chat.content,
friends_chat.groups,
friends_chat.meta.reactions
);
println!("\n7. Demonstrating Utility Methods");
println!("------------------------------");
// Filter contacts by group
println!("\nFiltering contacts by work group (ID: {}):", work_circle.id);
let all_contacts = db.list_any::<Contact>()?;
for contact in all_contacts {
if contact.filter_by_groups(&[work_circle.id]) {
println!(" - {} ({})", contact.full_name(), contact.email);
}
}
// Search emails by subject
println!("\nSearching emails with subject containing 'Meeting':");
let all_emails = db.list_any::<Email>()?;
for email in all_emails {
if email.search_by_subject("Meeting") {
println!(" - Subject: {}, From: {}",
email.envelope.as_ref().unwrap().subject,
email.envelope.as_ref().unwrap().from[0]
);
}
}
// Get events for a calendar
println!("\nGetting events for Work Calendar (ID: {}):", work_calendar.id);
let all_events = db.list_any::<Event>()?;
let work_events: Vec<Event> = all_events
.into_iter()
.filter(|event| event.calendar_id == work_calendar.id)
.collect();
for event in work_events {
println!(" - {}: {} on {}",
event.id,
event.title,
event.start_time.format("%Y-%m-%d %H:%M")
);
}
// Get attendee contacts for an event
println!("\nGetting attendee contacts for Team Meeting (ID: {}):", work_meeting.id);
let all_contacts = db.list_any::<Contact>()?;
let attendee_contacts: Vec<Contact> = all_contacts
.into_iter()
.filter(|contact| work_meeting.attendees.contains(&contact.email))
.collect();
for contact in attendee_contacts {
println!(" - {} ({})", contact.full_name(), contact.email);
}
// Convert email to message
println!("\nConverting work email to message:");
let email_to_message = work_email.to_message(3, "thread_converted_789".to_string());
println!(" - Original Email Subject: {}", work_email.envelope.as_ref().unwrap().subject);
println!(" - Converted Message Content: {}", email_to_message.content.split('\n').next().unwrap_or(""));
println!(" - Converted Message Groups: {:?}", email_to_message.groups);
// Insert the converted message
db.set_any::<Message>(&email_to_message, "message")?;
println!("\n8. Relationship Management");
println!("------------------------");
// Get the calendar for an event
println!("\nGetting calendar for Family Dinner event (ID: {}):", family_dinner.id);
let event_calendar = db.get_any::<Calendar>(family_dinner.calendar_id)?;
println!(" - Calendar: {} ({})", event_calendar.title, event_calendar.description);
// Get events for a contact
println!("\nGetting events where John Doe is an attendee:");
let all_events = db.list_any::<Event>()?;
let john_events: Vec<Event> = all_events
.into_iter()
.filter(|event| event.attendees.contains(&john.email))
.collect();
for event in john_events {
println!(" - {}: {} on {}",
event.id,
event.title,
event.start_time.format("%Y-%m-%d %H:%M")
);
}
// Get messages in the same thread
println!("\nGetting all messages in the work chat thread:");
let all_messages = db.list_any::<Message>()?;
let thread_messages: Vec<Message> = all_messages
.into_iter()
.filter(|message| message.thread_id == work_chat.thread_id)
.collect();
for message in thread_messages {
println!(" - From: {}, Content: {}", message.sender_id, message.content);
}
println!("\nExample completed successfully!");
Ok(())
}

View File

@@ -0,0 +1,218 @@
use chrono::{DateTime, Duration, Utc};
use herodb::db::{DB, DBBuilder};
use herodb::models::biz::{
Currency, CurrencyBuilder, Product, ProductBuilder, ProductComponent, ProductComponentBuilder,
ProductStatus, ProductType, Sale, SaleBuilder, SaleItem, SaleItemBuilder, SaleStatus,
};
use rhai::{Engine, packages::Package};
use std::fs;
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Note: This example has been modified to work with the current API
println!("DB Example 2: Using Builder Pattern and Model-Specific Methods");
println!("============================================================");
// Create a temporary directory for the database
let db_path = PathBuf::from("/tmp/dbexample_prod");
if db_path.exists() {
fs::remove_dir_all(&db_path)?;
}
fs::create_dir_all(&db_path)?;
println!("Database path: {:?}", db_path);
// Skip the Rhai engine part as it has compatibility issues
println!("\nSkipping Rhai engine part due to compatibility issues");
// Create a database instance with our models registered
let mut db = DBBuilder::new(&db_path)
.register_model::<Product>()
.register_model::<Currency>()
.register_model::<Sale>()
.build()?;
println!("\n1. Creating Products with Builder Pattern");
println!("----------------------------------------");
// Create a currency using the builder
let usd = CurrencyBuilder::new()
.id(1) // Add the required ID
.amount(0.0) // Initial amount
.currency_code("USD")
.build()?;
// Insert the currency
db.insert_currency(usd.clone())?;
println!("Currency created: ${:.2} {}", usd.amount, usd.currency_code);
// Create product components using the builder
let component1 = ProductComponentBuilder::new()
.id(101)
.name("Basic Support")
.description("24/7 email support")
.quantity(1)
.build()?;
let component2 = ProductComponentBuilder::new()
.id(102)
.name("Premium Support")
.description("24/7 phone and email support")
.quantity(1)
.build()?;
// Create products using the builder
let product1 = ProductBuilder::new()
.id(1)
.name("Standard Plan")
.description("Our standard service offering")
.price(
CurrencyBuilder::new()
.id(2) // Add ID
.amount(29.99)
.currency_code("USD")
.build()?,
)
.type_(ProductType::Service)
.category("Subscription")
.status(ProductStatus::Available)
.max_amount(1000)
.validity_days(30)
.add_component(component1)
.build()?;
let product2 = ProductBuilder::new()
.id(2)
.name("Premium Plan")
.description("Our premium service offering with priority support")
.price(
CurrencyBuilder::new()
.id(3) // Add ID
.amount(99.99)
.currency_code("USD")
.build()?,
)
.type_(ProductType::Service)
.category("Subscription")
.status(ProductStatus::Available)
.max_amount(500)
.validity_days(30)
.add_component(component2)
.build()?;
// Insert products using model-specific methods
db.insert_product(product1.clone())?;
db.insert_product(product2.clone())?;
println!(
"Product created: {} (${:.2})",
product1.name, product1.price.amount
);
println!(
"Product created: {} (${:.2})",
product2.name, product2.price.amount
);
println!("\n2. Retrieving Products");
println!("--------------------");
// Retrieve products using model-specific methods
let retrieved_product1 = db.get_product(1)?;
println!(
"Retrieved: {} (${:.2})",
retrieved_product1.name, retrieved_product1.price.amount
);
println!("Components:");
for component in &retrieved_product1.components {
println!(" - {} ({})", component.name, component.description);
}
println!("\n3. Listing All Products");
println!("----------------------");
// List all products using model-specific methods
let all_products = db.list_products()?;
println!("Found {} products:", all_products.len());
for product in all_products {
println!(
" - {} (${:.2}, {})",
product.name,
product.price.amount,
if product.is_purchasable() {
"Available"
} else {
"Unavailable"
}
);
}
println!("\n4. Creating a Sale");
println!("-----------------");
// Create a sale using the builder
let now = Utc::now();
let item1 = SaleItemBuilder::new()
.id(201)
.sale_id(1)
.product_id(Some(1))
.name("Standard Plan")
.quantity(1)
.unit_price(
CurrencyBuilder::new()
.id(4) // Add ID
.amount(29.99)
.currency_code("USD")
.build()?,
)
.active_till(now + Duration::days(30))
.build()?;
let sale = SaleBuilder::new()
.id(1)
.company_id(101)
.customer_id(123)
.currency_code("USD")
.status(SaleStatus::Pending)
.add_item(item1)
.build()?;
// Insert the sale using model-specific methods
db.insert_sale(sale.clone())?;
println!(
"Sale created: #{} for customer #{} (${:.2})",
sale.id, sale.customer_id, sale.total_amount.amount
);
println!("\n5. Updating a Sale");
println!("-----------------");
// Retrieve the sale, update it, and save it back
let mut retrieved_sale = db.get_sale(1)?;
println!(
"Retrieved sale: #{} with status {:?}",
retrieved_sale.id, retrieved_sale.status
);
// Update the status
retrieved_sale.update_status(SaleStatus::Completed);
db.insert_sale(retrieved_sale.clone())?;
println!("Updated sale status to {:?}", retrieved_sale.status);
println!("\n6. Deleting Objects");
println!("------------------");
// Delete a product
db.delete_product(2)?;
println!("Deleted product #2");
// List remaining products
let remaining_products = db.list_products()?;
println!("Remaining products: {}", remaining_products.len());
for product in remaining_products {
println!(" - {}", product.name);
}
println!("\nExample completed successfully!");
Ok(())
}

View File

@@ -0,0 +1,7 @@
//! Command examples for HeroDB
//!
//! This module contains various example commands and applications
//! that demonstrate how to use HeroDB in different scenarios.
// Export the example modules
// pub mod dbexample_biz; // Commented out to avoid circular imports