Prerequisites
You need Rust 1.83 or later with the 2024 edition enabled.Create a new project
Create a new Rust project and add Bomboni as a dependency:Generate unique identifiers
Let’s start by generating unique, sortable identifiers using Bomboni’s ULID-based ID system. Replace the contents ofsrc/main.rs with:
src/main.rs
use bomboni::common::id::Id;
fn main() {
// Generate a single unique ID
let id = Id::generate();
println!("Generated ID: {}", id);
// Generate multiple IDs - they're monotonically increasing
let ids = Id::generate_multiple(5);
println!("\nGenerated {} IDs:", ids.len());
for (i, id) in ids.iter().enumerate() {
println!(" {}: {}", i + 1, id);
}
// Parse an ID from a string
let id_str = "01ARZ3NDEKTSV4RRFFQ69G5FAV";
match id_str.parse::<Id>() {
Ok(parsed_id) => println!("\nParsed ID: {}", parsed_id),
Err(e) => println!("\nFailed to parse ID: {}", e),
}
}
cargo run
Generated ID: 01JQZX8Y0K5XQJZJ9WQVX4WX0M
Generated 5 IDs:
1: 01JQZX8Y0K5XQJZJ9WQVX4WX0N
2: 01JQZX8Y0K5XQJZJ9WQVX4WX0P
3: 01JQZX8Y0K5XQJZJ9WQVX4WX0Q
4: 01JQZX8Y0K5XQJZJ9WQVX4WX0R
5: 01JQZX8Y0K5XQJZJ9WQVX4WX0S
Parsed ID: 01ARZ3NDEKTSV4RRFFQ69G5FAV
The IDs are sortable by creation time and are unique across distributed systems without coordination.
Work with UTC datetime
Bomboni provides a UTC-focused datetime type. Add this to yourmain.rs:
src/main.rs
use bomboni::common::date_time::UtcDateTime;
fn datetime_example() {
// Get the current time
let now = UtcDateTime::now();
println!("Current time: {}", now);
// Create from Unix timestamp
let dt = UtcDateTime::from_seconds(1609459200).unwrap();
println!("From timestamp: {}", dt); // 2021-01-01T00:00:00Z
// Parse from string
let parsed = "2024-03-15T10:30:00Z".parse::<UtcDateTime>().unwrap();
println!("Parsed datetime: {}", parsed);
// Get timestamp
let (seconds, nanos) = parsed.timestamp();
println!("Timestamp: {} seconds, {} nanoseconds", seconds, nanos);
}
src/main.rs
fn main() {
// ... previous ID code ...
println!("\n=== DateTime Examples ===");
datetime_example();
}
Use worker-based ID generation
For distributed systems where multiple workers generate IDs, use the worker-based generator:src/main.rs
use bomboni::common::id::worker::WorkerIdGenerator;
fn worker_id_example() {
// Create a generator for worker #1
let mut generator = WorkerIdGenerator::new(1);
// Generate IDs that are unique to this worker
let id1 = generator.generate();
let id2 = generator.generate();
println!("Worker 1 generated: {}", id1);
println!("Worker 1 generated: {}", id2);
// Generate multiple IDs efficiently
let batch = generator.generate_multiple(3);
println!("\nWorker 1 batch:");
for id in batch {
println!(" {}", id);
}
// Create a generator for worker #2
let mut generator2 = WorkerIdGenerator::new(2);
let id3 = generator2.generate();
println!("\nWorker 2 generated: {}", id3);
}
src/main.rs
fn main() {
// ... previous code ...
println!("\n=== Worker-Based ID Generation ===");
worker_id_example();
}
Filter and query data
Now let’s use Bomboni’s powerful filtering capabilities. First, define a data structure:src/main.rs
use bomboni::request::filter::Filter;
use bomboni::request::schema::{SchemaMapped, Schema, FieldMemberSchema, ValueType};
use bomboni::request::value::Value;
use bomboni::macros::btree_map_into;
#[derive(Debug, Clone)]
struct User {
id: String,
name: String,
age: i32,
email: String,
}
impl SchemaMapped for User {
fn get_field(&self, name: &str) -> Value {
match name {
"id" => self.id.clone().into(),
"name" => self.name.clone().into(),
"age" => self.age.into(),
"email" => self.email.clone().into(),
_ => Value::Null,
}
}
}
impl User {
fn get_schema() -> Schema {
Schema {
members: btree_map_into! {
"id" => FieldMemberSchema::new_ordered(ValueType::String),
"name" => FieldMemberSchema::new_ordered(ValueType::String),
"age" => FieldMemberSchema::new_ordered(ValueType::Integer),
"email" => FieldMemberSchema::new(ValueType::String),
},
}
}
}
src/main.rs
fn filtering_example() {
// Create sample users
let users = vec![
User {
id: "1".into(),
name: "Alice".into(),
age: 30,
email: "[email protected]".into(),
},
User {
id: "2".into(),
name: "Bob".into(),
age: 25,
email: "[email protected]".into(),
},
User {
id: "3".into(),
name: "Charlie".into(),
age: 35,
email: "[email protected]".into(),
},
];
// Parse a filter expression
let filter = Filter::parse(r#"age >= 30 AND name = "Alice""#).unwrap();
println!("Filter: {}", filter);
// Validate the filter against the schema
let schema = User::get_schema();
filter.validate(&schema, None).unwrap();
println!("Filter is valid!");
// Apply filter to users
println!("\nFiltering users:");
for user in &users {
match filter.evaluate(user) {
Ok(Value::Boolean(true)) => {
println!(" ✓ {} (age {}) matches", user.name, user.age);
}
Ok(Value::Boolean(false)) => {
println!(" ✗ {} (age {}) doesn't match", user.name, user.age);
}
Ok(other) => {
println!(" ? {} returned unexpected value: {:?}", user.name, other);
}
Err(e) => {
println!(" ! Error evaluating {}: {}", user.name, e);
}
}
}
// Try a more complex filter
let complex_filter = Filter::parse("age >= 25 AND age <= 35").unwrap();
println!("\nComplex filter: {}", complex_filter);
let matching_users: Vec<_> = users
.iter()
.filter(|user| {
matches!(complex_filter.evaluate(*user), Ok(Value::Boolean(true)))
})
.collect();
println!("Found {} matching users:", matching_users.len());
for user in matching_users {
println!(" - {} (age {})", user.name, user.age);
}
}
src/main.rs
fn main() {
// ... previous code ...
println!("\n=== Filtering Examples ===");
filtering_example();
}
Complete example
Here’s the completesrc/main.rs file:
use bomboni::common::id::{Id, worker::WorkerIdGenerator};
use bomboni::common::date_time::UtcDateTime;
use bomboni::request::filter::Filter;
use bomboni::request::schema::{SchemaMapped, Schema, FieldMemberSchema, ValueType};
use bomboni::request::value::Value;
use bomboni::macros::btree_map_into;
#[derive(Debug, Clone)]
struct User {
id: String,
name: String,
age: i32,
email: String,
}
impl SchemaMapped for User {
fn get_field(&self, name: &str) -> Value {
match name {
"id" => self.id.clone().into(),
"name" => self.name.clone().into(),
"age" => self.age.into(),
"email" => self.email.clone().into(),
_ => Value::Null,
}
}
}
impl User {
fn get_schema() -> Schema {
Schema {
members: btree_map_into! {
"id" => FieldMemberSchema::new_ordered(ValueType::String),
"name" => FieldMemberSchema::new_ordered(ValueType::String),
"age" => FieldMemberSchema::new_ordered(ValueType::Integer),
"email" => FieldMemberSchema::new(ValueType::String),
},
}
}
}
fn datetime_example() {
let now = UtcDateTime::now();
println!("Current time: {}", now);
let dt = UtcDateTime::from_seconds(1609459200).unwrap();
println!("From timestamp: {}", dt);
let parsed = "2024-03-15T10:30:00Z".parse::<UtcDateTime>().unwrap();
println!("Parsed datetime: {}", parsed);
let (seconds, nanos) = parsed.timestamp();
println!("Timestamp: {} seconds, {} nanoseconds", seconds, nanos);
}
fn worker_id_example() {
let mut generator = WorkerIdGenerator::new(1);
let id1 = generator.generate();
let id2 = generator.generate();
println!("Worker 1 generated: {}", id1);
println!("Worker 1 generated: {}", id2);
let batch = generator.generate_multiple(3);
println!("\nWorker 1 batch:");
for id in batch {
println!(" {}", id);
}
let mut generator2 = WorkerIdGenerator::new(2);
let id3 = generator2.generate();
println!("\nWorker 2 generated: {}", id3);
}
fn filtering_example() {
let users = vec![
User {
id: "1".into(),
name: "Alice".into(),
age: 30,
email: "[email protected]".into(),
},
User {
id: "2".into(),
name: "Bob".into(),
age: 25,
email: "[email protected]".into(),
},
User {
id: "3".into(),
name: "Charlie".into(),
age: 35,
email: "[email protected]".into(),
},
];
let filter = Filter::parse(r#"age >= 30 AND name = "Alice""#).unwrap();
println!("Filter: {}", filter);
let schema = User::get_schema();
filter.validate(&schema, None).unwrap();
println!("Filter is valid!");
println!("\nFiltering users:");
for user in &users {
match filter.evaluate(user) {
Ok(Value::Boolean(true)) => {
println!(" ✓ {} (age {}) matches", user.name, user.age);
}
Ok(Value::Boolean(false)) => {
println!(" ✗ {} (age {}) doesn't match", user.name, user.age);
}
_ => {}
}
}
let complex_filter = Filter::parse("age >= 25 AND age <= 35").unwrap();
println!("\nComplex filter: {}", complex_filter);
let matching_users: Vec<_> = users
.iter()
.filter(|user| {
matches!(complex_filter.evaluate(*user), Ok(Value::Boolean(true)))
})
.collect();
println!("Found {} matching users:", matching_users.len());
for user in matching_users {
println!(" - {} (age {})", user.name, user.age);
}
}
fn main() {
println!("=== ID Generation ===");
let id = Id::generate();
println!("Generated ID: {}", id);
let ids = Id::generate_multiple(5);
println!("\nGenerated {} IDs:", ids.len());
for (i, id) in ids.iter().enumerate() {
println!(" {}: {}", i + 1, id);
}
let id_str = "01ARZ3NDEKTSV4RRFFQ69G5FAV";
match id_str.parse::<Id>() {
Ok(parsed_id) => println!("\nParsed ID: {}", parsed_id),
Err(e) => println!("\nFailed to parse ID: {}", e),
}
println!("\n=== DateTime Examples ===");
datetime_example();
println!("\n=== Worker-Based ID Generation ===");
worker_id_example();
println!("\n=== Filtering Examples ===");
filtering_example();
}
Run the complete example
Run the complete example:cargo run
- Unique ID generation
- UTC datetime handling
- Worker-based ID generation
- Filter parsing and evaluation
Expected output
=== ID Generation ===
Generated ID: 01JQZX8Y0K5XQJZJ9WQVX4WX0M
Generated 5 IDs:
1: 01JQZX8Y0K5XQJZJ9WQVX4WX0N
2: 01JQZX8Y0K5XQJZJ9WQVX4WX0P
3: 01JQZX8Y0K5XQJZJ9WQVX4WX0Q
4: 01JQZX8Y0K5XQJZJ9WQVX4WX0R
5: 01JQZX8Y0K5XQJZJ9WQVX4WX0S
Parsed ID: 01ARZ3NDEKTSV4RRFFQ69G5FAV
=== DateTime Examples ===
Current time: 2026-03-04T12:34:56Z
From timestamp: 2021-01-01T00:00:00Z
Parsed datetime: 2024-03-15T10:30:00Z
Timestamp: 1710497400 seconds, 0 nanoseconds
=== Worker-Based ID Generation ===
Worker 1 generated: 01JQZX8Y0L...
Worker 1 generated: 01JQZX8Y0M...
Worker 1 batch:
01JQZX8Y0N...
01JQZX8Y0P...
01JQZX8Y0Q...
Worker 2 generated: 01JQZX8Y0R...
=== Filtering Examples ===
Filter: age >= 30 AND name = "Alice"
Filter is valid!
Filtering users:
✓ Alice (age 30) matches
✗ Bob (age 25) doesn't match
✗ Charlie (age 35) doesn't match
Complex filter: age >= 25 AND age <= 35
Found 3 matching users:
- Alice (age 30)
- Bob (age 25)
- Charlie (age 35)
Next steps
Now that you understand the basics, explore more advanced features:Request handling
Learn about list queries, pagination, and SQL generation
Protocol Buffers
Work with enhanced protobuf types and code generation
WebAssembly
Build WASM libraries with TypeScript bindings
Examples
Check out complete example projects