# Zinit Factory Pattern Guide
## Overview
The zinit client library now uses a **factory pattern** with **log levels** for a clean, intuitive API in both Rust and Rhai.
## Quick Start
### Rhai
```rhai
// Create a connected client with logging level 2 (normal)
let z = zinit.log_level(2).connect();
// Service operations
z.list();
z.start("my_service");
z.stop("my_service");
z.status("my_service");
z.logs("my_service");
```
### Rust
```rust
use herolib_clients::zinit::ZinitHandle;
// Connect to zinit
let z = ZinitHandle::new()?;
// Service operations
z.list()?;
z.start("my_service")?;
z.stop("my_service")?;
z.status("my_service")?;
z.logs("my_service", None)?;
```
## Factory Pattern API
### Creating a Connection
#### Rhai
```rhai
// Option 1: Default socket, minimal logging
let z = zinit.connect();
// Option 2: Custom socket path
let z = zinit.socket("/custom/path/zinit.sock").connect();
// Option 3: Custom log level
let z = zinit.log_level(3).connect();
// Option 4: Full configuration
let z = zinit
.socket("/custom/path/zinit.sock")
.log_level(3)
.connect();
```
#### Rust
```rust
use herolib_clients::zinit::client::ZinitClientBuilder;
// Option 1: Default configuration
let client = ZinitClientBuilder::new().build()?;
// Option 2: Custom socket path
let client = ZinitClientBuilder::new()
.socket("/custom/path/zinit.sock")
.build()?;
// Option 3: Custom log level
let client = ZinitClientBuilder::new()
.log_level(3)
.build()?;
// Option 4: Full configuration
let client = ZinitClientBuilder::new()
.socket("/custom/path/zinit.sock")
.log_level(3)
.build()?;
```
## Log Levels
Log levels control output verbosity when connecting and performing operations:
| 0 | Silent | No output |
| 1 | Minimal | Connection status only |
| 2 | Normal | Connection + basic operations |
| 3 | Verbose | All operations and details |
### Examples
#### Rhai
```rhai
// Silent - no output
let z = zinit.log_level(0).connect();
// Minimal - only connection messages
let z = zinit.log_level(1).connect();
// Normal - operations are logged
let z = zinit.log_level(2).connect();
z.start("service"); // Logs: "Started: service"
// Verbose - detailed output
let z = zinit.log_level(3).connect();
z.list(); // Shows all services with details
```
#### Rust
```rust
// The Rust client logs based on the log_level during build
let client = ZinitClientBuilder::new()
.log_level(2)
.build()?; // Logs: "✓ Connected to zinit server"
```
## Service Operations
All service operations are called as methods on the connected handle:
### Rhai
```rhai
let z = zinit.log_level(2).connect();
// List all services
let services = z.list();
// Create a service
z.service_new()
.name("my_service")
.exec("python3 /app/main.py")
.restart("on-failure")
.register();
// Service status
let status = z.status("my_service");
print("State: " + status.state);
// Service control
z.start("my_service");
z.stop("my_service");
z.restart("my_service");
// Logs
let logs = z.logs("my_service");
for log in logs {
print(log.line);
}
// Tree view (dependencies)
let tree = z.tree();
print(tree);
```
### Rust
```rust
use herolib_clients::zinit::ZinitHandle;
let z = ZinitHandle::new()?;
// List all services
let services = z.list()?;
// Create a service
z.service_new(&service_config)?;
// Service status
let status = z.status("my_service")?;
println!("State: {:?}", status.state);
// Service control
z.start("my_service")?;
z.stop("my_service")?;
z.restart("my_service")?;
// Logs
let logs = z.logs("my_service", None)?;
for entry in logs {
println!("{}", entry.line);
}
// Tree view
let tree = z.tree()?;
println!("{}", tree);
```
## Service Configuration
When creating services, use the builder pattern to configure all aspects:
### Rhai
```rhai
z.service_new()
// Basic information
.name("database")
.exec("postgres -D /var/lib/postgres")
.dir("/var/lib/postgres")
// Dependencies
.after("log_aggregator")
.requires("filesystem")
.wants("network")
// Lifecycle
.restart("on-failure")
.restart_delay_ms(5000)
.max_restarts(5)
.start_timeout_ms(30000)
.stop_timeout_ms(10000)
// Health checks
.health_tcp("127.0.0.1:5432")
.health_interval_ms(10000)
.health_retries(3)
// Logging
.log_buffer_lines(5000)
.log_file("/var/log/postgres.log")
// Register the service
.register();
```
### Rust
```rust
use herolib_clients::zinit::{ServiceDef, DependencyDef, HealthDef};
let config = ServiceDef {
name: "database".to_string(),
exec: "postgres -D /var/lib/postgres".to_string(),
dir: Some("/var/lib/postgres".to_string()),
dependencies: Some(DependencyDef {
after: Some(vec!["log_aggregator".to_string()]),
requires: Some(vec!["filesystem".to_string()]),
wants: Some(vec!["network".to_string()]),
conflicts: None,
}),
lifecycle: Some(LifecycleDef {
restart: Some("on-failure".to_string()),
restart_delay_ms: Some(5000),
max_restarts: Some(5),
start_timeout_ms: Some(30000),
stop_timeout_ms: Some(10000),
..Default::default()
}),
health: Some(HealthDef {
common: Some(HealthCommon {
interval_ms: Some(10000),
timeout_ms: Some(2000),
retries: Some(3),
start_period_ms: None,
}),
tcp: Some("127.0.0.1:5432".to_string()),
..Default::default()
}),
logging: Some(LoggingDef {
buffer_lines: Some(5000),
log_file: Some("/var/log/postgres.log".to_string()),
..Default::default()
}),
..Default::default()
};
z.service_new(&config)?;
```
## Error Handling
### Rhai
Rhai scripts will error if the zinit server isn't running:
```rhai
try {
let z = zinit.connect();
print("Connected successfully");
} catch (error) {
print("Failed to connect: " + error);
}
```
### Rust
Use standard Rust error handling:
```rust
match ZinitHandle::new() {
Ok(z) => {
println!("Connected successfully");
}
Err(e) => {
eprintln!("Failed to connect: {}", e);
}
}
```
## Design Principles
The factory pattern was chosen for several reasons:
1. **Fluent API**: Method chaining is intuitive and readable
```rhai
let z = zinit.socket(path).log_level(3).connect();
```
2. **Deferred Connection**: Connection happens only when `connect()` is called
```rhai
let z = zinit.socket(path).log_level(3); // Configuration only
let handle = z.connect(); // Connection happens here
```
3. **Consistent Logging**: Log level controls all output automatically
- No manual `print` statements needed in scripts
- Output adapts based on configuration
4. **Clean Method Calls**: Operations on the handle are simple
```rhai
z.list();
z.start("service");
z.stop("service");
```
## Migration from Old API
### Old Pattern (Deprecated)
```rhai
// Individual function calls
let services = zinit_list();
zinit_start("service");
zinit_stop("service");
```
### New Pattern (Recommended)
```rhai
// Factory pattern
let z = zinit.log_level(2).connect();
let services = z.list();
z.start("service");
z.stop("service");
```
## Examples
See the example files for complete working code:
- **Rhai Examples**:
- `01_basics.rhai` - Simple service operations
- `02_advanced.rhai` - Dependencies and health checks
- `03_complete_example.rhai` - Production-like setup
- **Rust Examples**:
- `examples/rust/zinit/main.rs` - Basic operations
- `examples/rust/zinit/advanced.rs` - Advanced features
## Further Reading
- See `INSTRUCTIONS_ZINIT.md` for additional documentation
- Check the OpenRPC specification for detailed API documentation
- Review the examples for practical usage patterns