You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
69 lines
2.3 KiB
69 lines
2.3 KiB
import Foundation
|
|
|
|
/// Represents the connection lifecycle of the remote client.
|
|
/// Transitions are validated by `canTransition(to:)` to enforce a strict state machine.
|
|
nonisolated enum ConnectionState: Equatable, Sendable {
|
|
case disconnected
|
|
case discovering
|
|
case foundHost(String)
|
|
case downloadingDB
|
|
case connectingCommandChannel
|
|
case connected(String)
|
|
case connectionLost(String)
|
|
|
|
/// Human-readable status message for display in the UI.
|
|
var userMessage: String? {
|
|
switch self {
|
|
case .disconnected:
|
|
nil
|
|
case .discovering:
|
|
"Searching for hosts..."
|
|
case .foundHost(let host):
|
|
"Found \(host)"
|
|
case .downloadingDB:
|
|
"Downloading library..."
|
|
case .connectingCommandChannel:
|
|
"Connecting..."
|
|
case .connected(let host):
|
|
"Connected to \(host)"
|
|
case .connectionLost(let reason):
|
|
"Connection lost — \(reason)"
|
|
}
|
|
}
|
|
|
|
/// Whether the client is fully connected.
|
|
var isConnected: Bool {
|
|
if case .connected = self { return true }
|
|
return false
|
|
}
|
|
|
|
/// Validates whether transitioning from the current state to `next` is allowed.
|
|
///
|
|
/// Valid forward transitions follow the connection lifecycle:
|
|
/// disconnected → discovering → foundHost → downloadingDB → connectingCommandChannel → connected → connectionLost → discovering
|
|
///
|
|
/// Any state may transition to `.disconnected` (the user can always disconnect).
|
|
func canTransition(to next: ConnectionState) -> Bool {
|
|
// Any state can go to disconnected
|
|
if case .disconnected = next { return true }
|
|
|
|
switch self {
|
|
case .disconnected:
|
|
if case .discovering = next { return true }
|
|
case .discovering:
|
|
if case .foundHost = next { return true }
|
|
case .foundHost:
|
|
if case .downloadingDB = next { return true }
|
|
case .downloadingDB:
|
|
if case .connectingCommandChannel = next { return true }
|
|
case .connectingCommandChannel:
|
|
if case .connected = next { return true }
|
|
case .connected:
|
|
if case .connectionLost = next { return true }
|
|
case .connectionLost:
|
|
if case .discovering = next { return true }
|
|
}
|
|
|
|
return false
|
|
}
|
|
}
|
|
|