zinit 0.3.9

Process supervisor with dependency management
Documentation
# 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:

| Level | Name | Behavior |
|-------|------|----------|
| 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