update grid4 & heroledger models

This commit is contained in:
Timur Gordon
2025-09-16 14:18:08 +02:00
parent cb1fb0f0ec
commit 53e9a2d4f0
31 changed files with 3216 additions and 399 deletions

View File

@@ -1,10 +1,25 @@
use chrono::{Duration, Utc};
use chrono::{Duration, Utc, NaiveDateTime};
use heromodels::db::{Collection, Db};
use heromodels::models::User;
use heromodels::models::calendar::{AttendanceStatus, Attendee, Calendar, Event, EventStatus};
use heromodels_core::Model;
fn main() {
// Helper to format i64 timestamps
let fmt_time = |ts: i64| -> String {
let ndt = NaiveDateTime::from_timestamp_opt(ts, 0)
.unwrap_or(NaiveDateTime::from_timestamp_opt(0, 0).unwrap());
chrono::DateTime::<Utc>::from_utc(ndt, Utc)
.format("%Y-%m-%d %H:%M")
.to_string()
};
let fmt_date = |ts: i64| -> String {
let ndt = NaiveDateTime::from_timestamp_opt(ts, 0)
.unwrap_or(NaiveDateTime::from_timestamp_opt(0, 0).unwrap());
chrono::DateTime::<Utc>::from_utc(ndt, Utc)
.format("%Y-%m-%d")
.to_string()
};
// Create a new DB instance, reset before every run
let db_path = "/tmp/ourdb_calendar_example";
let db = heromodels::db::hero::OurDB::new(db_path, true).expect("Can create DB");
@@ -47,50 +62,21 @@ fn main() {
println!("- User 2 (ID: {}): {}", user2_id, stored_user2.full_name);
println!("- User 3 (ID: {}): {}", user3_id, stored_user3.full_name);
// --- Create Attendees ---
// --- Create Attendees (embedded in events, not stored separately) ---
println!("\n--- Creating Attendees ---");
let attendee1 = Attendee::new(user1_id).status(AttendanceStatus::Accepted);
let attendee2 = Attendee::new(user2_id).status(AttendanceStatus::Tentative);
let attendee3 = Attendee::new(user3_id); // Default NoResponse
// Store attendees in database and get their IDs
let attendee_collection = db
.collection::<Attendee>()
.expect("can open attendee collection");
let (attendee1_id, stored_attendee1) = attendee_collection
.set(&attendee1)
.expect("can set attendee1");
let (attendee2_id, stored_attendee2) = attendee_collection
.set(&attendee2)
.expect("can set attendee2");
let (attendee3_id, stored_attendee3) = attendee_collection
.set(&attendee3)
.expect("can set attendee3");
println!("Created attendees:");
println!(
"- Attendee 1 (ID: {}): Contact ID {}, Status: {:?}",
attendee1_id, stored_attendee1.contact_id, stored_attendee1.status
);
println!(
"- Attendee 2 (ID: {}): Contact ID {}, Status: {:?}",
attendee2_id, stored_attendee2.contact_id, stored_attendee2.status
);
println!(
"- Attendee 3 (ID: {}): Contact ID {}, Status: {:?}",
attendee3_id, stored_attendee3.contact_id, stored_attendee3.status
);
// --- Create Events with Attendees ---
println!("\n--- Creating Events with Enhanced Features ---");
let now = Utc::now();
let event1_start = (now + Duration::hours(1)).timestamp();
let event1_end = (now + Duration::hours(2)).timestamp();
let event1 = Event::new(
"Team Meeting",
now + Duration::hours(1),
now + Duration::hours(2),
)
let event1 = Event::new()
.title("Team Meeting")
.reschedule(event1_start, event1_end)
.description("Weekly sync-up meeting to discuss project progress.")
.location("Conference Room A")
.color("#FF5722") // Red-orange color
@@ -99,14 +85,14 @@ fn main() {
.category("Work")
.reminder_minutes(15)
.timezone("UTC")
.add_attendee(attendee1_id)
.add_attendee(attendee2_id);
.add_attendee(attendee1.clone())
.add_attendee(attendee2.clone());
let event2 = Event::new(
"Project Brainstorm",
now + Duration::days(1),
now + Duration::days(1) + Duration::minutes(90),
)
let event2_start = (now + Duration::days(1)).timestamp();
let event2_end = (now + Duration::days(1) + Duration::minutes(90)).timestamp();
let event2 = Event::new()
.title("Project Brainstorm")
.reschedule(event2_start, event2_end)
.description("Brainstorming session for new project features.")
.location("Innovation Lab")
.color("#4CAF50") // Green color
@@ -115,28 +101,28 @@ fn main() {
.category("Planning")
.reminder_minutes(30)
.is_recurring(true)
.add_attendee(attendee1_id)
.add_attendee(attendee3_id);
.add_attendee(attendee1.clone())
.add_attendee(attendee3.clone());
let event3 = Event::new(
"Client Call",
now + Duration::days(2),
now + Duration::days(2) + Duration::hours(1),
)
let event3_start = (now + Duration::days(2)).timestamp();
let event3_end = (now + Duration::days(2) + Duration::hours(1)).timestamp();
let event3 = Event::new()
.title("Client Call")
.reschedule(event3_start, event3_end)
.description("Quarterly review with key client.")
.color("#9C27B0") // Purple color
.created_by(user3_id)
.status(EventStatus::Published)
.category("Client")
.reminder_minutes(60)
.add_attendee(attendee2_id);
.add_attendee(attendee2.clone());
// Create an all-day event
let event4 = Event::new(
"Company Holiday",
now + Duration::days(7),
now + Duration::days(7) + Duration::hours(24),
)
let event4_start = (now + Duration::days(7)).timestamp();
let event4_end = (now + Duration::days(7) + Duration::hours(24)).timestamp();
let event4 = Event::new()
.title("Company Holiday")
.reschedule(event4_start, event4_end)
.description("National holiday - office closed.")
.color("#FFC107") // Amber color
.all_day(true)
@@ -148,7 +134,7 @@ fn main() {
println!(
"- Event 1: '{}' at {} with {} attendees",
event1.title,
event1.start_time.format("%Y-%m-%d %H:%M"),
fmt_time(event1.start_time),
event1.attendees.len()
);
println!(
@@ -174,12 +160,19 @@ fn main() {
);
println!(" All-day: {}", event1.all_day);
println!(" Recurring: {}", event1.is_recurring);
println!(" Attendee IDs: {:?}", event1.attendees);
println!(
" Attendee IDs: {:?}",
event1
.attendees
.iter()
.map(|a| a.contact_id)
.collect::<Vec<u32>>()
);
println!(
"- Event 2: '{}' at {} with {} attendees",
event2.title,
event2.start_time.format("%Y-%m-%d %H:%M"),
fmt_time(event2.start_time),
event2.attendees.len()
);
println!(
@@ -205,12 +198,19 @@ fn main() {
);
println!(" All-day: {}", event2.all_day);
println!(" Recurring: {}", event2.is_recurring);
println!(" Attendee IDs: {:?}", event2.attendees);
println!(
" Attendee IDs: {:?}",
event2
.attendees
.iter()
.map(|a| a.contact_id)
.collect::<Vec<u32>>()
);
println!(
"- Event 3: '{}' at {} with {} attendees",
event3.title,
event3.start_time.format("%Y-%m-%d %H:%M"),
fmt_time(event3.start_time),
event3.attendees.len()
);
println!(
@@ -236,12 +236,19 @@ fn main() {
);
println!(" All-day: {}", event3.all_day);
println!(" Recurring: {}", event3.is_recurring);
println!(" Attendee IDs: {:?}", event3.attendees);
println!(
" Attendee IDs: {:?}",
event3
.attendees
.iter()
.map(|a| a.contact_id)
.collect::<Vec<u32>>()
);
println!(
"- Event 4: '{}' at {} (All-day: {})",
event4.title,
event4.start_time.format("%Y-%m-%d"),
fmt_date(event4.start_time),
event4.all_day
);
println!(
@@ -262,25 +269,37 @@ fn main() {
let new_start = now + Duration::hours(2);
let new_end = now + Duration::hours(3);
let mut updated_event1 = event1.clone();
updated_event1 = updated_event1.reschedule(new_start, new_end);
updated_event1 = updated_event1.reschedule(new_start.timestamp(), new_end.timestamp());
println!(
"Rescheduled '{}' to {}",
updated_event1.title,
new_start.format("%Y-%m-%d %H:%M")
fmt_time(new_start.timestamp())
);
// Remove an attendee
updated_event1 = updated_event1.remove_attendee(attendee1_id);
updated_event1 = updated_event1.remove_attendee(user1_id);
println!(
"Removed attendee {} from '{}'. Remaining attendee IDs: {:?}",
attendee1_id, updated_event1.title, updated_event1.attendees
user1_id,
updated_event1.title,
updated_event1
.attendees
.iter()
.map(|a| a.contact_id)
.collect::<Vec<u32>>()
);
// Add a new attendee
updated_event1 = updated_event1.add_attendee(attendee3_id);
updated_event1 = updated_event1.add_attendee(attendee3.clone());
println!(
"Added attendee {} to '{}'. Current attendee IDs: {:?}",
attendee3_id, updated_event1.title, updated_event1.attendees
user3_id,
updated_event1.title,
updated_event1
.attendees
.iter()
.map(|a| a.contact_id)
.collect::<Vec<u32>>()
);
// --- Demonstrate Event Status Changes ---
@@ -300,11 +319,11 @@ fn main() {
println!("Cancelled event: '{}'", cancelled_event.title);
// Update event with new features
let enhanced_event = Event::new(
"Enhanced Meeting",
now + Duration::days(5),
now + Duration::days(5) + Duration::hours(2),
)
let enhanced_start = (now + Duration::days(5)).timestamp();
let enhanced_end = (now + Duration::days(5) + Duration::hours(2)).timestamp();
let enhanced_event = Event::new()
.title("Enhanced Meeting")
.reschedule(enhanced_start, enhanced_end)
.description("Meeting with all new features demonstrated.")
.location("Virtual - Zoom")
.color("#673AB7") // Deep purple
@@ -314,9 +333,9 @@ fn main() {
.reminder_minutes(45)
.timezone("America/New_York")
.is_recurring(true)
.add_attendee(attendee1_id)
.add_attendee(attendee2_id)
.add_attendee(attendee3_id);
.add_attendee(attendee1)
.add_attendee(attendee2)
.add_attendee(attendee3);
println!("Created enhanced event with all features:");
println!(" Title: {}", enhanced_event.title);
@@ -485,13 +504,13 @@ fn main() {
println!("\n--- Modifying Calendar ---");
// Create and store a new event
let new_event = Event::new(
"1-on-1 Meeting",
now + Duration::days(3),
now + Duration::days(3) + Duration::minutes(30),
)
.description("One-on-one meeting with team member.")
.location("Office");
let ne_start = (now + Duration::days(3)).timestamp();
let ne_end = (now + Duration::days(3) + Duration::minutes(30)).timestamp();
let new_event = Event::new()
.title("1-on-1 Meeting")
.reschedule(ne_start, ne_end)
.description("One-on-one meeting with team member.")
.location("Office");
let (new_event_id, _stored_new_event) =
event_collection.set(&new_event).expect("can set new event");
@@ -565,7 +584,7 @@ fn main() {
"- Event ID: {}, Title: '{}', Start: {}, Attendees: {}",
event.get_id(),
event.title,
event.start_time.format("%Y-%m-%d %H:%M"),
fmt_time(event.start_time),
event.attendees.len()
);
}
@@ -583,22 +602,16 @@ fn main() {
retrieved_event1.attendees.len()
);
// Look up attendee details for each attendee ID
for &attendee_id in &retrieved_event1.attendees {
if let Some(attendee) = attendee_collection
.get_by_id(attendee_id)
.expect("can try to get attendee")
// Look up attendee details directly from embedded attendees
for attendee in &retrieved_event1.attendees {
if let Some(user) = user_collection
.get_by_id(attendee.contact_id)
.expect("can try to get user")
{
// Look up user details for the attendee's contact_id
if let Some(user) = user_collection
.get_by_id(attendee.contact_id)
.expect("can try to get user")
{
println!(
" - Attendee ID {}: {} (User: {}, Status: {:?})",
attendee_id, user.full_name, attendee.contact_id, attendee.status
);
}
println!(
" - User {}: {} (Status: {:?})",
attendee.contact_id, user.full_name, attendee.status
);
}
}
}

View File

@@ -0,0 +1,199 @@
use heromodels::db::{Collection, Db};
use heromodels::models::grid4::{Bid, BidStatus, BillingPeriod};
use heromodels::models::grid4::bid::bid_index::customer_id;
use heromodels_core::Model;
// Helper function to print bid details
fn print_bid_details(bid: &Bid) {
println!("\n--- Bid Details ---");
println!("ID: {}", bid.get_id());
println!("Customer ID: {}", bid.customer_id);
println!("Compute Slices: {}", bid.compute_slices_nr);
println!("Compute Slice Price: ${:.2}", bid.compute_slice_price);
println!("Storage Slices: {}", bid.storage_slices_nr);
println!("Storage Slice Price: ${:.2}", bid.storage_slice_price);
println!("Status: {:?}", bid.status);
println!("Obligation: {}", bid.obligation);
println!("Start Date: {}", bid.start_date);
println!("End Date: {}", bid.end_date);
println!("Billing Period: {:?}", bid.billing_period);
println!("Signature User: {}", bid.signature_user);
println!("Created At: {}", bid.base_data.created_at);
println!("Modified At: {}", bid.base_data.modified_at);
}
fn main() {
// Create a new DB instance in /tmp/grid4_db, and reset before every run
let db = heromodels::db::hero::OurDB::new("/tmp/grid4_db", true).expect("Can create DB");
println!("Grid4 Bid Models - Basic Usage Example");
println!("=====================================");
// Create bids with different configurations
// Bid 1 - Small compute request
let bid1 = Bid::new()
.customer_id(101)
.compute_slices_nr(4)
.compute_slice_price(0.05)
.storage_slices_nr(10)
.storage_slice_price(0.02)
.status(BidStatus::Pending)
.obligation(false)
.start_date(1640995200) // 2022-01-01
.end_date(1672531200) // 2023-01-01
.billing_period(BillingPeriod::Monthly)
.signature_user("sig_user_101_abc123".to_string());
// Bid 2 - Large compute request with obligation
let bid2 = Bid::new()
.customer_id(102)
.compute_slices_nr(16)
.compute_slice_price(0.04)
.storage_slices_nr(50)
.storage_slice_price(0.015)
.status(BidStatus::Confirmed)
.obligation(true)
.start_date(1640995200)
.end_date(1704067200) // 2024-01-01
.billing_period(BillingPeriod::Yearly)
.signature_user("sig_user_102_def456".to_string());
// Bid 3 - Storage-heavy request
let bid3 = Bid::new()
.customer_id(103)
.compute_slices_nr(2)
.compute_slice_price(0.06)
.storage_slices_nr(100)
.storage_slice_price(0.01)
.status(BidStatus::Assigned)
.obligation(true)
.start_date(1640995200)
.end_date(1672531200)
.billing_period(BillingPeriod::Hourly)
.signature_user("sig_user_103_ghi789".to_string());
// Bid 4 - Cancelled bid
let bid4 = Bid::new()
.customer_id(104)
.compute_slices_nr(8)
.compute_slice_price(0.055)
.storage_slices_nr(25)
.storage_slice_price(0.018)
.status(BidStatus::Cancelled)
.obligation(false)
.start_date(1640995200)
.end_date(1672531200)
.billing_period(BillingPeriod::Monthly)
.signature_user("sig_user_104_jkl012".to_string());
// Save all bids to database and get their assigned IDs and updated models
let (bid1_id, db_bid1) = db
.collection()
.expect("can open bid collection")
.set(&bid1)
.expect("can set bid");
let (bid2_id, db_bid2) = db
.collection()
.expect("can open bid collection")
.set(&bid2)
.expect("can set bid");
let (bid3_id, db_bid3) = db
.collection()
.expect("can open bid collection")
.set(&bid3)
.expect("can set bid");
let (bid4_id, db_bid4) = db
.collection()
.expect("can open bid collection")
.set(&bid4)
.expect("can set bid");
println!("Bid 1 assigned ID: {}", bid1_id);
println!("Bid 2 assigned ID: {}", bid2_id);
println!("Bid 3 assigned ID: {}", bid3_id);
println!("Bid 4 assigned ID: {}", bid4_id);
// Print all bids retrieved from database
println!("\n--- Bids Retrieved from Database ---");
println!("\n1. Small compute bid:");
print_bid_details(&db_bid1);
println!("\n2. Large compute bid with obligation:");
print_bid_details(&db_bid2);
println!("\n3. Storage-heavy bid:");
print_bid_details(&db_bid3);
println!("\n4. Cancelled bid:");
print_bid_details(&db_bid4);
// Demonstrate different ways to retrieve bids from the database
println!("\n--- Retrieving Bids by Different Methods ---");
println!("\n1. By Customer ID Index (Customer 102):");
let customer_bids = db
.collection::<Bid>()
.expect("can open bid collection")
.get::<customer_id, _>(&102u32)
.expect("can load bids by customer");
assert_eq!(customer_bids.len(), 1);
print_bid_details(&customer_bids[0]);
println!("\n2. Updating Bid Status:");
let mut updated_bid = db_bid1.clone();
updated_bid.status = BidStatus::Confirmed;
let (_, confirmed_bid) = db
.collection::<Bid>()
.expect("can open bid collection")
.set(&updated_bid)
.expect("can update bid");
println!("Updated bid status to Confirmed:");
print_bid_details(&confirmed_bid);
// 3. Delete a bid and show the updated results
println!("\n3. After Deleting a Bid:");
println!("Deleting bid with ID: {}", bid4_id);
db.collection::<Bid>()
.expect("can open bid collection")
.delete_by_id(bid4_id)
.expect("can delete existing bid");
// Show remaining bids
let all_bids = db
.collection::<Bid>()
.expect("can open bid collection")
.get_all()
.expect("can load all bids");
println!("Remaining bids count: {}", all_bids.len());
assert_eq!(all_bids.len(), 3);
// Calculate total compute and storage requested
println!("\n--- Bid Analytics ---");
let total_compute_slices: i32 = all_bids.iter().map(|b| b.compute_slices_nr).sum();
let total_storage_slices: i32 = all_bids.iter().map(|b| b.storage_slices_nr).sum();
let avg_compute_price: f64 = all_bids.iter().map(|b| b.compute_slice_price).sum::<f64>() / all_bids.len() as f64;
let avg_storage_price: f64 = all_bids.iter().map(|b| b.storage_slice_price).sum::<f64>() / all_bids.len() as f64;
println!("Total Compute Slices Requested: {}", total_compute_slices);
println!("Total Storage Slices Requested: {}", total_storage_slices);
println!("Average Compute Price: ${:.3}", avg_compute_price);
println!("Average Storage Price: ${:.3}", avg_storage_price);
// Count bids by status
let confirmed_count = all_bids.iter().filter(|b| matches!(b.status, BidStatus::Confirmed)).count();
let assigned_count = all_bids.iter().filter(|b| matches!(b.status, BidStatus::Assigned)).count();
let pending_count = all_bids.iter().filter(|b| matches!(b.status, BidStatus::Pending)).count();
println!("\nBids by Status:");
println!(" Confirmed: {}", confirmed_count);
println!(" Assigned: {}", assigned_count);
println!(" Pending: {}", pending_count);
println!("\n--- Model Information ---");
println!("Bid DB Prefix: {}", Bid::db_prefix());
}

View File

@@ -0,0 +1,301 @@
use heromodels::db::{Collection, Db};
use heromodels::models::grid4::{Contract, ContractStatus};
use heromodels::models::grid4::contract::contract_index::customer_id;
use heromodels_core::Model;
// Helper function to print contract details
fn print_contract_details(contract: &Contract) {
println!("\n--- Contract Details ---");
println!("ID: {}", contract.get_id());
println!("Customer ID: {}", contract.customer_id);
println!("Compute Slices: {}", contract.compute_slices.len());
println!("Storage Slices: {}", contract.storage_slices.len());
println!("Compute Slice Price: ${:.2}", contract.compute_slice_price);
println!("Storage Slice Price: ${:.2}", contract.storage_slice_price);
println!("Network Slice Price: ${:.2}", contract.network_slice_price);
println!("Status: {:?}", contract.status);
println!("Start Date: {}", contract.start_date);
println!("End Date: {}", contract.end_date);
println!("Billing Period: {:?}", contract.billing_period);
println!("Signature User: {}", contract.signature_user);
println!("Signature Hoster: {}", contract.signature_hoster);
println!("Created At: {}", contract.base_data.created_at);
println!("Modified At: {}", contract.base_data.modified_at);
// Print compute slices details
if !contract.compute_slices.is_empty() {
println!(" Compute Slices:");
for (i, slice) in contract.compute_slices.iter().enumerate() {
println!(" {}. Node: {}, ID: {}, Memory: {:.1}GB, Storage: {:.1}GB, Passmark: {}, vCores: {}",
i + 1, slice.node_id, slice.id, slice.mem_gb, slice.storage_gb, slice.passmark, slice.vcores);
}
}
// Print storage slices details
if !contract.storage_slices.is_empty() {
println!(" Storage Slices:");
for (i, slice) in contract.storage_slices.iter().enumerate() {
println!(" {}. Node: {}, ID: {}, Size: {}GB",
i + 1, slice.node_id, slice.id, slice.storage_size_gb);
}
}
}
fn main() {
// Create a new DB instance in /tmp/grid4_contracts_db, and reset before every run
let db = heromodels::db::hero::OurDB::new("/tmp/grid4_contracts_db", true).expect("Can create DB");
println!("Grid4 Contract Models - Basic Usage Example");
println!("==========================================");
// Create compute slices for contracts
let compute_slice1 = ComputeSliceProvisioned::new()
.node_id(1001)
.id(1)
.mem_gb(2.0)
.storage_gb(20.0)
.passmark(2500)
.vcores(2)
.cpu_oversubscription(150)
.tags("web-server,production".to_string());
let compute_slice2 = ComputeSliceProvisioned::new()
.node_id(1002)
.id(2)
.mem_gb(4.0)
.storage_gb(40.0)
.passmark(5000)
.vcores(4)
.cpu_oversubscription(120)
.tags("database,high-performance".to_string());
let compute_slice3 = ComputeSliceProvisioned::new()
.node_id(1003)
.id(1)
.mem_gb(8.0)
.storage_gb(80.0)
.passmark(10000)
.vcores(8)
.cpu_oversubscription(100)
.tags("ml-training,gpu-enabled".to_string());
// Create storage slices for contracts
let storage_slice1 = StorageSliceProvisioned::new()
.node_id(2001)
.id(1)
.storage_size_gb(100)
.tags("backup,cold-storage".to_string());
let storage_slice2 = StorageSliceProvisioned::new()
.node_id(2002)
.id(2)
.storage_size_gb(500)
.tags("data-lake,analytics".to_string());
let storage_slice3 = StorageSliceProvisioned::new()
.node_id(2003)
.id(1)
.storage_size_gb(1000)
.tags("archive,long-term".to_string());
// Create contracts with different configurations
// Contract 1 - Small web hosting contract
let contract1 = Contract::new()
.customer_id(201)
.add_compute_slice(compute_slice1.clone())
.add_storage_slice(storage_slice1.clone())
.compute_slice_price(0.05)
.storage_slice_price(0.02)
.network_slice_price(0.01)
.status(ContractStatus::Active)
.start_date(1640995200) // 2022-01-01
.end_date(1672531200) // 2023-01-01
.billing_period(BillingPeriod::Monthly)
.signature_user("contract_user_201_abc123".to_string())
.signature_hoster("hoster_node1001_xyz789".to_string());
// Contract 2 - Database hosting contract
let contract2 = Contract::new()
.customer_id(202)
.add_compute_slice(compute_slice2.clone())
.add_storage_slice(storage_slice2.clone())
.compute_slice_price(0.04)
.storage_slice_price(0.015)
.network_slice_price(0.008)
.status(ContractStatus::Active)
.start_date(1640995200)
.end_date(1704067200) // 2024-01-01
.billing_period(BillingPeriod::Yearly)
.signature_user("contract_user_202_def456".to_string())
.signature_hoster("hoster_node1002_uvw123".to_string());
// Contract 3 - ML training contract (paused)
let contract3 = Contract::new()
.customer_id(203)
.add_compute_slice(compute_slice3.clone())
.add_storage_slice(storage_slice3.clone())
.compute_slice_price(0.08)
.storage_slice_price(0.01)
.network_slice_price(0.015)
.status(ContractStatus::Paused)
.start_date(1640995200)
.end_date(1672531200)
.billing_period(BillingPeriod::Hourly)
.signature_user("contract_user_203_ghi789".to_string())
.signature_hoster("hoster_node1003_rst456".to_string());
// Contract 4 - Multi-slice enterprise contract
let contract4 = Contract::new()
.customer_id(204)
.add_compute_slice(compute_slice1.clone())
.add_compute_slice(compute_slice2.clone())
.add_storage_slice(storage_slice1.clone())
.add_storage_slice(storage_slice2.clone())
.compute_slice_price(0.045)
.storage_slice_price(0.018)
.network_slice_price(0.012)
.status(ContractStatus::Active)
.start_date(1640995200)
.end_date(1735689600) // 2025-01-01
.billing_period(BillingPeriod::Monthly)
.signature_user("contract_user_204_jkl012".to_string())
.signature_hoster("hoster_enterprise_mno345".to_string());
// Save all contracts to database and get their assigned IDs and updated models
let (contract1_id, db_contract1) = db
.collection()
.expect("can open contract collection")
.set(&contract1)
.expect("can set contract");
let (contract2_id, db_contract2) = db
.collection()
.expect("can open contract collection")
.set(&contract2)
.expect("can set contract");
let (contract3_id, db_contract3) = db
.collection()
.expect("can open contract collection")
.set(&contract3)
.expect("can set contract");
let (contract4_id, db_contract4) = db
.collection()
.expect("can open contract collection")
.set(&contract4)
.expect("can set contract");
println!("Contract 1 assigned ID: {}", contract1_id);
println!("Contract 2 assigned ID: {}", contract2_id);
println!("Contract 3 assigned ID: {}", contract3_id);
println!("Contract 4 assigned ID: {}", contract4_id);
// Print all contracts retrieved from database
println!("\n--- Contracts Retrieved from Database ---");
println!("\n1. Web hosting contract:");
print_contract_details(&db_contract1);
println!("\n2. Database hosting contract:");
print_contract_details(&db_contract2);
println!("\n3. ML training contract (paused):");
print_contract_details(&db_contract3);
println!("\n4. Enterprise multi-slice contract:");
print_contract_details(&db_contract4);
// Demonstrate different ways to retrieve contracts from the database
// 1. Retrieve by customer ID index
println!("\n--- Retrieving Contracts by Different Methods ---");
println!("\n1. By Customer ID Index (Customer 202):");
let customer_contracts = db
.collection::<Contract>()
.expect("can open contract collection")
.get::<customer_id, _>(&202u32)
.expect("can load contracts by customer");
assert_eq!(customer_contracts.len(), 1);
print_contract_details(&customer_contracts[0]);
// 2. Update contract status
println!("\n2. Resuming Paused Contract:");
let mut updated_contract = db_contract3.clone();
updated_contract.status = ContractStatus::Active;
let (_, resumed_contract) = db
.collection::<Contract>()
.expect("can open contract collection")
.set(&updated_contract)
.expect("can update contract");
println!("Updated contract status to Active:");
print_contract_details(&resumed_contract);
// 3. Cancel a contract
println!("\n3. Cancelling a Contract:");
let mut cancelled_contract = db_contract1.clone();
cancelled_contract.status = ContractStatus::Cancelled;
let (_, final_contract) = db
.collection::<Contract>()
.expect("can open contract collection")
.set(&cancelled_contract)
.expect("can update contract");
println!("Cancelled contract:");
print_contract_details(&final_contract);
// Show remaining active contracts
let all_contracts = db
.collection::<Contract>()
.expect("can open contract collection")
.get_all()
.expect("can load all contracts");
println!("\n--- Contract Analytics ---");
let active_contracts: Vec<_> = all_contracts.iter()
.filter(|c| matches!(c.status, ContractStatus::Active))
.collect();
let paused_contracts: Vec<_> = all_contracts.iter()
.filter(|c| matches!(c.status, ContractStatus::Paused))
.collect();
let cancelled_contracts: Vec<_> = all_contracts.iter()
.filter(|c| matches!(c.status, ContractStatus::Cancelled))
.collect();
println!("Total Contracts: {}", all_contracts.len());
println!("Active Contracts: {}", active_contracts.len());
println!("Paused Contracts: {}", paused_contracts.len());
println!("Cancelled Contracts: {}", cancelled_contracts.len());
// Calculate total provisioned resources
let total_compute_slices: usize = all_contracts.iter().map(|c| c.compute_slices.len()).sum();
let total_storage_slices: usize = all_contracts.iter().map(|c| c.storage_slices.len()).sum();
let total_memory_gb: f64 = all_contracts.iter()
.flat_map(|c| &c.compute_slices)
.map(|s| s.mem_gb)
.sum();
let total_storage_gb: i32 = all_contracts.iter()
.flat_map(|c| &c.storage_slices)
.map(|s| s.storage_size_gb)
.sum();
println!("\nProvisioned Resources:");
println!(" Total Compute Slices: {}", total_compute_slices);
println!(" Total Storage Slices: {}", total_storage_slices);
println!(" Total Memory: {:.1} GB", total_memory_gb);
println!(" Total Storage: {} GB", total_storage_gb);
// Calculate average pricing
let avg_compute_price: f64 = all_contracts.iter().map(|c| c.compute_slice_price).sum::<f64>() / all_contracts.len() as f64;
let avg_storage_price: f64 = all_contracts.iter().map(|c| c.storage_slice_price).sum::<f64>() / all_contracts.len() as f64;
let avg_network_price: f64 = all_contracts.iter().map(|c| c.network_slice_price).sum::<f64>() / all_contracts.len() as f64;
println!("\nAverage Pricing:");
println!(" Compute: ${:.3} per slice", avg_compute_price);
println!(" Storage: ${:.3} per slice", avg_storage_price);
println!(" Network: ${:.3} per slice", avg_network_price);
println!("\n--- Model Information ---");
println!("Contract DB Prefix: {}", Contract::db_prefix());
}

View File

@@ -0,0 +1,12 @@
# Grid4 Node Example (OurDB)
This example demonstrates how to use the Grid4 `Node` model against the embedded OurDB backend.
- Creates an in-memory/on-disk OurDB under `/tmp`.
- Demonstrates CRUD and simple index lookups on `country`, `nodegroupid`, and `pubkey`.
Run it:
```bash
cargo run -p heromodels --example grid4_example
```

View File

@@ -0,0 +1,66 @@
use heromodels::db::hero::OurDB;
use heromodels::db::{Collection, Db};
use heromodels::models::grid4::node::node_index::{country, nodegroupid, pubkey};
use heromodels::models::grid4::node::{ComputeSlice, DeviceInfo, Node};
use std::sync::Arc;
fn main() {
// Create a temp OurDB
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let path = format!("/tmp/grid4_example_{}", ts);
let _ = std::fs::remove_dir_all(&path);
let db = Arc::new(OurDB::new(&path, true).expect("create OurDB"));
let nodes = db.collection::<Node>().expect("open node collection");
// Build a node
let cs = ComputeSlice::new()
.nodeid(1)
.slice_id(1)
.mem_gb(64.0)
.storage_gb(1024.0)
.passmark(8000)
.vcores(24)
.gpus(2)
.price_cc(0.5);
let dev = DeviceInfo {
vendor: "ACME".into(),
..Default::default()
};
let n = Node::new()
.nodegroupid(7)
.uptime(98)
.add_compute_slice(cs)
.devices(dev)
.country("BE")
.pubkey("PUB_NODE_X")
.build();
// Store
let (id, stored) = nodes.set(&n).expect("store node");
println!("Stored node id={id} pubkey={} country={}", stored.pubkey, stored.country);
// Query by indexes
let by_country = nodes.get::<country, _>("BE").expect("query country");
println!("Found {} nodes in country=BE", by_country.len());
let by_group = nodes.get::<nodegroupid, _>(&7).expect("query group");
println!("Found {} nodes in group=7", by_group.len());
let by_key = nodes.get::<pubkey, _>("PUB_NODE_X").expect("query pubkey");
println!("Found {} with pubkey PUB_NODE_X", by_key.len());
// Update
let updated = stored.clone().country("NL");
let (_, back) = nodes.set(&updated).expect("update node");
println!("Updated node country={}", back.country);
// Delete
nodes.delete_by_id(id).expect("delete node");
println!("Deleted node id={id}");
}

View File

@@ -0,0 +1,390 @@
use heromodels::db::{Collection, Db};
use heromodels::models::grid4::{Node, NodeDevice, ComputeSlice, StorageSlice};
use heromodels::models::grid4::node::node_index::{nodegroupid, country};
use heromodels_core::Model;
// Helper function to print node details
fn print_node_details(node: &Node) {
println!("\n--- Node Details ---");
println!("ID: {}", node.get_id());
println!("NodeGroup ID: {}", node.nodegroupid);
println!("Uptime: {}%", node.uptime);
println!("Country: {}", node.country);
println!("Birth Time: {}", node.birthtime);
println!("Public Key: {}", node.pubkey);
println!("Compute Slices: {}", node.computeslices.len());
println!("Storage Slices: {}", node.storageslices.len());
println!("Created At: {}", node.base_data.created_at);
println!("Modified At: {}", node.base_data.modified_at);
// Print capacity details
println!(" Capacity:");
println!(" Storage: {:.1} GB", node.capacity.storage_gb);
println!(" Memory: {:.1} GB", node.capacity.mem_gb);
println!(" GPU Memory: {:.1} GB", node.capacity.mem_gb_gpu);
println!(" Passmark: {}", node.capacity.passmark);
println!(" vCores: {}", node.capacity.vcores);
// Print device info
println!(" Devices:");
println!(" Vendor: {}", node.devices.vendor);
println!(" CPUs: {}", node.devices.cpu.len());
println!(" GPUs: {}", node.devices.gpu.len());
println!(" Memory: {}", node.devices.memory.len());
println!(" Storage: {}", node.devices.storage.len());
println!(" Network: {}", node.devices.network.len());
// Print compute slices
if !node.computeslices.is_empty() {
println!(" Compute Slices:");
for (i, slice) in node.computeslices.iter().enumerate() {
println!(" {}. ID: {}, Memory: {:.1}GB, Storage: {:.1}GB, vCores: {}, GPUs: {}",
i + 1, slice.id, slice.mem_gb, slice.storage_gb, slice.vcores, slice.gpus);
}
}
// Print storage slices
if !node.storageslices.is_empty() {
println!(" Storage Slices:");
for (i, slice) in node.storageslices.iter().enumerate() {
println!(" {}. ID: {}", i + 1, slice.id);
}
}
}
fn main() {
// Create a new DB instance in /tmp/grid4_nodes_db, and reset before every run
let db = heromodels::db::hero::OurDB::new("/tmp/grid4_nodes_db", true).expect("Can create DB");
println!("Grid4 Node Models - Basic Usage Example");
println!("======================================");
// Create device components for nodes
// CPU devices
let cpu1 = CPUDevice {
id: "cpu_intel_i7_12700k".to_string(),
cores: 12,
passmark: 28500,
description: "Intel Core i7-12700K".to_string(),
cpu_brand: "Intel".to_string(),
cpu_version: "12th Gen".to_string(),
};
let cpu2 = CPUDevice {
id: "cpu_amd_ryzen_9_5900x".to_string(),
cores: 12,
passmark: 32000,
description: "AMD Ryzen 9 5900X".to_string(),
cpu_brand: "AMD".to_string(),
cpu_version: "Zen 3".to_string(),
};
// GPU devices
let gpu1 = GPUDevice {
id: "gpu_rtx_3080".to_string(),
cores: 8704,
memory_gb: 10.0,
description: "NVIDIA GeForce RTX 3080".to_string(),
gpu_brand: "NVIDIA".to_string(),
gpu_version: "RTX 30 Series".to_string(),
};
let gpu2 = GPUDevice {
id: "gpu_rtx_4090".to_string(),
cores: 16384,
memory_gb: 24.0,
description: "NVIDIA GeForce RTX 4090".to_string(),
gpu_brand: "NVIDIA".to_string(),
gpu_version: "RTX 40 Series".to_string(),
};
// Memory devices
let memory1 = MemoryDevice {
id: "mem_ddr4_32gb".to_string(),
size_gb: 32.0,
description: "DDR4-3200 32GB Kit".to_string(),
};
let memory2 = MemoryDevice {
id: "mem_ddr5_64gb".to_string(),
size_gb: 64.0,
description: "DDR5-5600 64GB Kit".to_string(),
};
// Storage devices
let storage1 = StorageDevice {
id: "ssd_nvme_1tb".to_string(),
size_gb: 1000.0,
description: "NVMe SSD 1TB".to_string(),
};
let storage2 = StorageDevice {
id: "hdd_sata_4tb".to_string(),
size_gb: 4000.0,
description: "SATA HDD 4TB".to_string(),
};
// Network devices
let network1 = NetworkDevice {
id: "eth_1gbit".to_string(),
speed_mbps: 1000,
description: "Gigabit Ethernet".to_string(),
};
let network2 = NetworkDevice {
id: "eth_10gbit".to_string(),
speed_mbps: 10000,
description: "10 Gigabit Ethernet".to_string(),
};
// Create device info configurations
let devices1 = DeviceInfo {
vendor: "Dell".to_string(),
cpu: vec![cpu1.clone()],
gpu: vec![gpu1.clone()],
memory: vec![memory1.clone()],
storage: vec![storage1.clone(), storage2.clone()],
network: vec![network1.clone()],
};
let devices2 = DeviceInfo {
vendor: "HP".to_string(),
cpu: vec![cpu2.clone()],
gpu: vec![gpu2.clone()],
memory: vec![memory2.clone()],
storage: vec![storage1.clone()],
network: vec![network2.clone()],
};
// Create node capacities
let capacity1 = NodeCapacity {
storage_gb: 5000.0,
mem_gb: 32.0,
mem_gb_gpu: 10.0,
passmark: 28500,
vcores: 24,
};
let capacity2 = NodeCapacity {
storage_gb: 1000.0,
mem_gb: 64.0,
mem_gb_gpu: 24.0,
passmark: 32000,
vcores: 24,
};
// Create compute slices
let compute_slice1 = ComputeSlice::new()
.id(1)
.mem_gb(4.0)
.storage_gb(100.0)
.passmark(3000)
.vcores(2)
.cpu_oversubscription(150)
.storage_oversubscription(120)
.gpus(0);
let compute_slice2 = ComputeSlice::new()
.id(2)
.mem_gb(8.0)
.storage_gb(200.0)
.passmark(6000)
.vcores(4)
.cpu_oversubscription(130)
.storage_oversubscription(110)
.gpus(1);
let compute_slice3 = ComputeSlice::new()
.id(1)
.mem_gb(16.0)
.storage_gb(400.0)
.passmark(12000)
.vcores(8)
.cpu_oversubscription(110)
.storage_oversubscription(100)
.gpus(1);
// Create storage slices
let storage_slice1 = StorageSlice::new().id(1);
let storage_slice2 = StorageSlice::new().id(2);
let storage_slice3 = StorageSlice::new().id(3);
// Create nodes with different configurations
// Node 1 - Web hosting node
let node1 = Node::new()
.nodegroupid(1001)
.uptime(98)
.add_compute_slice(compute_slice1.clone())
.add_compute_slice(compute_slice2.clone())
.add_storage_slice(storage_slice1.clone())
.add_storage_slice(storage_slice2.clone())
.devices(devices1.clone())
.country("US".to_string())
.capacity(capacity1.clone())
.birthtime(1640995200) // 2022-01-01
.pubkey("node1_pubkey_abc123xyz789".to_string())
.signature_node("node1_signature_def456".to_string())
.signature_farmer("farmer1_signature_ghi789".to_string());
// Node 2 - High-performance computing node
let node2 = Node::new()
.nodegroupid(1002)
.uptime(99)
.add_compute_slice(compute_slice3.clone())
.add_storage_slice(storage_slice3.clone())
.devices(devices2.clone())
.country("DE".to_string())
.capacity(capacity2.clone())
.birthtime(1672531200) // 2023-01-01
.pubkey("node2_pubkey_jkl012mno345".to_string())
.signature_node("node2_signature_pqr678".to_string())
.signature_farmer("farmer2_signature_stu901".to_string());
// Node 3 - Storage-focused node
let node3 = Node::new()
.nodegroupid(1001)
.uptime(95)
.add_storage_slice(storage_slice1.clone())
.add_storage_slice(storage_slice2.clone())
.add_storage_slice(storage_slice3.clone())
.devices(devices1.clone())
.country("NL".to_string())
.capacity(capacity1.clone())
.birthtime(1704067200) // 2024-01-01
.pubkey("node3_pubkey_vwx234yzab567".to_string())
.signature_node("node3_signature_cde890".to_string())
.signature_farmer("farmer1_signature_fgh123".to_string());
// Save all nodes to database and get their assigned IDs and updated models
let (node1_id, db_node1) = db
.collection()
.expect("can open node collection")
.set(&node1)
.expect("can set node");
let (node2_id, db_node2) = db
.collection()
.expect("can open node collection")
.set(&node2)
.expect("can set node");
let (node3_id, db_node3) = db
.collection()
.expect("can open node collection")
.set(&node3)
.expect("can set node");
println!("Node 1 assigned ID: {}", node1_id);
println!("Node 2 assigned ID: {}", node2_id);
println!("Node 3 assigned ID: {}", node3_id);
// Print all nodes retrieved from database
println!("\n--- Nodes Retrieved from Database ---");
println!("\n1. Web hosting node:");
print_node_details(&db_node1);
println!("\n2. High-performance computing node:");
print_node_details(&db_node2);
println!("\n3. Storage-focused node:");
print_node_details(&db_node3);
// Demonstrate different ways to retrieve nodes from the database
// 1. Retrieve by nodegroup ID index
println!("\n--- Retrieving Nodes by Different Methods ---");
println!("\n1. By NodeGroup ID Index (NodeGroup 1001):");
let nodegroup_nodes = db
.collection::<Node>()
.expect("can open node collection")
.get::<nodegroupid, _>(&1001i32)
.expect("can load nodes by nodegroup");
assert_eq!(nodegroup_nodes.len(), 2);
for (i, node) in nodegroup_nodes.iter().enumerate() {
println!(" Node {}: ID {}, Country: {}, Uptime: {}%",
i + 1, node.get_id(), node.country, node.uptime);
}
// 2. Retrieve by country index
println!("\n2. By Country Index (Germany - DE):");
let country_nodes = db
.collection::<Node>()
.expect("can open node collection")
.get::<country, _>("DE")
.expect("can load nodes by country");
assert_eq!(country_nodes.len(), 1);
print_node_details(&country_nodes[0]);
// 3. Update node uptime
println!("\n3. Updating Node Uptime:");
let mut updated_node = db_node1.clone();
updated_node.uptime = 99;
let (_, uptime_updated_node) = db
.collection::<Node>()
.expect("can open node collection")
.set(&updated_node)
.expect("can update node");
println!("Updated node uptime to 99%:");
println!(" Node ID: {}, New Uptime: {}%", uptime_updated_node.get_id(), uptime_updated_node.uptime);
// Show all nodes and calculate analytics
let all_nodes = db
.collection::<Node>()
.expect("can open node collection")
.get_all()
.expect("can load all nodes");
println!("\n--- Node Analytics ---");
println!("Total Nodes: {}", all_nodes.len());
// Calculate total capacity
let total_storage_gb: f64 = all_nodes.iter().map(|n| n.capacity.storage_gb).sum();
let total_memory_gb: f64 = all_nodes.iter().map(|n| n.capacity.mem_gb).sum();
let total_gpu_memory_gb: f64 = all_nodes.iter().map(|n| n.capacity.mem_gb_gpu).sum();
let total_vcores: i32 = all_nodes.iter().map(|n| n.capacity.vcores).sum();
let avg_uptime: f64 = all_nodes.iter().map(|n| n.uptime as f64).sum::<f64>() / all_nodes.len() as f64;
println!("Total Capacity:");
println!(" Storage: {:.1} GB", total_storage_gb);
println!(" Memory: {:.1} GB", total_memory_gb);
println!(" GPU Memory: {:.1} GB", total_gpu_memory_gb);
println!(" vCores: {}", total_vcores);
println!(" Average Uptime: {:.1}%", avg_uptime);
// Count nodes by country
let mut country_counts = std::collections::HashMap::new();
for node in &all_nodes {
*country_counts.entry(&node.country).or_insert(0) += 1;
}
println!("\nNodes by Country:");
for (country, count) in country_counts {
println!(" {}: {}", country, count);
}
// Count total slices
let total_compute_slices: usize = all_nodes.iter().map(|n| n.computeslices.len()).sum();
let total_storage_slices: usize = all_nodes.iter().map(|n| n.storageslices.len()).sum();
println!("\nTotal Slices:");
println!(" Compute Slices: {}", total_compute_slices);
println!(" Storage Slices: {}", total_storage_slices);
// Vendor distribution
let mut vendor_counts = std::collections::HashMap::new();
for node in &all_nodes {
*vendor_counts.entry(&node.devices.vendor).or_insert(0) += 1;
}
println!("\nNodes by Vendor:");
for (vendor, count) in vendor_counts {
println!(" {}: {}", vendor, count);
}
println!("\n--- Model Information ---");
println!("Node DB Prefix: {}", Node::db_prefix());
}

View File

@@ -0,0 +1,284 @@
use heromodels::db::{Collection, Db};
use heromodels::models::grid4::{NodeGroup, PricingPolicy, SLAPolicy};
use heromodels_core::Model;
// Helper function to print nodegroup details
fn print_nodegroup_details(nodegroup: &NodeGroup) {
println!("\n--- NodeGroup Details ---");
println!("ID: {}", nodegroup.get_id());
println!("Farmer ID: {}", nodegroup.farmerid);
println!("Description: {}", nodegroup.description);
println!("Secret: {}", nodegroup.secret);
println!("Compute Slice Pricing (CC): {:.4}", nodegroup.compute_slice_normalized_pricing_cc);
println!("Storage Slice Pricing (CC): {:.4}", nodegroup.storage_slice_normalized_pricing_cc);
println!("Signature Farmer: {}", nodegroup.signature_farmer);
println!("Created At: {}", nodegroup.base_data.created_at);
println!("Modified At: {}", nodegroup.base_data.modified_at);
// Print SLA Policy details
println!(" SLA Policy:");
println!(" Uptime: {}%", nodegroup.slapolicy.sla_uptime);
println!(" Bandwidth: {} Mbit/s", nodegroup.slapolicy.sla_bandwidth_mbit);
println!(" Penalty: {}%", nodegroup.slapolicy.sla_penalty);
// Print Pricing Policy details
println!(" Pricing Policy:");
println!(" Marketplace Year Discounts: {:?}%", nodegroup.pricingpolicy.marketplace_year_discounts);
}
fn main() {
// Create a new DB instance in /tmp/grid4_nodegroups_db, and reset before every run
let db = heromodels::db::hero::OurDB::new("/tmp/grid4_nodegroups_db", true).expect("Can create DB");
println!("Grid4 NodeGroup Models - Basic Usage Example");
println!("===========================================");
// Create SLA policies
let sla_policy_premium = SLAPolicy {
sla_uptime: 99,
sla_bandwidth_mbit: 1000,
sla_penalty: 200,
};
let sla_policy_standard = SLAPolicy {
sla_uptime: 95,
sla_bandwidth_mbit: 100,
sla_penalty: 100,
};
let sla_policy_basic = SLAPolicy {
sla_uptime: 90,
sla_bandwidth_mbit: 50,
sla_penalty: 50,
};
// Create pricing policies
let pricing_policy_aggressive = PricingPolicy {
marketplace_year_discounts: vec![40, 50, 60],
};
let pricing_policy_standard = PricingPolicy {
marketplace_year_discounts: vec![30, 40, 50],
};
let pricing_policy_conservative = PricingPolicy {
marketplace_year_discounts: vec![20, 30, 40],
};
// Create nodegroups with different configurations
// NodeGroup 1 - Premium hosting provider
let nodegroup1 = NodeGroup::new()
.farmerid(501)
.secret("encrypted_boot_secret_premium_abc123".to_string())
.description("Premium hosting with 99% uptime SLA and high-speed connectivity".to_string())
.slapolicy(sla_policy_premium.clone())
.pricingpolicy(pricing_policy_aggressive.clone())
.compute_slice_normalized_pricing_cc(0.0450)
.storage_slice_normalized_pricing_cc(0.0180)
.signature_farmer("farmer_501_premium_signature_xyz789".to_string());
// NodeGroup 2 - Standard business provider
let nodegroup2 = NodeGroup::new()
.farmerid(502)
.secret("encrypted_boot_secret_standard_def456".to_string())
.description("Standard business hosting with reliable performance".to_string())
.slapolicy(sla_policy_standard.clone())
.pricingpolicy(pricing_policy_standard.clone())
.compute_slice_normalized_pricing_cc(0.0350)
.storage_slice_normalized_pricing_cc(0.0150)
.signature_farmer("farmer_502_standard_signature_uvw012".to_string());
// NodeGroup 3 - Budget-friendly provider
let nodegroup3 = NodeGroup::new()
.farmerid(503)
.secret("encrypted_boot_secret_budget_ghi789".to_string())
.description("Cost-effective hosting for development and testing".to_string())
.slapolicy(sla_policy_basic.clone())
.pricingpolicy(pricing_policy_conservative.clone())
.compute_slice_normalized_pricing_cc(0.0250)
.storage_slice_normalized_pricing_cc(0.0120)
.signature_farmer("farmer_503_budget_signature_rst345".to_string());
// NodeGroup 4 - Enterprise provider
let nodegroup4 = NodeGroup::new()
.farmerid(504)
.secret("encrypted_boot_secret_enterprise_jkl012".to_string())
.description("Enterprise-grade infrastructure with maximum reliability".to_string())
.slapolicy(sla_policy_premium.clone())
.pricingpolicy(pricing_policy_standard.clone())
.compute_slice_normalized_pricing_cc(0.0500)
.storage_slice_normalized_pricing_cc(0.0200)
.signature_farmer("farmer_504_enterprise_signature_mno678".to_string());
// Save all nodegroups to database and get their assigned IDs and updated models
let (nodegroup1_id, db_nodegroup1) = db
.collection()
.expect("can open nodegroup collection")
.set(&nodegroup1)
.expect("can set nodegroup");
let (nodegroup2_id, db_nodegroup2) = db
.collection()
.expect("can open nodegroup collection")
.set(&nodegroup2)
.expect("can set nodegroup");
let (nodegroup3_id, db_nodegroup3) = db
.collection()
.expect("can open nodegroup collection")
.set(&nodegroup3)
.expect("can set nodegroup");
let (nodegroup4_id, db_nodegroup4) = db
.collection()
.expect("can open nodegroup collection")
.set(&nodegroup4)
.expect("can set nodegroup");
println!("NodeGroup 1 assigned ID: {}", nodegroup1_id);
println!("NodeGroup 2 assigned ID: {}", nodegroup2_id);
println!("NodeGroup 3 assigned ID: {}", nodegroup3_id);
println!("NodeGroup 4 assigned ID: {}", nodegroup4_id);
// Print all nodegroups retrieved from database
println!("\n--- NodeGroups Retrieved from Database ---");
println!("\n1. Premium hosting provider:");
print_nodegroup_details(&db_nodegroup1);
println!("\n2. Standard business provider:");
print_nodegroup_details(&db_nodegroup2);
println!("\n3. Budget-friendly provider:");
print_nodegroup_details(&db_nodegroup3);
println!("\n4. Enterprise provider:");
print_nodegroup_details(&db_nodegroup4);
// Demonstrate different ways to retrieve nodegroups from the database
// 1. Retrieve by farmer ID index
println!("\n--- Retrieving NodeGroups by Different Methods ---");
println!("\n1. By Farmer ID Index (Farmer 502):");
let farmer_nodegroups = db
.collection::<NodeGroup>()
.expect("can open nodegroup collection")
.get_by_index("farmerid", &502u32)
.expect("can load nodegroups by farmer");
assert_eq!(farmer_nodegroups.len(), 1);
print_nodegroup_details(&farmer_nodegroups[0]);
// 2. Update nodegroup pricing
println!("\n2. Updating NodeGroup Pricing:");
let mut updated_nodegroup = db_nodegroup3.clone();
updated_nodegroup.compute_slice_normalized_pricing_cc = 0.0280;
updated_nodegroup.storage_slice_normalized_pricing_cc = 0.0130;
let (_, price_updated_nodegroup) = db
.collection::<NodeGroup>()
.expect("can open nodegroup collection")
.set(&updated_nodegroup)
.expect("can update nodegroup");
println!("Updated pricing for budget provider:");
println!(" Compute: {:.4} CC", price_updated_nodegroup.compute_slice_normalized_pricing_cc);
println!(" Storage: {:.4} CC", price_updated_nodegroup.storage_slice_normalized_pricing_cc);
// 3. Update SLA policy
println!("\n3. Updating SLA Policy:");
let mut sla_updated_nodegroup = db_nodegroup2.clone();
sla_updated_nodegroup.slapolicy.sla_uptime = 98;
sla_updated_nodegroup.slapolicy.sla_bandwidth_mbit = 500;
let (_, sla_updated_nodegroup) = db
.collection::<NodeGroup>()
.expect("can open nodegroup collection")
.set(&sla_updated_nodegroup)
.expect("can update nodegroup");
println!("Updated SLA policy for standard provider:");
println!(" Uptime: {}%", sla_updated_nodegroup.slapolicy.sla_uptime);
println!(" Bandwidth: {} Mbit/s", sla_updated_nodegroup.slapolicy.sla_bandwidth_mbit);
// Show all nodegroups and calculate analytics
let all_nodegroups = db
.collection::<NodeGroup>()
.expect("can open nodegroup collection")
.get_all()
.expect("can load all nodegroups");
println!("\n--- NodeGroup Analytics ---");
println!("Total NodeGroups: {}", all_nodegroups.len());
// Calculate pricing statistics
let avg_compute_price: f64 = all_nodegroups.iter()
.map(|ng| ng.compute_slice_normalized_pricing_cc)
.sum::<f64>() / all_nodegroups.len() as f64;
let avg_storage_price: f64 = all_nodegroups.iter()
.map(|ng| ng.storage_slice_normalized_pricing_cc)
.sum::<f64>() / all_nodegroups.len() as f64;
let min_compute_price = all_nodegroups.iter()
.map(|ng| ng.compute_slice_normalized_pricing_cc)
.fold(f64::INFINITY, f64::min);
let max_compute_price = all_nodegroups.iter()
.map(|ng| ng.compute_slice_normalized_pricing_cc)
.fold(f64::NEG_INFINITY, f64::max);
println!("Pricing Statistics:");
println!(" Average Compute Price: {:.4} CC", avg_compute_price);
println!(" Average Storage Price: {:.4} CC", avg_storage_price);
println!(" Compute Price Range: {:.4} - {:.4} CC", min_compute_price, max_compute_price);
// Calculate SLA statistics
let avg_uptime: f64 = all_nodegroups.iter()
.map(|ng| ng.slapolicy.sla_uptime as f64)
.sum::<f64>() / all_nodegroups.len() as f64;
let avg_bandwidth: f64 = all_nodegroups.iter()
.map(|ng| ng.slapolicy.sla_bandwidth_mbit as f64)
.sum::<f64>() / all_nodegroups.len() as f64;
let avg_penalty: f64 = all_nodegroups.iter()
.map(|ng| ng.slapolicy.sla_penalty as f64)
.sum::<f64>() / all_nodegroups.len() as f64;
println!("\nSLA Statistics:");
println!(" Average Uptime Guarantee: {:.1}%", avg_uptime);
println!(" Average Bandwidth Guarantee: {:.0} Mbit/s", avg_bandwidth);
println!(" Average Penalty Rate: {:.0}%", avg_penalty);
// Count farmers
let unique_farmers: std::collections::HashSet<_> = all_nodegroups.iter()
.map(|ng| ng.farmerid)
.collect();
println!("\nFarmer Statistics:");
println!(" Unique Farmers: {}", unique_farmers.len());
println!(" NodeGroups per Farmer: {:.1}", all_nodegroups.len() as f64 / unique_farmers.len() as f64);
// Analyze discount policies
let total_discount_tiers: usize = all_nodegroups.iter()
.map(|ng| ng.pricingpolicy.marketplace_year_discounts.len())
.sum();
let avg_discount_tiers: f64 = total_discount_tiers as f64 / all_nodegroups.len() as f64;
println!("\nDiscount Policy Statistics:");
println!(" Average Discount Tiers: {:.1}", avg_discount_tiers);
// Find best value providers (high SLA, low price)
println!("\n--- Provider Rankings ---");
let mut providers_with_scores: Vec<_> = all_nodegroups.iter()
.map(|ng| {
let value_score = (ng.slapolicy.sla_uptime as f64) / ng.compute_slice_normalized_pricing_cc;
(ng, value_score)
})
.collect();
providers_with_scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
println!("Best Value Providers (Uptime/Price ratio):");
for (i, (ng, score)) in providers_with_scores.iter().enumerate() {
println!(" {}. Farmer {}: {:.0} ({}% uptime, {:.4} CC)",
i + 1, ng.farmerid, score, ng.slapolicy.sla_uptime, ng.compute_slice_normalized_pricing_cc);
}
println!("\n--- Model Information ---");
println!("NodeGroup DB Prefix: {}", NodeGroup::db_prefix());
}

View File

@@ -0,0 +1,311 @@
use heromodels::db::{Collection, Db};
use heromodels::models::grid4::{NodeGroupReputation, NodeReputation};
use heromodels_core::Model;
// Helper function to print nodegroup reputation details
fn print_nodegroup_reputation_details(reputation: &NodeGroupReputation) {
println!("\n--- NodeGroup Reputation Details ---");
println!("ID: {}", reputation.get_id());
println!("NodeGroup ID: {}", reputation.nodegroup_id);
println!("Reputation Score: {}/100", reputation.reputation);
println!("Uptime: {}%", reputation.uptime);
println!("Node Count: {}", reputation.nodes.len());
println!("Created At: {}", reputation.base_data.created_at);
println!("Modified At: {}", reputation.base_data.modified_at);
// Print individual node reputations
if !reputation.nodes.is_empty() {
println!(" Individual Node Reputations:");
for (i, node_rep) in reputation.nodes.iter().enumerate() {
println!(" {}. Node {}: Reputation {}/100, Uptime {}%",
i + 1, node_rep.node_id, node_rep.reputation, node_rep.uptime);
}
// Calculate average node reputation and uptime
let avg_node_reputation: f64 = reputation.nodes.iter()
.map(|n| n.reputation as f64)
.sum::<f64>() / reputation.nodes.len() as f64;
let avg_node_uptime: f64 = reputation.nodes.iter()
.map(|n| n.uptime as f64)
.sum::<f64>() / reputation.nodes.len() as f64;
println!(" Average Node Reputation: {:.1}/100", avg_node_reputation);
println!(" Average Node Uptime: {:.1}%", avg_node_uptime);
}
}
fn main() {
// Create a new DB instance in /tmp/grid4_reputation_db, and reset before every run
let db = heromodels::db::hero::OurDB::new("/tmp/grid4_reputation_db", true).expect("Can create DB");
println!("Grid4 Reputation Models - Basic Usage Example");
println!("============================================");
// Create individual node reputations
// High-performing nodes
let node_rep1 = NodeReputation::new()
.node_id(1001)
.reputation(85)
.uptime(99);
let node_rep2 = NodeReputation::new()
.node_id(1002)
.reputation(92)
.uptime(98);
let node_rep3 = NodeReputation::new()
.node_id(1003)
.reputation(78)
.uptime(97);
// Medium-performing nodes
let node_rep4 = NodeReputation::new()
.node_id(2001)
.reputation(65)
.uptime(94);
let node_rep5 = NodeReputation::new()
.node_id(2002)
.reputation(72)
.uptime(96);
// Lower-performing nodes
let node_rep6 = NodeReputation::new()
.node_id(3001)
.reputation(45)
.uptime(88);
let node_rep7 = NodeReputation::new()
.node_id(3002)
.reputation(38)
.uptime(85);
// New nodes with default reputation
let node_rep8 = NodeReputation::new()
.node_id(4001)
.reputation(50) // default
.uptime(0); // just started
let node_rep9 = NodeReputation::new()
.node_id(4002)
.reputation(50) // default
.uptime(0); // just started
// Create nodegroup reputations with different performance profiles
// NodeGroup 1 - High-performance provider
let nodegroup_rep1 = NodeGroupReputation::new()
.nodegroup_id(1001)
.reputation(85) // high reputation earned over time
.uptime(98) // excellent uptime
.add_node_reputation(node_rep1.clone())
.add_node_reputation(node_rep2.clone())
.add_node_reputation(node_rep3.clone());
// NodeGroup 2 - Medium-performance provider
let nodegroup_rep2 = NodeGroupReputation::new()
.nodegroup_id(1002)
.reputation(68) // decent reputation
.uptime(95) // good uptime
.add_node_reputation(node_rep4.clone())
.add_node_reputation(node_rep5.clone());
// NodeGroup 3 - Struggling provider
let nodegroup_rep3 = NodeGroupReputation::new()
.nodegroup_id(1003)
.reputation(42) // below average reputation
.uptime(87) // poor uptime
.add_node_reputation(node_rep6.clone())
.add_node_reputation(node_rep7.clone());
// NodeGroup 4 - New provider (default reputation)
let nodegroup_rep4 = NodeGroupReputation::new()
.nodegroup_id(1004)
.reputation(50) // default starting reputation
.uptime(0) // no history yet
.add_node_reputation(node_rep8.clone())
.add_node_reputation(node_rep9.clone());
// Save all nodegroup reputations to database and get their assigned IDs and updated models
let (rep1_id, db_rep1) = db
.collection()
.expect("can open reputation collection")
.set(&nodegroup_rep1)
.expect("can set reputation");
let (rep2_id, db_rep2) = db
.collection()
.expect("can open reputation collection")
.set(&nodegroup_rep2)
.expect("can set reputation");
let (rep3_id, db_rep3) = db
.collection()
.expect("can open reputation collection")
.set(&nodegroup_rep3)
.expect("can set reputation");
let (rep4_id, db_rep4) = db
.collection()
.expect("can open reputation collection")
.set(&nodegroup_rep4)
.expect("can set reputation");
println!("NodeGroup Reputation 1 assigned ID: {}", rep1_id);
println!("NodeGroup Reputation 2 assigned ID: {}", rep2_id);
println!("NodeGroup Reputation 3 assigned ID: {}", rep3_id);
println!("NodeGroup Reputation 4 assigned ID: {}", rep4_id);
// Print all reputation records retrieved from database
println!("\n--- Reputation Records Retrieved from Database ---");
println!("\n1. High-performance provider:");
print_nodegroup_reputation_details(&db_rep1);
println!("\n2. Medium-performance provider:");
print_nodegroup_reputation_details(&db_rep2);
println!("\n3. Struggling provider:");
print_nodegroup_reputation_details(&db_rep3);
println!("\n4. New provider:");
print_nodegroup_reputation_details(&db_rep4);
// Demonstrate different ways to retrieve reputation records from the database
// 1. Retrieve by nodegroup ID index
println!("\n--- Retrieving Reputation by Different Methods ---");
println!("\n1. By NodeGroup ID Index (NodeGroup 1002):");
let nodegroup_reps = db
.collection::<NodeGroupReputation>()
.expect("can open reputation collection")
.get_by_index("nodegroup_id", &1002u32)
.expect("can load reputation by nodegroup");
assert_eq!(nodegroup_reps.len(), 1);
print_nodegroup_reputation_details(&nodegroup_reps[0]);
// 2. Update reputation scores (simulate performance improvement)
println!("\n2. Updating Reputation Scores (Performance Improvement):");
let mut improved_rep = db_rep3.clone();
improved_rep.reputation = 55; // improved from 42
improved_rep.uptime = 92; // improved from 87
// Also improve individual node reputations
for node_rep in &mut improved_rep.nodes {
node_rep.reputation += 10; // boost each node's reputation
node_rep.uptime += 5; // improve uptime
}
let (_, updated_rep) = db
.collection::<NodeGroupReputation>()
.expect("can open reputation collection")
.set(&improved_rep)
.expect("can update reputation");
println!("Updated reputation for struggling provider:");
print_nodegroup_reputation_details(&updated_rep);
// 3. Add new node to existing nodegroup reputation
println!("\n3. Adding New Node to Existing NodeGroup:");
let new_node_rep = NodeReputation::new()
.node_id(1004)
.reputation(88)
.uptime(99);
let mut expanded_rep = db_rep1.clone();
expanded_rep.add_node_reputation(new_node_rep);
// Recalculate nodegroup reputation based on node average
let total_node_rep: i32 = expanded_rep.nodes.iter().map(|n| n.reputation).sum();
expanded_rep.reputation = total_node_rep / expanded_rep.nodes.len() as i32;
let (_, expanded_rep) = db
.collection::<NodeGroupReputation>()
.expect("can open reputation collection")
.set(&expanded_rep)
.expect("can update reputation");
println!("Added new high-performing node to top provider:");
print_nodegroup_reputation_details(&expanded_rep);
// Show all reputation records and calculate analytics
let all_reps = db
.collection::<NodeGroupReputation>()
.expect("can open reputation collection")
.get_all()
.expect("can load all reputations");
println!("\n--- Reputation Analytics ---");
println!("Total NodeGroup Reputations: {}", all_reps.len());
// Calculate overall statistics
let avg_nodegroup_reputation: f64 = all_reps.iter()
.map(|r| r.reputation as f64)
.sum::<f64>() / all_reps.len() as f64;
let avg_nodegroup_uptime: f64 = all_reps.iter()
.filter(|r| r.uptime > 0) // exclude new providers with 0 uptime
.map(|r| r.uptime as f64)
.sum::<f64>() / all_reps.iter().filter(|r| r.uptime > 0).count() as f64;
println!("Overall Statistics:");
println!(" Average NodeGroup Reputation: {:.1}/100", avg_nodegroup_reputation);
println!(" Average NodeGroup Uptime: {:.1}%", avg_nodegroup_uptime);
// Count reputation tiers
let excellent_reps = all_reps.iter().filter(|r| r.reputation >= 80).count();
let good_reps = all_reps.iter().filter(|r| r.reputation >= 60 && r.reputation < 80).count();
let average_reps = all_reps.iter().filter(|r| r.reputation >= 40 && r.reputation < 60).count();
let poor_reps = all_reps.iter().filter(|r| r.reputation < 40).count();
println!("\nReputation Distribution:");
println!(" Excellent (80-100): {}", excellent_reps);
println!(" Good (60-79): {}", good_reps);
println!(" Average (40-59): {}", average_reps);
println!(" Poor (0-39): {}", poor_reps);
// Calculate total nodes and their statistics
let total_nodes: usize = all_reps.iter().map(|r| r.nodes.len()).sum();
let all_node_reps: Vec<i32> = all_reps.iter()
.flat_map(|r| &r.nodes)
.map(|n| n.reputation)
.collect();
let all_node_uptimes: Vec<i32> = all_reps.iter()
.flat_map(|r| &r.nodes)
.filter(|n| n.uptime > 0)
.map(|n| n.uptime)
.collect();
let avg_node_reputation: f64 = all_node_reps.iter().sum::<i32>() as f64 / all_node_reps.len() as f64;
let avg_node_uptime: f64 = all_node_uptimes.iter().sum::<i32>() as f64 / all_node_uptimes.len() as f64;
println!("\nNode-Level Statistics:");
println!(" Total Nodes: {}", total_nodes);
println!(" Average Node Reputation: {:.1}/100", avg_node_reputation);
println!(" Average Node Uptime: {:.1}%", avg_node_uptime);
// Find best and worst performing nodegroups
let best_nodegroup = all_reps.iter().max_by_key(|r| r.reputation).unwrap();
let worst_nodegroup = all_reps.iter().min_by_key(|r| r.reputation).unwrap();
println!("\nPerformance Leaders:");
println!(" Best NodeGroup: {} (Reputation: {}, Uptime: {}%)",
best_nodegroup.nodegroup_id, best_nodegroup.reputation, best_nodegroup.uptime);
println!(" Worst NodeGroup: {} (Reputation: {}, Uptime: {}%)",
worst_nodegroup.nodegroup_id, worst_nodegroup.reputation, worst_nodegroup.uptime);
// Rank nodegroups by reputation
let mut ranked_nodegroups: Vec<_> = all_reps.iter().collect();
ranked_nodegroups.sort_by(|a, b| b.reputation.cmp(&a.reputation));
println!("\nNodeGroup Rankings (by Reputation):");
for (i, rep) in ranked_nodegroups.iter().enumerate() {
let status = match rep.reputation {
80..=100 => "Excellent",
60..=79 => "Good",
40..=59 => "Average",
_ => "Poor",
};
println!(" {}. NodeGroup {}: {} ({}/100, {}% uptime)",
i + 1, rep.nodegroup_id, status, rep.reputation, rep.uptime);
}
println!("\n--- Model Information ---");
println!("NodeGroupReputation DB Prefix: {}", NodeGroupReputation::db_prefix());
}

View File

@@ -0,0 +1,15 @@
# Heroledger Postgres Example
This example demonstrates how to use the Heroledger `User` model against Postgres using the `heromodels::db::postgres` backend.
- Connects to Postgres with user `postgres` and password `test123` on `localhost:5432`.
- Creates the table and indexes automatically on first use.
- Shows basic CRUD and an index lookup on `username`.
Run it:
```bash
cargo run -p heromodels --example heroledger_example
```
Make sure Postgres is running locally and accessible with the credentials above.

View File

@@ -0,0 +1,54 @@
use heromodels::db::postgres::{Config, Postgres};
use heromodels::db::{Collection, Db};
use heromodels::models::heroledger::user::user_index::username;
use heromodels::models::heroledger::user::{SecretBox, User};
fn main() {
let db = Postgres::new(
Config::new()
.user(Some("postgres".into()))
.password(Some("test123".into()))
.host(Some("localhost".into()))
.port(Some(5432)),
)
.expect("Can connect to Postgres");
println!("Heroledger User - Postgres Example");
println!("==================================");
let users = db.collection::<User>().expect("open user collection");
// Clean
if let Ok(existing) = users.get_all() {
for u in existing {
let _ = users.delete_by_id(u.get_id());
}
}
let sb = SecretBox::new().data(vec![1, 2, 3]).nonce(vec![9, 9, 9]).build();
let u = User::new(0)
.username("alice")
.pubkey("PUBKEY_A")
.add_email("alice@example.com")
.add_userprofile(sb)
.build();
let (id, stored) = users.set(&u).expect("store user");
println!("Stored user id={id} username={} pubkey={}", stored.username, stored.pubkey);
let by_idx = users.get::<username, _>("alice").expect("by username");
println!("Found {} user(s) with username=alice", by_idx.len());
let fetched = users.get_by_id(id).expect("get by id").expect("exists");
println!("Fetched by id={} username={} emails={:?}", id, fetched.username, fetched.email);
// Update
let updated = fetched.clone().add_email("work@alice.example");
let (_, back) = users.set(&updated).expect("update user");
println!("Updated emails = {:?}", back.email);
// Delete
users.delete_by_id(id).expect("delete user");
println!("Deleted user id={id}");
}

View File

@@ -1,8 +1,11 @@
use heromodels::db::postgres::Config;
use heromodels::db::{Collection, Db};
use heromodels::models::userexample::user::user_index::{is_active, username};
use heromodels::models::userexample::user::user_index::{email, username};
use heromodels::models::{Comment, User};
use heromodels_core::Model;
// For demonstrating embedded/nested indexes
use heromodels::models::grid4::node::{ComputeSlice, DeviceInfo, Node};
use heromodels::models::grid4::node::node_index::{country as node_country, pubkey as node_pubkey};
// Helper function to print user details
fn print_user_details(user: &User) {
@@ -37,6 +40,21 @@ fn main() {
)
.expect("Can connect to postgress");
// Unique suffix to avoid collisions with legacy rows from prior runs
use std::time::{SystemTime, UNIX_EPOCH};
let ts = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let user1_name = format!("johndoe_{}", ts);
let user2_name = format!("janesmith_{}", ts);
let user3_name = format!("willism_{}", ts);
let user4_name = format!("carrols_{}", ts);
let user1_email = format!("john.doe+{}@example.com", ts);
let user2_email = format!("jane.smith+{}@example.com", ts);
let user3_email = format!("willis.masters+{}@example.com", ts);
let user4_email = format!("carrol.smith+{}@example.com", ts);
println!("Hero Models - Basic Usage Example");
println!("================================");
@@ -64,32 +82,32 @@ fn main() {
// User 1
let user1 = User::new()
.username("johndoe")
.email("john.doe@example.com")
.username(&user1_name)
.email(&user1_email)
.full_name("John Doe")
.is_active(false)
.build();
// User 2
let user2 = User::new()
.username("janesmith")
.email("jane.smith@example.com")
.username(&user2_name)
.email(&user2_email)
.full_name("Jane Smith")
.is_active(true)
.build();
// User 3
let user3 = User::new()
.username("willism")
.email("willis.masters@example.com")
.username(&user3_name)
.email(&user3_email)
.full_name("Willis Masters")
.is_active(true)
.build();
// User 4
let user4 = User::new()
.username("carrols")
.email("carrol.smith@example.com")
.username(&user4_name)
.email(&user4_email)
.full_name("Carrol Smith")
.is_active(false)
.build();
@@ -145,66 +163,95 @@ fn main() {
let stored_users = db
.collection::<User>()
.expect("can open user collection")
.get::<username, _>("johndoe")
.get::<username, _>(&user1_name)
.expect("can load stored user");
assert_eq!(stored_users.len(), 1);
print_user_details(&stored_users[0]);
// 2. Retrieve by active status
println!("\n2. By Active Status (Active = true):");
let active_users = db
// 2. Retrieve by email index
println!("\n2. By Email Index:");
let by_email = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&true)
.expect("can load stored users");
assert_eq!(active_users.len(), 2);
for active_user in active_users.iter() {
print_user_details(active_user);
}
.get::<email, _>(&user2_email)
.expect("can load stored user by email");
assert_eq!(by_email.len(), 1);
print_user_details(&by_email[0]);
// 3. Delete a user and show the updated results
println!("\n3. After Deleting a User:");
let user_to_delete_id = active_users[0].get_id();
let user_to_delete_id = stored_users[0].get_id();
println!("Deleting user with ID: {user_to_delete_id}");
db.collection::<User>()
.expect("can open user collection")
.delete_by_id(user_to_delete_id)
.expect("can delete existing user");
// Show remaining active users
let active_users = db
// Verify deletion by querying the same username again
let should_be_empty = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&true)
.expect("can load stored users");
println!(" a. Remaining Active Users:");
assert_eq!(active_users.len(), 1);
for active_user in active_users.iter() {
print_user_details(active_user);
}
// Show inactive users
let inactive_users = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&false)
.expect("can load stored users");
println!(" b. Inactive Users:");
assert_eq!(inactive_users.len(), 2);
for inactive_user in inactive_users.iter() {
print_user_details(inactive_user);
}
.get::<username, _>(&user1_name)
.expect("can query by username after delete");
println!(" a. Query by username '{}' after delete -> {} results", user1_name, should_be_empty.len());
assert_eq!(should_be_empty.len(), 0);
// Delete a user based on an index for good measure
db.collection::<User>()
.expect("can open user collection")
.delete::<username, _>("janesmith")
.delete::<username, _>(&user4_name)
.expect("can delete existing user");
// Demonstrate embedded/nested indexes with Grid4 Node
println!("\n--- Demonstrating Embedded/Nested Indexes (Grid4::Node) ---");
println!("Node indexed fields: {:?}", Node::indexed_fields());
// Build a minimal node with nested data and persist it
let cs = ComputeSlice::new()
.nodeid(42)
.slice_id(1)
.mem_gb(32.0)
.storage_gb(512.0)
.passmark(6000)
.vcores(16)
.gpus(1)
.price_cc(0.33);
let dev = DeviceInfo { vendor: "ACME".into(), ..Default::default() };
let node = Node::new()
.nodegroupid(101)
.uptime(99)
.add_compute_slice(cs)
.devices(dev)
.country("BE")
.pubkey("EX_NODE_PK_1")
.build();
let (node_id, _stored_node) = db
.collection::<Node>()
.expect("can open node collection")
.set(&node)
.expect("can set node");
println!("Stored node id: {}", node_id);
// Query by top-level indexes
let be_nodes = db
.collection::<Node>()
.expect("can open node collection")
.get::<node_country, _>("BE")
.expect("can query nodes by country");
println!("Nodes in BE (count may include legacy rows): {}", be_nodes.len());
let by_pk = db
.collection::<Node>()
.expect("can open node collection")
.get::<node_pubkey, _>("EX_NODE_PK_1")
.expect("can query node by pubkey");
assert!(by_pk.iter().any(|n| n.get_id() == node_id));
// Note: Nested path indexes (e.g., devices.vendor, computeslices.passmark) are created and used
// for DB-side indexing, but are not yet exposed as typed Index keys in the API. They appear in
// Node::indexed_fields() and contribute to Model::db_keys(), enabling performant JSONB GIN indexes.
println!("\n--- User Model Information ---");
println!("User DB Prefix: {}", User::db_prefix());
@@ -214,7 +261,7 @@ fn main() {
// 1. Create and save a comment
println!("\n1. Creating a Comment:");
let comment = Comment::new()
.user_id(db_user1.get_id()) // commenter's user ID
.user_id(db_user2.get_id()) // commenter's user ID (use an existing user)
.content("This is a comment on the user")
.build();
@@ -232,7 +279,7 @@ fn main() {
// 3. Associate the comment with a user
println!("\n2. Associating Comment with User:");
let mut updated_user = db_user1.clone();
let mut updated_user = db_user2.clone();
updated_user.base_data.add_comment(db_comment.get_id());
// Save the updated user and get the new version