opencore-jsonrpc-rust 1.0.0

A simple and elegant library for creating JSON-RPC servers that communicate with the TypeScript framework OpenCore via stdin/stdout
Documentation
# OpenCore JSON-RPC Rust

A simple and elegant library for creating JSON-RPC servers that communicate with TypeScript frameworks via stdin/stdout.

## Features

- **Simple API** - Register handlers with a clean, type-safe interface
- **Line-delimited JSON** - Easy integration with any language that can spawn processes
- **Automatic serialization** - Request/response handling is transparent
- **Binary events** - Emit messages that map to OpenCore `@BinaryEvent` listeners
- **Robust error handling** - Clear error messages and graceful failure modes
- **Well-tested** - Comprehensive unit and integration tests
- **Fully documented** - Complete API documentation with examples

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
opencore-jsonrpc-rust = "0.1.0"
```

## Quick Start

```rust
use opencore_jsonrpc_rust::server::BinaryServer;
use serde_json::Value;

fn add(params: Vec<Value>) -> Result<Value, String> {
    if params.len() != 2 {
        return Err("Expected 2 parameters".into());
    }
    let a = params[0].as_i64().ok_or("Invalid number")?;
    let b = params[1].as_i64().ok_or("Invalid number")?;
    Ok(Value::from(a + b))
}

fn main() {
    let mut server = BinaryServer::new();
    server.register("add", add);
    server
        .emit_event("worker.ready", serde_json::json!({ "pid": std::process::id() }))
        .unwrap();
    server.run();
}
```

## Protocol

### Request Format

Requests are sent as line-delimited JSON to stdin:

```json
{"id": "req-123", "action": "add", "params": [5, 10]}
```

### Response Format

Responses are written as line-delimited JSON to stdout:

**Success:**
```json
{"status": "ok", "id": "req-123", "result": 15}
```

**Error:**
```json
{"status": "error", "id": "req-123", "error": "Invalid parameters"}
```

**Event:**
```json
{"type": "event", "event": "worker.ready", "data": {"pid": 1234}}
```

## Examples

### Basic Math Operations

```rust
use opencore_jsonrpc_rust::server::BinaryServer;
use serde_json::Value;

fn multiply(params: Vec<Value>) -> Result<Value, String> {
    if params.len() != 2 {
        return Err("Expected 2 parameters".into());
    }
    let a = params[0].as_f64().ok_or("Invalid number")?;
    let b = params[1].as_f64().ok_or("Invalid number")?;
    Ok(Value::from(a * b))
}

fn main() {
    let mut server = BinaryServer::new();
    server.register("multiply", multiply);
    server.run();
}
```

### Emitting Events

```rust
let emitter = server.emitter();
emitter.emit("worker.ready", serde_json::json!({ "pid": std::process::id() }))?;
```

### String Operations

```rust
use opencore_jsonrpc_rust::server::BinaryServer;
use serde_json::Value;

fn concat(params: Vec<Value>) -> Result<Value, String> {
    let strings: Result<Vec<String>, String> = params
        .iter()
        .map(|v| {
            v.as_str()
                .map(|s| s.to_string())
                .ok_or_else(|| "All parameters must be strings".to_string())
        })
        .collect();
    
    Ok(Value::from(strings?.join("")))
}

fn main() {
    let mut server = BinaryServer::new();
    server.register("concat", concat);
    server.run();
}
```

### Complex Data Structures

```rust
use opencore_jsonrpc_rust::server::BinaryServer;
use serde_json::{json, Value};

fn process_user(params: Vec<Value>) -> Result<Value, String> {
    let user = params.first().ok_or("No user data provided")?;
    
    let name = user["name"].as_str().ok_or("Missing name")?;
    let age = user["age"].as_i64().ok_or("Missing age")?;
    
    Ok(json!({
        "message": format!("Hello, {}!", name),
        "is_adult": age >= 18
    }))
}

fn main() {
    let mut server = BinaryServer::new();
    server.register("process_user", process_user);
    server.run();
}
```

## TypeScript Integration

Here's how to use this library from TypeScript (Using OpenCore Framework):

```typescript
import { Server } from '@open-core/framework/server'

@Server.BinaryService({
  name: 'math',
  binary: 'math',
  timeoutMs: 2000,
})
export class MathBinaryService {

  @Server.BinaryCall({ action: 'sum' })
  sum(a: number, b: number): Promise<number> {
    throw new Error('BinaryCall proxy')
    // or return null as any
  }
}
```

## Error Handling

Handlers should return `Result<Value, String>`:

- `Ok(value)` - Success with a JSON value
- `Err(message)` - Error with a descriptive message

The server automatically handles:
- Invalid JSON in requests
- Unknown actions
- Serialization errors
- I/O errors

Use `stdout` only for protocol traffic and write logs to `stderr`.

## Testing

Run the test suite:

```bash
cargo test
```

Run with output:

```bash
cargo test -- --nocapture
```

## Documentation

Generate and view the full API documentation:

```bash
cargo doc --open
```

## License

MIT

## Contributing

Contributions are welcome! Please ensure:

1. All tests pass (`cargo test`)
2. Code is formatted (`cargo fmt`)
3. No clippy warnings (`cargo clippy`)
4. Documentation is updated