133 lines
3.3 KiB
Zig
133 lines
3.3 KiB
Zig
//! State machine definitions for L2 sessions
|
|
//!
|
|
//! States represent the lifecycle of a peer relationship.
|
|
|
|
const std = @import("std");
|
|
|
|
/// Session states
|
|
///
|
|
/// See SPEC.md for full state diagram and transition rules.
|
|
pub const State = enum {
|
|
/// Initial state
|
|
idle,
|
|
|
|
/// Handshake initiated, awaiting response
|
|
handshake_initiated,
|
|
|
|
/// Handshake received, preparing response
|
|
handshake_received,
|
|
|
|
/// Active, healthy session
|
|
established,
|
|
|
|
/// Connectivity issues detected
|
|
degraded,
|
|
|
|
/// Key rotation in progress
|
|
rotating,
|
|
|
|
/// Extended failure, pending cleanup or retry
|
|
suspended,
|
|
|
|
/// Terminal failure state
|
|
failed,
|
|
|
|
/// Check if this state allows sending messages
|
|
pub fn canSend(self: State) bool {
|
|
return switch (self) {
|
|
.established, .degraded, .rotating => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Check if this state allows receiving messages
|
|
pub fn canReceive(self: State) bool {
|
|
return switch (self) {
|
|
.established, .degraded, .rotating, .handshake_received => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Check if this is a terminal state
|
|
pub fn isTerminal(self: State) bool {
|
|
return switch (self) {
|
|
.suspended, .failed => true,
|
|
else => false,
|
|
};
|
|
}
|
|
};
|
|
|
|
/// State transition events
|
|
pub const Event = enum {
|
|
initiate_handshake,
|
|
receive_handshake,
|
|
receive_response,
|
|
send_response,
|
|
receive_ack,
|
|
heartbeat_ok,
|
|
heartbeat_missed,
|
|
timeout,
|
|
connectivity_restored,
|
|
time_to_rotate,
|
|
rotation_complete,
|
|
rotation_timeout,
|
|
invalid_signature,
|
|
cleanup,
|
|
retry,
|
|
};
|
|
|
|
/// Attempt state transition
|
|
/// Returns new state or null if transition is invalid
|
|
pub fn transition(current: State, event: Event) ?State {
|
|
return switch (current) {
|
|
.idle => switch (event) {
|
|
.initiate_handshake => .handshake_initiated,
|
|
.receive_handshake => .handshake_received,
|
|
else => null,
|
|
},
|
|
|
|
.handshake_initiated => switch (event) {
|
|
.receive_response => .established,
|
|
.timeout => .failed,
|
|
.invalid_signature => .failed,
|
|
else => null,
|
|
},
|
|
|
|
.handshake_received => switch (event) {
|
|
.send_response => .established,
|
|
.timeout => .failed,
|
|
else => null,
|
|
},
|
|
|
|
.established => switch (event) {
|
|
.heartbeat_missed => .degraded,
|
|
.time_to_rotate => .rotating,
|
|
else => null,
|
|
},
|
|
|
|
.degraded => switch (event) {
|
|
.connectivity_restored => .established,
|
|
.timeout => .suspended,
|
|
else => null,
|
|
},
|
|
|
|
.rotating => switch (event) {
|
|
.rotation_complete => .established,
|
|
.rotation_timeout => .failed,
|
|
else => null,
|
|
},
|
|
|
|
.suspended => switch (event) {
|
|
.cleanup => null, // Terminal
|
|
.retry => .handshake_initiated,
|
|
else => null,
|
|
},
|
|
|
|
.failed => switch (event) {
|
|
.cleanup => null, // Terminal
|
|
.retry => .handshake_initiated,
|
|
else => null,
|
|
},
|
|
};
|
|
}
|