libertaria-stack/core/l0-transport/ipc/client.zig

110 lines
3.2 KiB
Zig

//! IPC Client - L0 -> L2 Event Bridge
//!
//! Sends transport events to the L2 Membrane Agent via Unix Domain Sockets.
const std = @import("std");
const net = std.net;
const os = std.os;
const mem = std.mem;
pub const IpcClient = struct {
allocator: mem.Allocator,
socket_path: []const u8,
stream: ?net.Stream,
connected: bool,
// Constants
const MAGIC: u16 = 0x55AA;
// Event Types
const EVENT_PACKET_RECEIVED: u8 = 0x01;
const EVENT_CONNECTION_ESTABLISHED: u8 = 0x02;
const EVENT_CONNECTION_DROPPED: u8 = 0x03;
pub fn init(allocator: mem.Allocator, socket_path: []const u8) IpcClient {
return IpcClient{
.allocator = allocator,
.socket_path = socket_path,
.stream = null,
.connected = false,
};
}
pub fn deinit(self: *IpcClient) void {
if (self.stream) |s| {
s.close();
}
}
/// Try to connect if not connected
pub fn connect(self: *IpcClient) !void {
if (self.connected) return;
// Non-blocking connect attempt
const stream = net.connectUnixSocket(self.socket_path) catch |err| {
// Connection failed (agent not running?)
// Just return, don't crash. We'll try again next time.
// Log debug?
return err;
};
self.stream = stream;
self.connected = true;
}
/// Send 'PacketReceived' event
pub fn sendPacketReceived(self: *IpcClient, sender_did: [32]u8, packet_type: u8, payload_size: u32) !void {
if (!self.connected) {
self.connect() catch return; // Retry connect
}
// Payload size: DID(32) + Type(1) + Size(4) = 37 bytes
const payload_len: u32 = 37;
// Prepare Header (8 bytes)
// Magic(2) + Type(1) + Flags(1) + Len(4)
var buffer: [8 + 37]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buffer);
const writer = fbs.writer();
// Header
try writer.writeInt(u16, MAGIC, .little);
try writer.writeInt(u8, EVENT_PACKET_RECEIVED, .little);
try writer.writeInt(u8, 0, .little); // Flags
try writer.writeInt(u32, payload_len, .little);
// Payload
try writer.writeAll(&sender_did);
try writer.writeInt(u8, packet_type, .little);
try writer.writeInt(u32, payload_size, .little);
// Send
if (self.stream) |s| {
s.writeAll(&buffer) catch |err| {
// Write failed, assume disconnected
self.connected = false;
s.close();
self.stream = null;
return err;
};
}
}
};
test "ipc packet serialization" {
// Just verify bytes match expected format
var buffer: [8 + 37]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buffer);
const writer = fbs.writer();
// Manual write
try writer.writeInt(u16, 0x55AA, .little);
try writer.writeInt(u8, 0x01, .little);
try writer.writeInt(u8, 0, .little);
try writer.writeInt(u32, 37, .little);
// Offset 8: Payload starts
try std.testing.expectEqual(buffer[0], 0xAA);
try std.testing.expectEqual(buffer[1], 0x55);
}