Merge branch 'development_rhai'

This commit is contained in:
Timur Gordon
2025-08-04 11:26:50 +02:00
10 changed files with 408 additions and 61 deletions

View File

@@ -0,0 +1,171 @@
# Circle Launcher - Simplified Builder Pattern
This example demonstrates the simplified builder pattern for creating circles and launching them using the unified launcher interface.
## Overview
The system now uses a clean builder pattern with two main builders:
1. **Circle Builder** (`new_circle()`) - For creating and saving circles
2. **Launcher Builder** (`new_launcher()`) - For configuring and launching multiple circles
## Usage Examples
### Creating and Saving Circles
```rust
use heromodels::models::circle::circle::new_circle;
// Create and save a circle
let circle = new_circle()
.title("My Test Circle")
.public_key("02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4")
.ws_url("ws://127.0.0.1:8080")
.description("A test circle for demonstration")
.save();
```
### Simple Launcher (Direct Spawn Mode)
```rust
use heromodels::models::circle::circle::new_launcher;
// Launch a single circle in direct spawn mode
new_launcher()
.add_circle("02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4")
.redis_url("redis://127.0.0.1:6379")
.port(8080)
.launch()?;
```
### Service Manager Mode
```rust
// Launch with service manager (uses default worker binary path)
new_launcher()
.add_circle("02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4")
.service_manager(true) // Enables service manager with default worker binary
.redis_url("redis://127.0.0.1:6379")
.port(8080)
.launch()?;
// Or with custom worker binary path
new_launcher()
.add_circle("02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4")
.worker_binary("./custom/path/to/worker") // Custom worker binary path
.redis_url("redis://127.0.0.1:6379")
.port(8080)
.launch()?;
```
### Multiple Circles with Initialization Scripts
```rust
// Launch multiple circles with different configurations
new_launcher()
.add_circle("02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4")
.initialization_script("init_script1.rhai")
.add_circle("03b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d5")
.initialization_script("init_script2.rhai")
.add_circle("04c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d6")
.worker_binary("./target/release/worker")
.redis_url("redis://127.0.0.1:6379")
.port(8080)
.launch()?;
```
### Mixed Configuration
```rust
// Some circles with scripts, some without
new_launcher()
.add_circle("02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4")
.initialization_script("setup.rhai")
.add_circle("03b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d5") // No script
.add_circle("04c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d6")
.initialization_script("config.rhai")
.redis_url("redis://127.0.0.1:6379")
.port(3000)
.launch()
.await?;
```
## Builder Methods
### Circle Builder Methods
| Method | Description | Example |
|--------|-------------|---------|
| `title()` | Sets the circle title | `.title("My Circle")` |
| `public_key()` | Sets the circle's public key | `.public_key("02a1b2c3...")` |
| `ws_url()` | Sets the WebSocket URL | `.ws_url("ws://127.0.0.1:8080")` |
| `description()` | Sets the description | `.description("Test circle")` |
| `logo()` | Sets the logo URL | `.logo("https://example.com/logo.png")` |
| `add_member()` | Adds a member | `.add_member("member_public_key")` |
| `save()` | Saves the circle | `.save()` |
### Launcher Builder Methods
| Method | Description | Example |
|--------|-------------|---------|
| `add_circle()` | Adds a circle by public key | `.add_circle("02a1b2c3...")` |
| `initialization_script()` | Sets init script for last added circle | `.initialization_script("init.rhai")` |
| `worker_binary()` | Sets custom worker binary path (enables service manager) | `.worker_binary("./custom/worker")` |
| `redis_url()` | Sets Redis URL | `.redis_url("redis://127.0.0.1:6379")` |
| `port()` | Sets the port | `.port(8080)` |
| `service_manager()` | Enables service manager with default worker binary | `.service_manager(true)` |
| `launch()` | Launches all configured circles | `.launch()?` |
## Generated Commands
The launcher builder generates clean commands:
### Direct Spawn Mode
```bash
launcher --redis-url redis://127.0.0.1:6379 --port 8080 \
-c 02a1b2c3... -c 03b2c3d4...:init.rhai -c 04c3d4e5...
```
### Service Manager Mode
```bash
launcher --redis-url redis://127.0.0.1:6379 --port 8080 \
--use-service-manager --worker-binary ../target/release/worker \
-c 02a1b2c3...:init1.rhai -c 03b2c3d4...:init2.rhai -c 04c3d4e5...
```
## Key Features
### Simplified API
- Single way to do things (no multiple methods for the same functionality)
- Clean builder pattern for both circles and launcher
- Method chaining for fluent interface
### Automatic Mode Detection
- **Direct Spawn Mode**: Default behavior
- **Service Manager Mode**: Enabled when `service_manager(true)` is called or when `worker_binary()` is called
- **Default Worker Binary**: Uses `../target/release/worker` unless custom path is specified
### Flexible Configuration
- Add multiple circles in a single launcher
- Each circle can have its own initialization script
- Mix circles with and without scripts
### Error Handling
- Clear error messages for missing configuration
- Validation of required parameters
- Proper error propagation
## Running the Example
```bash
# From the heromodels directory
cargo run --example circle_launcher_example
```
## Notes
- The `initialization_script()` method applies to the last circle added with `add_circle()`
- Setting a `worker_binary()` automatically enables service manager mode
- Redis must be running for the workers to function properly
- Public keys must be valid secp256k1 format (64 hex characters)
- The launcher binary must be available in PATH or specify full path

View File

@@ -0,0 +1,39 @@
use circles_launcher::{new_launcher};
use heromodels::models::circle::circle::{new_circle};
use secp256k1::{Secp256k1, SecretKey, PublicKey};
use rand::rngs::OsRng;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate valid secp256k1 keypairs for testing
let secp = Secp256k1::new();
let mut rng = OsRng;
let secret_key1 = SecretKey::new(&mut rng);
let public_key1 = PublicKey::from_secret_key(&secp, &secret_key1);
let pk1_hex = hex::encode(public_key1.serialize());
let secret_key2 = SecretKey::new(&mut rng);
let public_key2 = PublicKey::from_secret_key(&secp, &secret_key2);
let pk2_hex = hex::encode(public_key2.serialize());
let secret_key3 = SecretKey::new(&mut rng);
let public_key3 = PublicKey::from_secret_key(&secp, &secret_key3);
let pk3_hex = hex::encode(public_key3.serialize());
println!("Generated test public keys:");
println!(" PK1: {}", pk1_hex);
println!(" PK2: {}", pk2_hex);
println!(" PK3: {}", pk3_hex);
// Example 1: Create and save a circle
println!("\n=== Example 1: Create and Save Circle ===");
let _circle = new_circle()
.title("My Test Circle")
.public_key(&pk1_hex)
.ws_url("ws://127.0.0.1:8080")
.description("A test circle for demonstration")
.save();
Ok(())
}