valinor-wire

Crates.iovalinor-wire
lib.rsvalinor-wire
version0.1.0
created_at2026-01-09 14:58:40.93746+00
updated_at2026-01-09 14:58:40.93746+00
descriptionWire protocol and message encoding for MudWorld platform
homepage
repositoryhttps://github.com/douglance/mudworld
max_upload_size
id2032216
size24,180
doug (douglance)

documentation

README

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:

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

Or if published:

[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:

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:

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

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

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

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

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:

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:

{
  "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

Commit count: 0

cargo fmt