refactor: Extraer tipos de connection.zig (1212 → 793 líneas)

Nuevo módulo connection/types.zig (200 líneas):
- ConnectionState, NatType, ConnectionMethod
- Error enum
- PeerInfo, PeerAddress structs
- Tests de tipos

connection.zig ahora importa y re-exporta los tipos.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
R.Eugenio 2026-01-29 00:57:22 +01:00
parent 8b968d7add
commit 789236092b
2 changed files with 277 additions and 495 deletions

File diff suppressed because it is too large Load diff

200
src/connection/types.zig Normal file
View file

@ -0,0 +1,200 @@
//! Tipos y estructuras para el módulo de conexión
const std = @import("std");
const identity = @import("../identity.zig");
const utils = @import("../utils.zig");
pub const DeviceId = identity.DeviceId;
// =============================================================================
// Estados y tipos
// =============================================================================
/// Estado de conexión
pub const ConnectionState = enum {
disconnected,
resolving,
connecting,
handshaking,
connected,
disconnecting,
@"error",
pub fn isActive(self: ConnectionState) bool {
return self == .connecting or self == .handshaking or self == .connected;
}
};
/// Tipo de NAT detectado
pub const NatType = enum {
unknown,
none,
full_cone,
restricted,
port_restricted,
symmetric,
blocked,
pub fn canAcceptIncoming(self: NatType) bool {
return self == .none or self == .full_cone;
}
pub fn needsHolePunch(self: NatType) bool {
return self == .restricted or self == .port_restricted;
}
pub fn needsRelay(self: NatType) bool {
return self == .symmetric or self == .blocked;
}
};
/// Método de conexión usado
pub const ConnectionMethod = enum {
direct,
hole_punch,
relay,
local,
};
/// Errores del módulo
pub const Error = error{
AlreadyInitialized,
NotInitialized,
InvalidDeviceId,
ConnectionFailed,
ConnectionTimeout,
ConnectionClosed,
PeerNotFound,
CertificateError,
TlsError,
ProtocolError,
CompressionError,
OutOfMemory,
IoError,
InvalidConfig,
AddressInUse,
NetworkUnreachable,
HandshakeFailed,
InvalidMessage,
RelayFailed,
};
// =============================================================================
// Información de peers
// =============================================================================
/// Información de un peer
pub const PeerInfo = struct {
device_id: DeviceId,
device_name: []const u8,
client_name: []const u8,
client_version: []const u8,
addresses: [][]const u8,
connected_at: i64,
is_local: bool,
bytes_sent: u64,
bytes_received: u64,
connection_method: ConnectionMethod,
allocator: std.mem.Allocator,
pub fn deinit(self: *PeerInfo) void {
self.allocator.free(self.device_name);
self.allocator.free(self.client_name);
self.allocator.free(self.client_version);
for (self.addresses) |addr| {
self.allocator.free(addr);
}
self.allocator.free(self.addresses);
}
};
/// Dirección de peer con metadatos
pub const PeerAddress = struct {
address: []const u8,
port: u16,
is_local: bool,
is_relay: bool,
last_seen: i64,
priority: u8,
pub fn parse(addr_str: []const u8, allocator: std.mem.Allocator) !PeerAddress {
if (std.mem.startsWith(u8, addr_str, "relay://")) {
return .{
.address = try allocator.dupe(u8, addr_str),
.port = 0,
.is_local = false,
.is_relay = true,
.last_seen = utils.timestamp(),
.priority = 100,
};
}
if (std.mem.lastIndexOf(u8, addr_str, ":")) |colon| {
const port = std.fmt.parseInt(u16, addr_str[colon + 1 ..], 10) catch 22000;
const ip = addr_str[0..colon];
const is_local = std.mem.startsWith(u8, ip, "192.168.") or
std.mem.startsWith(u8, ip, "10.") or
std.mem.startsWith(u8, ip, "172.16.") or
std.mem.startsWith(u8, ip, "172.17.") or
std.mem.startsWith(u8, ip, "172.18.") or
std.mem.startsWith(u8, ip, "172.19.") or
std.mem.startsWith(u8, ip, "172.2") or
std.mem.startsWith(u8, ip, "172.30.") or
std.mem.startsWith(u8, ip, "172.31.");
return .{
.address = try allocator.dupe(u8, ip),
.port = port,
.is_local = is_local,
.is_relay = false,
.last_seen = utils.timestamp(),
.priority = if (is_local) 10 else 50,
};
}
return error.InvalidAddress;
}
};
// =============================================================================
// Tests
// =============================================================================
test "connection state active check" {
try std.testing.expect(!ConnectionState.disconnected.isActive());
try std.testing.expect(ConnectionState.connecting.isActive());
try std.testing.expect(ConnectionState.connected.isActive());
try std.testing.expect(!ConnectionState.@"error".isActive());
}
test "nat type capabilities" {
try std.testing.expect(NatType.none.canAcceptIncoming());
try std.testing.expect(NatType.full_cone.canAcceptIncoming());
try std.testing.expect(!NatType.restricted.canAcceptIncoming());
try std.testing.expect(NatType.restricted.needsHolePunch());
try std.testing.expect(NatType.port_restricted.needsHolePunch());
try std.testing.expect(!NatType.symmetric.needsHolePunch());
try std.testing.expect(NatType.symmetric.needsRelay());
try std.testing.expect(NatType.blocked.needsRelay());
}
test "peer address parse" {
const allocator = std.testing.allocator;
const local = try PeerAddress.parse("192.168.1.100:22000", allocator);
defer allocator.free(local.address);
try std.testing.expect(local.is_local);
try std.testing.expect(!local.is_relay);
try std.testing.expectEqual(@as(u16, 22000), local.port);
const public_addr = try PeerAddress.parse("8.8.8.8:22000", allocator);
defer allocator.free(public_addr.address);
try std.testing.expect(!public_addr.is_local);
const relay_addr = try PeerAddress.parse("relay://relay.example.com/device123", allocator);
defer allocator.free(relay_addr.address);
try std.testing.expect(relay_addr.is_relay);
}