valinor-wire 0.1.0

Wire protocol and message encoding for MudWorld platform
Documentation
# valinor-wire

Wire protocol codec for MudWorld client-server communication.

## Purpose

`valinor-wire` provides a transport-agnostic message envelope format and codec for encoding/decoding messages between MudWorld clients and servers. It abstracts the serialization format (JSON or binary) behind a unified API, enabling format negotiation during connection handshake.

## When to Use This Crate

- Building a WebSocket handler that needs to encode/decode messages
- Implementing a client library that communicates with MudWorld servers
- Adding support for a new transport layer
- Working with request/response patterns over bidirectional connections

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
valinor-wire = { path = "../valinor-wire" }
```

Or if published:

```toml
[dependencies]
valinor-wire = "0.1"
```

## API Overview

### Core Types

| Type | Description |
|------|-------------|
| `Envelope` | Message wrapper containing type, optional request ID, and JSON payload |
| `WireCodec` | Encoder/decoder supporting JSON and Protobuf formats |
| `WireFormat` | Format selection enum (`Json`, `Protobuf`) |
| `ContentType` | HTTP content-type header negotiation helper |
| `EnvelopeError` | Error type for encoding/decoding failures |

### Envelope

The `Envelope` struct is the standard message container:

```rust
pub struct Envelope {
    pub msg_type: String,           // Message type identifier (e.g., "chat.say")
    pub request_id: Option<String>, // Present for request/response patterns
    pub payload: serde_json::Value, // Message-specific data
}
```

Factory methods:
- `Envelope::request()` - Create a request expecting a response
- `Envelope::response()` - Create a response to a request
- `Envelope::event()` - Create a fire-and-forget event
- `Envelope::error()` - Create an error response

### WireCodec

The codec handles serialization:

```rust
pub struct WireCodec { /* ... */ }

impl WireCodec {
    pub fn json() -> Self;
    pub fn protobuf() -> Self;
    pub fn encode(&self, envelope: &Envelope) -> Result<Vec<u8>, EnvelopeError>;
    pub fn decode(&self, data: &[u8]) -> Result<Envelope, EnvelopeError>;
}
```

## Usage Examples

### Sending an Event

```rust
use valinor_wire::{Envelope, WireCodec};
use serde_json::json;

// Create a chat message event
let envelope = Envelope::event("chat.say", json!({
    "room_id": "tavern",
    "text": "Hello, adventurers!"
}));

// Encode for transmission
let codec = WireCodec::json();
let bytes = codec.encode(&envelope).expect("encode failed");

// Send bytes over WebSocket...
```

### Request/Response Pattern

```rust
use valinor_wire::{Envelope, WireCodec};
use serde_json::json;

// Create a request with correlation ID
let request = Envelope::request(
    "room.join",
    "req-123".to_string(),
    json!({ "room_id": "tavern" })
);

let codec = WireCodec::json();
let bytes = codec.encode(&request).expect("encode failed");

// Later, when response arrives...
let response_bytes: &[u8] = /* received from server */;
let response = codec.decode(response_bytes).expect("decode failed");

if response.request_id == Some("req-123".to_string()) {
    // This is our response
    println!("Joined room: {:?}", response.payload);
}
```

### Creating Error Responses

```rust
use valinor_wire::Envelope;
use serde_json::json;

// Error with correlation to original request
let error = Envelope::error(
    Some("req-123".to_string()),
    "ROOM_FULL",
    "The tavern is at capacity"
);

// Error without request context (e.g., protocol violation)
let protocol_error = Envelope::error(
    None,
    "INVALID_MESSAGE",
    "Unrecognized message type"
);
```

### Content-Type Negotiation

```rust
use valinor_wire::{ContentType, WireCodec, WireFormat};

// Parse from HTTP header
let content_type = ContentType::from_header("application/json");
assert_eq!(content_type, Some(ContentType::Json));

// Create codec based on negotiated format
let codec = match content_type {
    Some(ContentType::Json) => WireCodec::json(),
    Some(ContentType::Protobuf) => WireCodec::protobuf(),
    None => WireCodec::json(), // Default fallback
};

// Get header value for responses
let header = ContentType::Json.to_header();
assert_eq!(header, "application/json");
```

### Binary (Protobuf) Format

The binary format is optimized for game state updates where payload is already serialized protobuf:

```rust
use valinor_wire::{Envelope, WireCodec};
use base64::engine::general_purpose::STANDARD;
use base64::Engine;

// Binary payloads must be base64-encoded strings
let protobuf_bytes: Vec<u8> = vec![/* serialized protobuf */];
let payload_b64 = STANDARD.encode(&protobuf_bytes);

let envelope = Envelope::event(
    "state.update",
    serde_json::Value::String(payload_b64)
);

let codec = WireCodec::protobuf();
let wire_bytes = codec.encode(&envelope).expect("encode failed");

// Binary format: [2-byte type length][type bytes][payload bytes]
// More compact than JSON for binary payloads
```

## Wire Format Details

### JSON Format

Standard JSON serialization:

```json
{
  "type": "chat.say",
  "request_id": "req-123",
  "payload": { "text": "Hello!" }
}
```

### Binary Format

Compact binary encoding for protobuf payloads:

```
[u16 big-endian: type length][type bytes][raw payload bytes]
```

Note: Binary format does not support `request_id`. Use JSON for request/response patterns.

## Related Crates

| Crate | Relationship |
|-------|--------------|
| `valinor-proto` | Protobuf message definitions used as payloads |
| `valinor-router` | Message routing that dispatches decoded envelopes |
| `valinor-session` | Session management using wire protocol |
| `valinor-worker` | Cloudflare Worker that uses this codec |

## License

MIT