use rustlite::{Database, IndexType, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u64,
name: String,
email: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct Order {
id: u64,
user_id: u64, product: String,
amount: f64,
}
fn main() -> Result<()> {
println!("=== RustLite Relational Demo ===\n");
let db = Database::in_memory()?;
println!("Setting up Users table indexes...");
db.create_index("users_pk", IndexType::Hash)?;
db.create_index("users_by_email", IndexType::Hash)?;
db.create_index("users_by_name", IndexType::BTree)?;
println!("Setting up Orders table indexes...");
db.create_index("orders_pk", IndexType::Hash)?;
db.create_index("orders_by_user", IndexType::BTree)?;
println!("✓ Indexes created\n");
println!("Inserting users...");
let users = vec![
User {
id: 1,
name: "Alice Johnson".to_string(),
email: "alice@example.com".to_string(),
},
User {
id: 2,
name: "Bob Smith".to_string(),
email: "bob@example.com".to_string(),
},
User {
id: 3,
name: "Charlie Brown".to_string(),
email: "charlie@example.com".to_string(),
},
];
for user in &users {
let key = format!("user:{}", user.id);
let value = bincode::serialize(user).unwrap();
db.put(key.as_bytes(), &value)?;
db.index_insert("users_pk", &user.id.to_le_bytes(), user.id)?;
db.index_insert("users_by_email", user.email.as_bytes(), user.id)?;
db.index_insert("users_by_name", user.name.as_bytes(), user.id)?;
println!(" ✓ Inserted user: {} (ID: {})", user.name, user.id);
}
println!();
println!("Inserting orders...");
let orders = vec![
Order {
id: 101,
user_id: 1,
product: "Laptop".to_string(),
amount: 1200.00,
},
Order {
id: 102,
user_id: 1,
product: "Mouse".to_string(),
amount: 25.00,
},
Order {
id: 103,
user_id: 2,
product: "Keyboard".to_string(),
amount: 75.00,
},
Order {
id: 104,
user_id: 3,
product: "Monitor".to_string(),
amount: 350.00,
},
Order {
id: 105,
user_id: 1,
product: "Headphones".to_string(),
amount: 150.00,
},
];
for order in &orders {
let user_exists = !db
.index_find("users_pk", &order.user_id.to_le_bytes())?
.is_empty();
if !user_exists {
eprintln!(
"ERROR: Invalid user_id {} for order {}",
order.user_id, order.id
);
continue;
}
let key = format!("order:{}", order.id);
let value = bincode::serialize(order).unwrap();
db.put(key.as_bytes(), &value)?;
db.index_insert("orders_pk", &order.id.to_le_bytes(), order.id)?;
db.index_insert("orders_by_user", &order.user_id.to_le_bytes(), order.id)?;
println!(
" ✓ Inserted order: {} for user {} (${:.2})",
order.product, order.user_id, order.amount
);
}
println!();
println!("=== Query 1: Find user by ID ===");
let user_id: u64 = 2;
let user_ids = db.index_find("users_pk", &user_id.to_le_bytes())?;
if let Some(&found_id) = user_ids.first() {
let key = format!("user:{}", found_id);
if let Some(data) = db.get(key.as_bytes())? {
let user: User = bincode::deserialize(&data).unwrap();
println!("Found user: {} <{}>", user.name, user.email);
}
}
println!();
println!("=== Query 2: Find user by email ===");
let email = b"alice@example.com";
let user_ids = db.index_find("users_by_email", email)?;
if let Some(&found_id) = user_ids.first() {
let key = format!("user:{}", found_id);
if let Some(data) = db.get(key.as_bytes())? {
let user: User = bincode::deserialize(&data).unwrap();
println!("Found user: {} (ID: {})", user.name, user.id);
}
}
println!();
println!("=== Query 3: Get all orders for user 1 (Alice) ===");
let user_id: u64 = 1;
let user_ids = db.index_find("users_pk", &user_id.to_le_bytes())?;
if let Some(&found_id) = user_ids.first() {
let key = format!("user:{}", found_id);
if let Some(data) = db.get(key.as_bytes())? {
let user: User = bincode::deserialize(&data).unwrap();
println!("User: {}", user.name);
}
}
let order_ids = db.index_find("orders_by_user", &user_id.to_le_bytes())?;
println!("Found {} orders:", order_ids.len());
let mut total = 0.0;
for &order_id in &order_ids {
let key = format!("order:{}", order_id);
if let Some(data) = db.get(key.as_bytes())? {
let order: Order = bincode::deserialize(&data).unwrap();
println!(
" - Order #{}: {} (${:.2})",
order.id, order.product, order.amount
);
total += order.amount;
}
}
println!("Total: ${:.2}", total);
println!();
println!("=== Query 4: List all users (sorted by name) ===");
let info = db.index_info()?;
for idx in &info {
if idx.name == "users_by_name" {
println!("Users in database: {}", idx.entry_count);
}
}
println!();
println!("=== Query 5: Order summary by user ===");
for user in &users {
let order_ids = db.index_find("orders_by_user", &user.id.to_le_bytes())?;
let mut total = 0.0;
for &order_id in &order_ids {
let key = format!("order:{}", order_id);
if let Some(data) = db.get(key.as_bytes())? {
let order: Order = bincode::deserialize(&data).unwrap();
total += order.amount;
}
}
println!(
"{}: {} orders, ${:.2} total",
user.name,
order_ids.len(),
total
);
}
println!();
println!("=== Delete user 3 and cascade to orders ===");
let user_id_to_delete: u64 = 3;
let order_ids = db.index_find("orders_by_user", &user_id_to_delete.to_le_bytes())?;
println!(
"Deleting {} orders for user {}...",
order_ids.len(),
user_id_to_delete
);
for &order_id in &order_ids {
let key = format!("order:{}", order_id);
db.delete(key.as_bytes())?;
db.index_remove("orders_pk", &order_id.to_le_bytes())?;
db.index_remove("orders_by_user", &user_id_to_delete.to_le_bytes())?;
println!(" ✓ Deleted order {}", order_id);
}
let key = format!("user:{}", user_id_to_delete);
if let Some(data) = db.get(key.as_bytes())? {
let user: User = bincode::deserialize(&data).unwrap();
db.delete(key.as_bytes())?;
db.index_remove("users_pk", &user.id.to_le_bytes())?;
db.index_remove("users_by_email", user.email.as_bytes())?;
db.index_remove("users_by_name", user.name.as_bytes())?;
println!(" ✓ Deleted user: {}", user.name);
}
println!();
println!("=== Final Database Statistics ===");
let index_info = db.index_info()?;
for info in index_info {
println!(
"Index '{}': {} entries ({:?})",
info.name, info.entry_count, info.index_type
);
}
Ok(())
}